feat: encapsulate identity with better-auth
This commit is contained in:
@@ -12,9 +12,8 @@ post {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"deviceId": "",
|
"email": "john.doe@fixture.none",
|
||||||
"preAuthSessionId": "",
|
"otp": ""
|
||||||
"userInputCode": ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ post {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"base": "http://localhost:5170",
|
|
||||||
"email": "john.doe@fixture.none"
|
"email": "john.doe@fixture.none"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ post {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"name": "valkyr"
|
"name": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
"start": "deno --allow-all --watch-hmr=routes/ server.ts"
|
"start": "deno --allow-all --watch-hmr=routes/ server.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@modules/identity": "workspace:*",
|
||||||
|
"@module/workspace": "workspace:*",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import identity from "@modules/identity/server.ts";
|
import identity from "@modules/identity/server.ts";
|
||||||
import workspace from "@modules/workspace/server.ts";
|
import workspace from "@modules/workspace/server.ts";
|
||||||
import database from "@platform/database/server.ts";
|
|
||||||
import { logger } from "@platform/logger";
|
import { logger } from "@platform/logger";
|
||||||
import { context } from "@platform/relay";
|
import { context } from "@platform/relay";
|
||||||
import { Api } from "@platform/server/api.ts";
|
import { Api } from "@platform/server/api.ts";
|
||||||
import server from "@platform/server/server.ts";
|
import server from "@platform/server/server.ts";
|
||||||
import socket from "@platform/socket/server.ts";
|
import socket from "@platform/socket/server.ts";
|
||||||
import { storage } from "@platform/storage";
|
import { storage } from "@platform/storage";
|
||||||
import supertokens from "@platform/supertoken/server.ts";
|
|
||||||
|
|
||||||
import { config } from "./config.ts";
|
import { config } from "./config.ts";
|
||||||
|
import session from "./services/session.ts";
|
||||||
|
|
||||||
const log = logger.prefix("Server");
|
const log = logger.prefix("Server");
|
||||||
|
|
||||||
@@ -21,10 +20,9 @@ const log = logger.prefix("Server");
|
|||||||
|
|
||||||
// ### Platform
|
// ### Platform
|
||||||
|
|
||||||
await database.bootstrap();
|
|
||||||
await server.bootstrap();
|
await server.bootstrap();
|
||||||
await socket.bootstrap();
|
await socket.bootstrap();
|
||||||
await supertokens.bootsrap();
|
await session.bootstrap();
|
||||||
|
|
||||||
// ### Modules
|
// ### Modules
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ Deno.serve(
|
|||||||
|
|
||||||
await server.resolve(request);
|
await server.resolve(request);
|
||||||
await socket.resolve();
|
await socket.resolve();
|
||||||
await supertokens.resolve(request);
|
await session.resolve(request);
|
||||||
|
|
||||||
// ### Fetch
|
// ### Fetch
|
||||||
// Execute fetch against the api instance.
|
// Execute fetch against the api instance.
|
||||||
|
|||||||
114
api/services/session.ts
Normal file
114
api/services/session.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import "@modules/identity/server.ts";
|
||||||
|
|
||||||
|
import { getAccessControlMethods, identity } from "@modules/identity/server.ts";
|
||||||
|
import { context, UnauthorizedError } from "@platform/relay";
|
||||||
|
import { storage } from "@platform/storage";
|
||||||
|
|
||||||
|
const IDENTITY_RESOLVE_HEADER = "x-identity-resolver";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
bootstrap: async () => {
|
||||||
|
bootstrapSessionContext();
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: async (request: Request) => {
|
||||||
|
await resolvePrincipalSession(request);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function bootstrapSessionContext() {
|
||||||
|
Object.defineProperties(context, {
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
isAuthenticated: {
|
||||||
|
get() {
|
||||||
|
return storage.getStore()?.principal !== undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
session: {
|
||||||
|
get() {
|
||||||
|
const session = storage.getStore()?.session;
|
||||||
|
if (session === undefined) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
principal: {
|
||||||
|
get() {
|
||||||
|
const principal = storage.getStore()?.principal;
|
||||||
|
if (principal === undefined) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
}
|
||||||
|
return principal;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
access: {
|
||||||
|
get() {
|
||||||
|
const access = storage.getStore()?.access;
|
||||||
|
if (access === undefined) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
}
|
||||||
|
return access;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolvePrincipalSession(request: Request) {
|
||||||
|
// ### Resolver
|
||||||
|
// Check if the incoming request is tagged as a resolver check.
|
||||||
|
// If it is a resolver we break out of the session resolution
|
||||||
|
// to avoid an infinite resolution loop.
|
||||||
|
|
||||||
|
const isResolver = request.headers.get(IDENTITY_RESOLVE_HEADER) !== null;
|
||||||
|
if (isResolver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Cookie
|
||||||
|
// Check for the existence of cookie to pass onto the session
|
||||||
|
// resolver.
|
||||||
|
|
||||||
|
const cookie = request.headers.get("cookie");
|
||||||
|
if (cookie === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Session
|
||||||
|
// Fetch session from identity module and tag it as a resolution
|
||||||
|
// call so it can break out of a resolution loop.
|
||||||
|
|
||||||
|
const session = await identity.resolve({
|
||||||
|
headers: new Headers({
|
||||||
|
cookie,
|
||||||
|
[IDENTITY_RESOLVE_HEADER]: "true",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ### Populate Context
|
||||||
|
// On successfull resolution we build the request identity context.
|
||||||
|
|
||||||
|
if ("data" in session) {
|
||||||
|
const context = storage.getStore();
|
||||||
|
if (context === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.session = session.data.session;
|
||||||
|
context.principal = session.data.principal;
|
||||||
|
context.access = getAccessControlMethods(session.data.principal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"apps/react",
|
"apps/react",
|
||||||
"modules/identity",
|
"modules/identity",
|
||||||
"modules/workspace",
|
"modules/workspace",
|
||||||
|
"platform/cerbos",
|
||||||
"platform/config",
|
"platform/config",
|
||||||
"platform/database",
|
"platform/database",
|
||||||
"platform/logger",
|
"platform/logger",
|
||||||
@@ -14,7 +15,6 @@
|
|||||||
"platform/socket",
|
"platform/socket",
|
||||||
"platform/spec",
|
"platform/spec",
|
||||||
"platform/storage",
|
"platform/storage",
|
||||||
"platform/supertoken",
|
|
||||||
"platform/vault"
|
"platform/vault"
|
||||||
],
|
],
|
||||||
"imports": {
|
"imports": {
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"@modules/identity/server.ts": "./modules/identity/server.ts",
|
"@modules/identity/server.ts": "./modules/identity/server.ts",
|
||||||
"@modules/workspace/client.ts": "./modules/workspace/client.ts",
|
"@modules/workspace/client.ts": "./modules/workspace/client.ts",
|
||||||
"@modules/workspace/server.ts": "./modules/workspace/server.ts",
|
"@modules/workspace/server.ts": "./modules/workspace/server.ts",
|
||||||
|
"@platform/cerbos": "./platform/cerbos/mod.ts",
|
||||||
"@platform/config/": "./platform/config/",
|
"@platform/config/": "./platform/config/",
|
||||||
"@platform/database/": "./platform/database/",
|
"@platform/database/": "./platform/database/",
|
||||||
"@platform/logger": "./platform/logger/mod.ts",
|
"@platform/logger": "./platform/logger/mod.ts",
|
||||||
@@ -30,7 +31,6 @@
|
|||||||
"@platform/socket/": "./platform/socket/",
|
"@platform/socket/": "./platform/socket/",
|
||||||
"@platform/spec/": "./platform/spec/",
|
"@platform/spec/": "./platform/spec/",
|
||||||
"@platform/storage": "./platform/storage/storage.ts",
|
"@platform/storage": "./platform/storage/storage.ts",
|
||||||
"@platform/supertoken/": "./platform/supertoken/",
|
|
||||||
"@platform/vault": "./platform/vault/vault.ts"
|
"@platform/vault": "./platform/vault/vault.ts"
|
||||||
},
|
},
|
||||||
"tasks": {
|
"tasks": {
|
||||||
|
|||||||
492
deno.lock
generated
492
deno.lock
generated
@@ -11,13 +11,15 @@
|
|||||||
"npm:@jsr/valkyr__event-store@2.0.1": "2.0.1",
|
"npm:@jsr/valkyr__event-store@2.0.1": "2.0.1",
|
||||||
"npm:@jsr/valkyr__inverse@1.0.1": "1.0.1",
|
"npm:@jsr/valkyr__inverse@1.0.1": "1.0.1",
|
||||||
"npm:@jsr/valkyr__json-rpc@1.1.0": "1.1.0",
|
"npm:@jsr/valkyr__json-rpc@1.1.0": "1.1.0",
|
||||||
"npm:@tailwindcss/vite@4.1.13": "4.1.13_vite@7.1.6__picomatch@4.0.3",
|
"npm:@tailwindcss/vite@4.1.13": "4.1.13_vite@7.1.6__picomatch@4.0.3_@types+node@24.2.0",
|
||||||
"npm:@tanstack/react-query@5.89.0": "5.89.0_react@19.1.1",
|
"npm:@tanstack/react-query@5.89.0": "5.89.0_react@19.1.1",
|
||||||
"npm:@tanstack/react-router-devtools@1.131.47": "1.131.47_@tanstack+react-router@1.131.47__react@19.1.1__react-dom@19.1.1___react@19.1.1_react@19.1.1_react-dom@19.1.1__react@19.1.1",
|
"npm:@tanstack/react-router-devtools@1.131.47": "1.131.47_@tanstack+react-router@1.131.47__react@19.1.1__react-dom@19.1.1___react@19.1.1_react@19.1.1_react-dom@19.1.1__react@19.1.1",
|
||||||
"npm:@tanstack/react-router@1.131.47": "1.131.47_react@19.1.1_react-dom@19.1.1__react@19.1.1",
|
"npm:@tanstack/react-router@1.131.47": "1.131.47_react@19.1.1_react-dom@19.1.1__react@19.1.1",
|
||||||
|
"npm:@types/node@*": "24.2.0",
|
||||||
"npm:@types/react-dom@19.1.9": "19.1.9_@types+react@19.1.13",
|
"npm:@types/react-dom@19.1.9": "19.1.9_@types+react@19.1.13",
|
||||||
"npm:@types/react@19.1.13": "19.1.13",
|
"npm:@types/react@19.1.13": "19.1.13",
|
||||||
"npm:@vitejs/plugin-react@4.7.0": "4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4",
|
"npm:@vitejs/plugin-react@4.7.0": "4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0",
|
||||||
|
"npm:better-auth@1.3.16": "1.3.16_react@19.1.1_react-dom@19.1.1__react@19.1.1",
|
||||||
"npm:cookie@1.0.2": "1.0.2",
|
"npm:cookie@1.0.2": "1.0.2",
|
||||||
"npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0",
|
"npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0",
|
||||||
"npm:eslint-plugin-react-refresh@0.4.20": "0.4.20_eslint@9.35.0",
|
"npm:eslint-plugin-react-refresh@0.4.20": "0.4.20_eslint@9.35.0",
|
||||||
@@ -32,11 +34,10 @@
|
|||||||
"npm:prettier@3.6.2": "3.6.2",
|
"npm:prettier@3.6.2": "3.6.2",
|
||||||
"npm:react-dom@19.1.1": "19.1.1_react@19.1.1",
|
"npm:react-dom@19.1.1": "19.1.1_react@19.1.1",
|
||||||
"npm:react@19.1.1": "19.1.1",
|
"npm:react@19.1.1": "19.1.1",
|
||||||
"npm:supertokens-node@23.0.1": "23.0.1",
|
|
||||||
"npm:tailwindcss@4.1.13": "4.1.13",
|
"npm:tailwindcss@4.1.13": "4.1.13",
|
||||||
"npm:typescript-eslint@8.44.0": "8.44.0_eslint@9.35.0_typescript@5.9.2_@typescript-eslint+parser@8.44.0__eslint@9.35.0__typescript@5.9.2",
|
"npm:typescript-eslint@8.44.0": "8.44.0_eslint@9.35.0_typescript@5.9.2_@typescript-eslint+parser@8.44.0__eslint@9.35.0__typescript@5.9.2",
|
||||||
"npm:typescript@5.9.2": "5.9.2",
|
"npm:typescript@5.9.2": "5.9.2",
|
||||||
"npm:vite@7.1.6": "7.1.6_picomatch@4.0.3",
|
"npm:vite@7.1.6": "7.1.6_picomatch@4.0.3_@types+node@24.2.0",
|
||||||
"npm:zod@4.1.11": "4.1.11"
|
"npm:zod@4.1.11": "4.1.11"
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
@@ -177,6 +178,12 @@
|
|||||||
"@babel/helper-validator-identifier"
|
"@babel/helper-validator-identifier"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@better-auth/utils@0.3.0": {
|
||||||
|
"integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="
|
||||||
|
},
|
||||||
|
"@better-fetch/fetch@1.1.18": {
|
||||||
|
"integrity": "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="
|
||||||
|
},
|
||||||
"@cerbos/core@0.24.1": {
|
"@cerbos/core@0.24.1": {
|
||||||
"integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==",
|
"integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -374,6 +381,9 @@
|
|||||||
"levn"
|
"levn"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@hexagon/base64@1.1.28": {
|
||||||
|
"integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="
|
||||||
|
},
|
||||||
"@humanfs/core@0.19.1": {
|
"@humanfs/core@0.19.1": {
|
||||||
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="
|
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="
|
||||||
},
|
},
|
||||||
@@ -530,12 +540,21 @@
|
|||||||
],
|
],
|
||||||
"tarball": "https://npm.jsr.io/~/11/@jsr/valkyr__testcontainers/2.0.2.tgz"
|
"tarball": "https://npm.jsr.io/~/11/@jsr/valkyr__testcontainers/2.0.2.tgz"
|
||||||
},
|
},
|
||||||
|
"@levischuck/tiny-cbor@0.2.11": {
|
||||||
|
"integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="
|
||||||
|
},
|
||||||
"@mongodb-js/saslprep@1.3.0": {
|
"@mongodb-js/saslprep@1.3.0": {
|
||||||
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
|
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"sparse-bitfield"
|
"sparse-bitfield"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@noble/ciphers@2.0.1": {
|
||||||
|
"integrity": "sha512-xHK3XHPUW8DTAobU+G0XT+/w+JLM7/8k1UFdB5xg/zTFPnFCobhftzw8wl4Lw2aq/Rvir5pxfZV5fEazmeCJ2g=="
|
||||||
|
},
|
||||||
|
"@noble/hashes@2.0.0": {
|
||||||
|
"integrity": "sha512-h8VUBlE8R42+XIDO229cgisD287im3kdY6nbNZJFjc6ZvKIXPYXe6Vc/t+kyjFdMFyt5JpapzTsEg8n63w5/lw=="
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir@2.1.5": {
|
"@nodelib/fs.scandir@2.1.5": {
|
||||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -553,6 +572,49 @@
|
|||||||
"fastq"
|
"fastq"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@peculiar/asn1-android@2.5.0": {
|
||||||
|
"integrity": "sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A==",
|
||||||
|
"dependencies": [
|
||||||
|
"@peculiar/asn1-schema",
|
||||||
|
"asn1js",
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@peculiar/asn1-ecc@2.5.0": {
|
||||||
|
"integrity": "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==",
|
||||||
|
"dependencies": [
|
||||||
|
"@peculiar/asn1-schema",
|
||||||
|
"@peculiar/asn1-x509",
|
||||||
|
"asn1js",
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@peculiar/asn1-rsa@2.5.0": {
|
||||||
|
"integrity": "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==",
|
||||||
|
"dependencies": [
|
||||||
|
"@peculiar/asn1-schema",
|
||||||
|
"@peculiar/asn1-x509",
|
||||||
|
"asn1js",
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@peculiar/asn1-schema@2.5.0": {
|
||||||
|
"integrity": "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==",
|
||||||
|
"dependencies": [
|
||||||
|
"asn1js",
|
||||||
|
"pvtsutils",
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@peculiar/asn1-x509@2.5.0": {
|
||||||
|
"integrity": "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==",
|
||||||
|
"dependencies": [
|
||||||
|
"@peculiar/asn1-schema",
|
||||||
|
"asn1js",
|
||||||
|
"pvtsutils",
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@rolldown/pluginutils@1.0.0-beta.27": {
|
"@rolldown/pluginutils@1.0.0-beta.27": {
|
||||||
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="
|
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="
|
||||||
},
|
},
|
||||||
@@ -666,6 +728,21 @@
|
|||||||
"os": ["win32"],
|
"os": ["win32"],
|
||||||
"cpu": ["x64"]
|
"cpu": ["x64"]
|
||||||
},
|
},
|
||||||
|
"@simplewebauthn/browser@13.2.0": {
|
||||||
|
"integrity": "sha512-N3fuA1AAnTo5gCStYoIoiasPccC+xPLx2YU88Dv0GeAmPQTWHETlZQq5xZ0DgUq1H9loXMWQH5qqUjcI7BHJ1A=="
|
||||||
|
},
|
||||||
|
"@simplewebauthn/server@13.1.2": {
|
||||||
|
"integrity": "sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g==",
|
||||||
|
"dependencies": [
|
||||||
|
"@hexagon/base64",
|
||||||
|
"@levischuck/tiny-cbor",
|
||||||
|
"@peculiar/asn1-android",
|
||||||
|
"@peculiar/asn1-ecc",
|
||||||
|
"@peculiar/asn1-rsa",
|
||||||
|
"@peculiar/asn1-schema",
|
||||||
|
"@peculiar/asn1-x509"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@tailwindcss/node@4.1.13": {
|
"@tailwindcss/node@4.1.13": {
|
||||||
"integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
|
"integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -765,7 +842,16 @@
|
|||||||
"@tailwindcss/node",
|
"@tailwindcss/node",
|
||||||
"@tailwindcss/oxide",
|
"@tailwindcss/oxide",
|
||||||
"tailwindcss",
|
"tailwindcss",
|
||||||
"vite"
|
"vite@7.1.6_picomatch@4.0.3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@tailwindcss/vite@4.1.13_vite@7.1.6__picomatch@4.0.3_@types+node@24.2.0": {
|
||||||
|
"integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==",
|
||||||
|
"dependencies": [
|
||||||
|
"@tailwindcss/node",
|
||||||
|
"@tailwindcss/oxide",
|
||||||
|
"tailwindcss",
|
||||||
|
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"@tanstack/history@1.131.2": {
|
"@tanstack/history@1.131.2": {
|
||||||
@@ -872,6 +958,12 @@
|
|||||||
"@types/json-schema@7.0.15": {
|
"@types/json-schema@7.0.15": {
|
||||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
||||||
},
|
},
|
||||||
|
"@types/node@24.2.0": {
|
||||||
|
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
||||||
|
"dependencies": [
|
||||||
|
"undici-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@types/react-dom@19.1.9_@types+react@19.1.13": {
|
"@types/react-dom@19.1.9_@types+react@19.1.13": {
|
||||||
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1002,7 +1094,19 @@
|
|||||||
"@rolldown/pluginutils",
|
"@rolldown/pluginutils",
|
||||||
"@types/babel__core",
|
"@types/babel__core",
|
||||||
"react-refresh",
|
"react-refresh",
|
||||||
"vite"
|
"vite@7.1.6_picomatch@4.0.3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@vitejs/plugin-react@4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0": {
|
||||||
|
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
||||||
|
"dependencies": [
|
||||||
|
"@babel/core",
|
||||||
|
"@babel/plugin-transform-react-jsx-self",
|
||||||
|
"@babel/plugin-transform-react-jsx-source",
|
||||||
|
"@rolldown/pluginutils",
|
||||||
|
"@types/babel__core",
|
||||||
|
"react-refresh",
|
||||||
|
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"acorn-jsx@5.3.2_acorn@8.15.0": {
|
"acorn-jsx@5.3.2_acorn@8.15.0": {
|
||||||
@@ -1015,12 +1119,6 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"agent-base@6.0.2": {
|
|
||||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"debug"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ajv@6.12.6": {
|
"ajv@6.12.6": {
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1039,27 +1137,54 @@
|
|||||||
"argparse@2.0.1": {
|
"argparse@2.0.1": {
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
},
|
},
|
||||||
"asynckit@0.4.0": {
|
"asn1js@3.0.6": {
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
"integrity": "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==",
|
||||||
},
|
|
||||||
"axios@1.11.0": {
|
|
||||||
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"follow-redirects",
|
"pvtsutils",
|
||||||
"form-data",
|
"pvutils",
|
||||||
"proxy-from-env"
|
"tslib"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"balanced-match@1.0.2": {
|
"balanced-match@1.0.2": {
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
},
|
},
|
||||||
"base64-js@1.5.1": {
|
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
|
||||||
},
|
|
||||||
"baseline-browser-mapping@2.8.6": {
|
"baseline-browser-mapping@2.8.6": {
|
||||||
"integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==",
|
"integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
|
"better-auth@1.3.16_react@19.1.1_react-dom@19.1.1__react@19.1.1": {
|
||||||
|
"integrity": "sha512-WHU3QTtkBdwMlzM4AbOSoti0aPVTw1IfdomNCcP9Mo0J03ENn7z2a6/Vz9fimHmjpVWMqRvW72cYcc30LLFFOw==",
|
||||||
|
"dependencies": [
|
||||||
|
"@better-auth/utils",
|
||||||
|
"@better-fetch/fetch",
|
||||||
|
"@noble/ciphers",
|
||||||
|
"@noble/hashes",
|
||||||
|
"@simplewebauthn/browser",
|
||||||
|
"@simplewebauthn/server",
|
||||||
|
"better-call",
|
||||||
|
"defu",
|
||||||
|
"jose",
|
||||||
|
"kysely",
|
||||||
|
"nanostores",
|
||||||
|
"react",
|
||||||
|
"react-dom",
|
||||||
|
"zod"
|
||||||
|
],
|
||||||
|
"optionalPeers": [
|
||||||
|
"react",
|
||||||
|
"react-dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"better-call@1.0.19": {
|
||||||
|
"integrity": "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw==",
|
||||||
|
"dependencies": [
|
||||||
|
"@better-auth/utils",
|
||||||
|
"@better-fetch/fetch",
|
||||||
|
"rou3",
|
||||||
|
"set-cookie-parser",
|
||||||
|
"uncrypto"
|
||||||
|
]
|
||||||
|
},
|
||||||
"brace-expansion@1.1.12": {
|
"brace-expansion@1.1.12": {
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1093,16 +1218,6 @@
|
|||||||
"bson@6.10.4": {
|
"bson@6.10.4": {
|
||||||
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="
|
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="
|
||||||
},
|
},
|
||||||
"buffer-equal-constant-time@1.0.1": {
|
|
||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
|
||||||
},
|
|
||||||
"buffer@6.0.3": {
|
|
||||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
|
||||||
"dependencies": [
|
|
||||||
"base64-js",
|
|
||||||
"ieee754"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"call-bind-apply-helpers@1.0.2": {
|
"call-bind-apply-helpers@1.0.2": {
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1145,36 +1260,18 @@
|
|||||||
"color-name@1.1.4": {
|
"color-name@1.1.4": {
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
"combined-stream@1.0.8": {
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"dependencies": [
|
|
||||||
"delayed-stream"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"concat-map@0.0.1": {
|
"concat-map@0.0.1": {
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
},
|
},
|
||||||
"content-type@1.0.5": {
|
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
|
|
||||||
},
|
|
||||||
"convert-source-map@2.0.0": {
|
"convert-source-map@2.0.0": {
|
||||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
|
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
|
||||||
},
|
},
|
||||||
"cookie-es@1.2.2": {
|
"cookie-es@1.2.2": {
|
||||||
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="
|
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="
|
||||||
},
|
},
|
||||||
"cookie@0.7.2": {
|
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="
|
|
||||||
},
|
|
||||||
"cookie@1.0.2": {
|
"cookie@1.0.2": {
|
||||||
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="
|
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="
|
||||||
},
|
},
|
||||||
"cross-fetch@3.2.0": {
|
|
||||||
"integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
|
|
||||||
"dependencies": [
|
|
||||||
"node-fetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cross-spawn@7.0.6": {
|
"cross-spawn@7.0.6": {
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1183,15 +1280,9 @@
|
|||||||
"which"
|
"which"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"crypto-js@4.2.0": {
|
|
||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
|
||||||
},
|
|
||||||
"csstype@3.1.3": {
|
"csstype@3.1.3": {
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
"dayjs@1.11.18": {
|
|
||||||
"integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA=="
|
|
||||||
},
|
|
||||||
"debug@4.4.3": {
|
"debug@4.4.3": {
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1201,8 +1292,8 @@
|
|||||||
"deep-is@0.1.4": {
|
"deep-is@0.1.4": {
|
||||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
||||||
},
|
},
|
||||||
"delayed-stream@1.0.0": {
|
"defu@6.1.4": {
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
|
||||||
},
|
},
|
||||||
"detect-libc@2.1.0": {
|
"detect-libc@2.1.0": {
|
||||||
"integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg=="
|
"integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg=="
|
||||||
@@ -1215,12 +1306,6 @@
|
|||||||
"gopd"
|
"gopd"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ecdsa-sig-formatter@1.0.11": {
|
|
||||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"safe-buffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"electron-to-chromium@1.5.222": {
|
"electron-to-chromium@1.5.222": {
|
||||||
"integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w=="
|
"integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w=="
|
||||||
},
|
},
|
||||||
@@ -1243,15 +1328,6 @@
|
|||||||
"es-errors"
|
"es-errors"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"es-set-tostringtag@2.1.0": {
|
|
||||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
|
||||||
"dependencies": [
|
|
||||||
"es-errors",
|
|
||||||
"get-intrinsic",
|
|
||||||
"has-tostringtag",
|
|
||||||
"hasown"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"esbuild@0.25.10": {
|
"esbuild@0.25.10": {
|
||||||
"integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
|
"integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
|
||||||
"optionalDependencies": [
|
"optionalDependencies": [
|
||||||
@@ -1458,19 +1534,6 @@
|
|||||||
"flatted@3.3.3": {
|
"flatted@3.3.3": {
|
||||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="
|
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="
|
||||||
},
|
},
|
||||||
"follow-redirects@1.15.11": {
|
|
||||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="
|
|
||||||
},
|
|
||||||
"form-data@4.0.4": {
|
|
||||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
|
||||||
"dependencies": [
|
|
||||||
"asynckit",
|
|
||||||
"combined-stream",
|
|
||||||
"es-set-tostringtag",
|
|
||||||
"hasown",
|
|
||||||
"mime-types"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"fsevents@2.3.3": {
|
"fsevents@2.3.3": {
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
@@ -1543,31 +1606,15 @@
|
|||||||
"has-symbols@1.1.0": {
|
"has-symbols@1.1.0": {
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
|
||||||
},
|
},
|
||||||
"has-tostringtag@1.0.2": {
|
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
|
||||||
"dependencies": [
|
|
||||||
"has-symbols"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hasown@2.0.2": {
|
"hasown@2.0.2": {
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"function-bind"
|
"function-bind"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"https-proxy-agent@5.0.1": {
|
|
||||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
|
||||||
"dependencies": [
|
|
||||||
"agent-base",
|
|
||||||
"debug"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"idb@8.0.3": {
|
"idb@8.0.3": {
|
||||||
"integrity": "sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg=="
|
"integrity": "sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg=="
|
||||||
},
|
},
|
||||||
"ieee754@1.2.1": {
|
|
||||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
|
||||||
},
|
|
||||||
"ignore@5.3.2": {
|
"ignore@5.3.2": {
|
||||||
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
|
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
|
||||||
},
|
},
|
||||||
@@ -1606,9 +1653,6 @@
|
|||||||
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
|
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"jose@4.15.9": {
|
|
||||||
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="
|
|
||||||
},
|
|
||||||
"jose@6.1.0": {
|
"jose@6.1.0": {
|
||||||
"integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="
|
"integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="
|
||||||
},
|
},
|
||||||
@@ -1639,42 +1683,15 @@
|
|||||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"jsonwebtoken@9.0.2": {
|
|
||||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"jws",
|
|
||||||
"lodash.includes",
|
|
||||||
"lodash.isboolean",
|
|
||||||
"lodash.isinteger",
|
|
||||||
"lodash.isnumber",
|
|
||||||
"lodash.isplainobject",
|
|
||||||
"lodash.isstring",
|
|
||||||
"lodash.once",
|
|
||||||
"ms",
|
|
||||||
"semver@7.7.2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jwa@1.4.2": {
|
|
||||||
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
|
|
||||||
"dependencies": [
|
|
||||||
"buffer-equal-constant-time",
|
|
||||||
"ecdsa-sig-formatter",
|
|
||||||
"safe-buffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jws@3.2.2": {
|
|
||||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
|
||||||
"dependencies": [
|
|
||||||
"jwa",
|
|
||||||
"safe-buffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"keyv@4.5.4": {
|
"keyv@4.5.4": {
|
||||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"json-buffer"
|
"json-buffer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"kysely@0.28.5": {
|
||||||
|
"integrity": "sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA=="
|
||||||
|
},
|
||||||
"levn@0.4.1": {
|
"levn@0.4.1": {
|
||||||
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
|
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1682,9 +1699,6 @@
|
|||||||
"type-check"
|
"type-check"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"libphonenumber-js@1.12.21": {
|
|
||||||
"integrity": "sha512-z/o0jBYS3d8js1QBksrHxZUARjmM0S6uvpINkyJ9IcPkXIoUh5l4S3rTbGAlq9ThbCExdSV0wWp8gw7729A/ww=="
|
|
||||||
},
|
|
||||||
"lightningcss-darwin-arm64@1.30.1": {
|
"lightningcss-darwin-arm64@1.30.1": {
|
||||||
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
|
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
@@ -1759,30 +1773,9 @@
|
|||||||
"p-locate"
|
"p-locate"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lodash.includes@4.3.0": {
|
|
||||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
|
||||||
},
|
|
||||||
"lodash.isboolean@3.0.3": {
|
|
||||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
|
||||||
},
|
|
||||||
"lodash.isinteger@4.0.4": {
|
|
||||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
|
||||||
},
|
|
||||||
"lodash.isnumber@3.0.3": {
|
|
||||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
|
||||||
},
|
|
||||||
"lodash.isplainobject@4.0.6": {
|
|
||||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
|
||||||
},
|
|
||||||
"lodash.isstring@4.0.1": {
|
|
||||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
|
||||||
},
|
|
||||||
"lodash.merge@4.6.2": {
|
"lodash.merge@4.6.2": {
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
||||||
},
|
},
|
||||||
"lodash.once@4.1.1": {
|
|
||||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
|
||||||
},
|
|
||||||
"lru-cache@5.1.1": {
|
"lru-cache@5.1.1": {
|
||||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1811,15 +1804,6 @@
|
|||||||
"picomatch@2.3.1"
|
"picomatch@2.3.1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mime-db@1.52.0": {
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
|
||||||
},
|
|
||||||
"mime-types@2.1.35": {
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"dependencies": [
|
|
||||||
"mime-db"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mingo@6.6.1": {
|
"mingo@6.6.1": {
|
||||||
"integrity": "sha512-KC6b1ODYoSdYu5fBm+SzQb7fa4ARmGwfa3Cf9F7U+2mnfD4Zhf89qQgO1cPTtaJ68w3ntIT5dVujgF52HvN7+g=="
|
"integrity": "sha512-KC6b1ODYoSdYu5fBm+SzQb7fa4ARmGwfa3Cf9F7U+2mnfD4Zhf89qQgO1cPTtaJ68w3ntIT5dVujgF52HvN7+g=="
|
||||||
},
|
},
|
||||||
@@ -1848,7 +1832,7 @@
|
|||||||
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"@types/whatwg-url",
|
"@types/whatwg-url",
|
||||||
"whatwg-url@14.2.0"
|
"whatwg-url"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mongodb@6.20.0": {
|
"mongodb@6.20.0": {
|
||||||
@@ -1870,21 +1854,15 @@
|
|||||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
|
"nanostores@1.0.1": {
|
||||||
|
"integrity": "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="
|
||||||
|
},
|
||||||
"natural-compare@1.4.0": {
|
"natural-compare@1.4.0": {
|
||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||||
},
|
},
|
||||||
"node-fetch@2.7.0": {
|
|
||||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
|
||||||
"dependencies": [
|
|
||||||
"whatwg-url@5.0.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node-releases@2.0.21": {
|
"node-releases@2.0.21": {
|
||||||
"integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="
|
"integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="
|
||||||
},
|
},
|
||||||
"nodemailer@6.10.1": {
|
|
||||||
"integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="
|
|
||||||
},
|
|
||||||
"object-inspect@1.13.4": {
|
"object-inspect@1.13.4": {
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="
|
||||||
},
|
},
|
||||||
@@ -1911,9 +1889,6 @@
|
|||||||
"p-limit"
|
"p-limit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"pako@2.1.0": {
|
|
||||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
|
|
||||||
},
|
|
||||||
"parent-module@1.0.1": {
|
"parent-module@1.0.1": {
|
||||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1938,12 +1913,6 @@
|
|||||||
"picomatch@4.0.3": {
|
"picomatch@4.0.3": {
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
|
||||||
},
|
},
|
||||||
"pkce-challenge@3.1.0": {
|
|
||||||
"integrity": "sha512-bQ/0XPZZ7eX+cdAkd61uYWpfMhakH3NeteUF1R8GNa+LMqX8QFAkbCLqq+AYAns1/ueACBu/BMWhrlKGrdvGZg==",
|
|
||||||
"dependencies": [
|
|
||||||
"crypto-js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"postcss@8.5.6": {
|
"postcss@8.5.6": {
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1962,24 +1931,24 @@
|
|||||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"process@0.11.10": {
|
|
||||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
|
|
||||||
},
|
|
||||||
"proxy-from-env@1.1.0": {
|
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
|
||||||
},
|
|
||||||
"punycode@2.3.1": {
|
"punycode@2.3.1": {
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
|
||||||
},
|
},
|
||||||
|
"pvtsutils@1.3.6": {
|
||||||
|
"integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
|
||||||
|
"dependencies": [
|
||||||
|
"tslib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pvutils@1.1.3": {
|
||||||
|
"integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="
|
||||||
|
},
|
||||||
"qs@6.14.0": {
|
"qs@6.14.0": {
|
||||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"side-channel"
|
"side-channel"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"querystringify@2.2.0": {
|
|
||||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
|
||||||
},
|
|
||||||
"queue-microtask@1.2.3": {
|
"queue-microtask@1.2.3": {
|
||||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||||
},
|
},
|
||||||
@@ -1996,9 +1965,6 @@
|
|||||||
"react@19.1.1": {
|
"react@19.1.1": {
|
||||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="
|
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="
|
||||||
},
|
},
|
||||||
"requires-port@1.0.0": {
|
|
||||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
|
||||||
},
|
|
||||||
"resolve-from@4.0.0": {
|
"resolve-from@4.0.0": {
|
||||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||||
},
|
},
|
||||||
@@ -2037,6 +2003,9 @@
|
|||||||
],
|
],
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
|
"rou3@0.5.1": {
|
||||||
|
"integrity": "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="
|
||||||
|
},
|
||||||
"run-parallel@1.2.0": {
|
"run-parallel@1.2.0": {
|
||||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2049,15 +2018,9 @@
|
|||||||
"tslib"
|
"tslib"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"safe-buffer@5.2.1": {
|
|
||||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
|
||||||
},
|
|
||||||
"scheduler@0.26.0": {
|
"scheduler@0.26.0": {
|
||||||
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
|
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
|
||||||
},
|
},
|
||||||
"scmp@2.1.0": {
|
|
||||||
"integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q=="
|
|
||||||
},
|
|
||||||
"semver@6.3.1": {
|
"semver@6.3.1": {
|
||||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"bin": true
|
"bin": true
|
||||||
@@ -2143,29 +2106,6 @@
|
|||||||
"strip-json-comments@3.1.1": {
|
"strip-json-comments@3.1.1": {
|
||||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
|
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
|
||||||
},
|
},
|
||||||
"supertokens-js-override@0.0.4": {
|
|
||||||
"integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg=="
|
|
||||||
},
|
|
||||||
"supertokens-node@23.0.1": {
|
|
||||||
"integrity": "sha512-cCuY9Y5Mj93Pg1ktbqilouWgAoQWniQauftB4Ef6rfOchogx13XTo1pNP14zezn2rSf7WIPb9iaZb5zif6TKtQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"buffer",
|
|
||||||
"content-type",
|
|
||||||
"cookie@0.7.2",
|
|
||||||
"cross-fetch",
|
|
||||||
"debug",
|
|
||||||
"jose@4.15.9",
|
|
||||||
"libphonenumber-js",
|
|
||||||
"nodemailer",
|
|
||||||
"pako",
|
|
||||||
"pkce-challenge",
|
|
||||||
"process",
|
|
||||||
"set-cookie-parser",
|
|
||||||
"supertokens-js-override",
|
|
||||||
"tldts",
|
|
||||||
"twilio"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"supports-color@7.2.0": {
|
"supports-color@7.2.0": {
|
||||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2201,25 +2141,12 @@
|
|||||||
"picomatch@4.0.3"
|
"picomatch@4.0.3"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tldts-core@6.1.86": {
|
|
||||||
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="
|
|
||||||
},
|
|
||||||
"tldts@6.1.86": {
|
|
||||||
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"tldts-core"
|
|
||||||
],
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"to-regex-range@5.0.1": {
|
"to-regex-range@5.0.1": {
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"is-number"
|
"is-number"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tr46@0.0.3": {
|
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
|
||||||
},
|
|
||||||
"tr46@5.1.1": {
|
"tr46@5.1.1": {
|
||||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2235,19 +2162,6 @@
|
|||||||
"tslib@2.8.1": {
|
"tslib@2.8.1": {
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
},
|
},
|
||||||
"twilio@4.23.0": {
|
|
||||||
"integrity": "sha512-LdNBQfOe0dY2oJH2sAsrxazpgfFQo5yXGxe96QA8UWB5uu+433PrUbkv8gQ5RmrRCqUTPQ0aOrIyAdBr1aB03Q==",
|
|
||||||
"dependencies": [
|
|
||||||
"axios",
|
|
||||||
"dayjs",
|
|
||||||
"https-proxy-agent",
|
|
||||||
"jsonwebtoken",
|
|
||||||
"qs",
|
|
||||||
"scmp",
|
|
||||||
"url-parse",
|
|
||||||
"xmlbuilder"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type-check@0.4.0": {
|
"type-check@0.4.0": {
|
||||||
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
|
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2269,6 +2183,12 @@
|
|||||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
|
"uncrypto@0.1.3": {
|
||||||
|
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="
|
||||||
|
},
|
||||||
|
"undici-types@7.10.0": {
|
||||||
|
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="
|
||||||
|
},
|
||||||
"update-browserslist-db@1.1.3_browserslist@4.26.2": {
|
"update-browserslist-db@1.1.3_browserslist@4.26.2": {
|
||||||
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2284,13 +2204,6 @@
|
|||||||
"punycode"
|
"punycode"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"url-parse@1.5.10": {
|
|
||||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"querystringify",
|
|
||||||
"requires-port"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"use-sync-external-store@1.5.0_react@19.1.1": {
|
"use-sync-external-store@1.5.0_react@19.1.1": {
|
||||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2316,8 +2229,24 @@
|
|||||||
],
|
],
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"webidl-conversions@3.0.1": {
|
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0": {
|
||||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
"integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==",
|
||||||
|
"dependencies": [
|
||||||
|
"@types/node",
|
||||||
|
"esbuild",
|
||||||
|
"fdir",
|
||||||
|
"picomatch@4.0.3",
|
||||||
|
"postcss",
|
||||||
|
"rollup",
|
||||||
|
"tinyglobby"
|
||||||
|
],
|
||||||
|
"optionalDependencies": [
|
||||||
|
"fsevents"
|
||||||
|
],
|
||||||
|
"optionalPeers": [
|
||||||
|
"@types/node"
|
||||||
|
],
|
||||||
|
"bin": true
|
||||||
},
|
},
|
||||||
"webidl-conversions@7.0.0": {
|
"webidl-conversions@7.0.0": {
|
||||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
|
||||||
@@ -2325,15 +2254,8 @@
|
|||||||
"whatwg-url@14.2.0": {
|
"whatwg-url@14.2.0": {
|
||||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"tr46@5.1.1",
|
"tr46",
|
||||||
"webidl-conversions@7.0.0"
|
"webidl-conversions"
|
||||||
]
|
|
||||||
},
|
|
||||||
"whatwg-url@5.0.0": {
|
|
||||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
|
||||||
"dependencies": [
|
|
||||||
"tr46@0.0.3",
|
|
||||||
"webidl-conversions@3.0.1"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"which@2.0.2": {
|
"which@2.0.2": {
|
||||||
@@ -2346,9 +2268,6 @@
|
|||||||
"word-wrap@1.2.5": {
|
"word-wrap@1.2.5": {
|
||||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
|
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
|
||||||
},
|
},
|
||||||
"xmlbuilder@13.0.2": {
|
|
||||||
"integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ=="
|
|
||||||
},
|
|
||||||
"yallist@3.1.1": {
|
"yallist@3.1.1": {
|
||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||||
},
|
},
|
||||||
@@ -2412,7 +2331,8 @@
|
|||||||
"modules/identity": {
|
"modules/identity": {
|
||||||
"packageJson": {
|
"packageJson": {
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"npm:supertokens-node@23.0.1",
|
"npm:better-auth@1.3.16",
|
||||||
|
"npm:cookie@1.0.2",
|
||||||
"npm:zod@4.1.11"
|
"npm:zod@4.1.11"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2426,6 +2346,14 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"platform/cerbos": {
|
||||||
|
"packageJson": {
|
||||||
|
"dependencies": [
|
||||||
|
"npm:@cerbos/http@0.23.1",
|
||||||
|
"npm:zod@4.1.11"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"platform/config": {
|
"platform/config": {
|
||||||
"packageJson": {
|
"packageJson": {
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -2481,16 +2409,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"platform/supertoken": {
|
|
||||||
"packageJson": {
|
|
||||||
"dependencies": [
|
|
||||||
"npm:@cerbos/http@0.23.1",
|
|
||||||
"npm:cookie@1.0.2",
|
|
||||||
"npm:supertokens-node@23.0.1",
|
|
||||||
"npm:zod@4.1.11"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"platform/vault": {
|
"platform/vault": {
|
||||||
"packageJson": {
|
"packageJson": {
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|||||||
@@ -17,49 +17,6 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- localdev
|
- localdev
|
||||||
|
|
||||||
# Super Tokens Database
|
|
||||||
# --------------------------------------------------------------------------------
|
|
||||||
# Used by supertokens instance to store and manage principals.
|
|
||||||
|
|
||||||
stdb:
|
|
||||||
image: 'postgres:latest'
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: supertokens_user
|
|
||||||
POSTGRES_PASSWORD: somePassword
|
|
||||||
POSTGRES_DB: supertokens
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
networks:
|
|
||||||
- localdev
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'pg_isready', '-U', 'supertokens_user', '-d', 'supertokens']
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
# Super Tokens
|
|
||||||
# --------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
supertokens:
|
|
||||||
image: registry.supertokens.io/supertokens/supertokens-postgresql:latest
|
|
||||||
depends_on:
|
|
||||||
stdb:
|
|
||||||
condition: service_healthy
|
|
||||||
ports:
|
|
||||||
- 3567:3567
|
|
||||||
environment:
|
|
||||||
POSTGRESQL_CONNECTION_URI: "postgresql://supertokens_user:somePassword@stdb:5432/supertokens"
|
|
||||||
networks:
|
|
||||||
- localdev
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: >
|
|
||||||
bash -c 'exec 3<>/dev/tcp/127.0.0.1/3567 && echo -e "GET /hello HTTP/1.1\r\nhost: 127.0.0.1:3567\r\nConnection: close\r\n\r\n" >&3 && cat <&3 | grep "Hello"'
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
# Cerbos
|
# Cerbos
|
||||||
# --------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------
|
||||||
# Policy engine for application access control.
|
# Policy engine for application access control.
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCy5ZoXkKP9mZTk
|
|
||||||
sKbQdSwspHZqyMH33Gby23+9ycNHMIww7djcWFfPRW4s7tu3SNaac6qVg9OI43+Z
|
|
||||||
6BPXxuh4nhQ4LX5No9iVEmcWvZtKE4ghwzsoU0llT7+aKl9UYvgqU1YX4zyfiyo2
|
|
||||||
bW0nVPasEHTyjLCVPK5BKlq+UmuyJTVcduALDnVETpUefu5Vca6tIRXsOovvAf5b
|
|
||||||
zmcxPccaXIatR/AeipxT0YWoInn8dxD3kyFgTPXtinuBZxvp6MUeSs5IE8OJRJRP
|
|
||||||
PEo1MQ9HFw9aYRIn9uIkbARbNZMGz77zB1+0TrPGyKOB5lLReWGMUFAJhjLrnTsY
|
|
||||||
z19se4kNAgMBAAECgf9QkG6A6ViiHIMnUskIDeP5Xir19d9kbGwrcn0F2OXYaX+l
|
|
||||||
Oot9w3KM6loRJx380/zk/e0Uch1MeZ2fyqQRUmAGQIzkXUm6LUWIekYQN6vZ3JlP
|
|
||||||
YA2/M+otdd8Tpws9hFSDMUlx0SP3GAi0cE48xdBkVAT0NjZ3Jjor7Wv6GLe//Kzg
|
|
||||||
1OVrbPAA/+RrPB+BQn5nmZFT0aLuLpyxB4f4ArHG/8DEBY49Syy7/3Ke0kfHMnhl
|
|
||||||
5Eg5Yau89wSLqEoUSuQvNixu/5nTTQ6v1VYPVG8D1hn773SbNoY9o5vZOPRl1P0q
|
|
||||||
9YC/qpzPJkm/A5TZLsoalIxuGTdwts+DaEeoKmECgYEA5CddLQbMNu9kYElxpSA3
|
|
||||||
xXoTL71ZBCQsWExmJrcGe2lQhGO40lF8jE6QnEvMt0mp8Dg9n2ih4J87+2Ozb0fp
|
|
||||||
2G2ilNeMxM7keywA/+Cwg71QyImppU0lQ5PYLv+pllfxN8FPpLBluy7rDahzphkn
|
|
||||||
1rijqI5d4bHNG6IgD2ynteECgYEAyLs2eBWxX39Jff3OdpSVmHf7NtacbtsUf1qM
|
|
||||||
RJSvLsiSwKn39n1+Y6ebzftxm/XD/j8FbN8XvMZMI4OrlfzP+YJaTybIbHrLzCE2
|
|
||||||
B5E9j0GbJRhJ/D3l9FQBGdY4g5yC4mgbncXURQqqQTtKk2d+ixZSrw8iyDGN+aMJ
|
|
||||||
ybqZoK0CgYALb6GvARk5Y7R/Uw8cPMou3tiZWv9cQsfqQSIZrLDpfLTpfeokuKrq
|
|
||||||
iYGcI/yF725SOS91jxQWI0Upa6zx1gP1skEk/szyjIBNYD5IlSWj5NhoxOW5AG3u
|
|
||||||
vjlm2a/RdmUD62+njKP8xvRHQftSBw7FJ4okh8ZS6suiJ/U9cK/TYQKBgFg+jTyP
|
|
||||||
dNGhuKJN0NUqjvVfUa4S/ORzJXizStTfdIAhpvpR/nN7SfPvfDw6nQBOM+JyvCTX
|
|
||||||
kqznlBNM0EL4yElNN/xx9UxTU4Ki2wjKngB7fAP7wJLGd3BI+c7s8R1S0etMj091
|
|
||||||
59KOVLimoytYJTZqEuFoywatWlfzh9sKUH1lAoGBAID6mqGL3SZhh+i2/kAytfzw
|
|
||||||
UswTQqA0CCBTzN/Eo1QozmUVTLQPj8rBchNSoiSc92y+lPIL8ePdU7imRB77i+9D
|
|
||||||
9MSmc5u3ACACOSkwF0JCEGN+Rju4HR5wwm3h6Kvf/FQ3yvSEOKAWhqXIY95qtYTU
|
|
||||||
j3O+iJbY32pbQsawIAkw
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsuWaF5Cj/ZmU5LCm0HUs
|
|
||||||
LKR2asjB99xm8tt/vcnDRzCMMO3Y3FhXz0VuLO7bt0jWmnOqlYPTiON/megT18bo
|
|
||||||
eJ4UOC1+TaPYlRJnFr2bShOIIcM7KFNJZU+/mipfVGL4KlNWF+M8n4sqNm1tJ1T2
|
|
||||||
rBB08oywlTyuQSpavlJrsiU1XHbgCw51RE6VHn7uVXGurSEV7DqL7wH+W85nMT3H
|
|
||||||
GlyGrUfwHoqcU9GFqCJ5/HcQ95MhYEz17Yp7gWcb6ejFHkrOSBPDiUSUTzxKNTEP
|
|
||||||
RxcPWmESJ/biJGwEWzWTBs++8wdftE6zxsijgeZS0XlhjFBQCYYy6507GM9fbHuJ
|
|
||||||
DQIDAQAB
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
@@ -11,4 +11,4 @@ resourcePolicy:
|
|||||||
|
|
||||||
- actions: ["manage"]
|
- actions: ["manage"]
|
||||||
effect: EFFECT_ALLOW
|
effect: EFFECT_ALLOW
|
||||||
roles: ["admin"]
|
roles: ["super"]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getEnvironmentVariable } from "@platform/config/environment.ts";
|
import { getEnvironmentVariable } from "@platform/config/environment.ts";
|
||||||
|
import { SerializeOptions } from "cookie";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
@@ -7,4 +8,37 @@ export const config = {
|
|||||||
type: z.url(),
|
type: z.url(),
|
||||||
fallback: "http://localhost:8370",
|
fallback: "http://localhost:8370",
|
||||||
}),
|
}),
|
||||||
|
internal: {
|
||||||
|
privateKey: getEnvironmentVariable({
|
||||||
|
key: "IDENTITY_PRIVATE_KEY",
|
||||||
|
type: z.string(),
|
||||||
|
fallback:
|
||||||
|
"-----BEGIN PRIVATE KEY-----\n" +
|
||||||
|
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2WYKMJZUWff5XOWC\n" +
|
||||||
|
"XGuU+wmsRzhQGEIzfUoL6rrGoaehRANCAATCpiGiFQxTA76EIVG0cBbj+AFt6BuJ\n" +
|
||||||
|
"t4q+zoInPUzkChCdwI+XfAYokrZwBjcyRGluC02HaN3cptrmjYSGSMSx\n" +
|
||||||
|
"-----END PRIVATE KEY-----",
|
||||||
|
}),
|
||||||
|
publicKey: getEnvironmentVariable({
|
||||||
|
key: "IDENTITY_PUBLIC_KEY",
|
||||||
|
type: z.string(),
|
||||||
|
fallback:
|
||||||
|
"-----BEGIN PUBLIC KEY-----\n" +
|
||||||
|
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwqYhohUMUwO+hCFRtHAW4/gBbegb\n" +
|
||||||
|
"ibeKvs6CJz1M5AoQncCPl3wGKJK2cAY3MkRpbgtNh2jd3Kba5o2EhkjEsQ==\n" +
|
||||||
|
"-----END PUBLIC KEY-----",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cookie: (maxAge: number) =>
|
||||||
|
({
|
||||||
|
httpOnly: true,
|
||||||
|
secure: getEnvironmentVariable({
|
||||||
|
key: "AUTH_COOKIE_SECURE",
|
||||||
|
type: z.coerce.boolean(),
|
||||||
|
fallback: "false",
|
||||||
|
}), // Set to true for HTTPS in production
|
||||||
|
maxAge,
|
||||||
|
path: "/",
|
||||||
|
sameSite: "strict",
|
||||||
|
}) satisfies SerializeOptions,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
import UserMetadata from "supertokens-node/recipe/usermetadata";
|
import { makeDocumentParser } from "@platform/database/utilities.ts";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
export enum PrincipalTypeId {
|
||||||
|
User = 1,
|
||||||
|
Group = 2,
|
||||||
|
Other = 99,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PRINCIPAL_TYPE_NAMES = {
|
||||||
|
[PrincipalTypeId.User]: "User",
|
||||||
|
[PrincipalTypeId.Group]: "Group",
|
||||||
|
[PrincipalTypeId.Other]: "Other",
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
| Schema
|
| Schema
|
||||||
@@ -9,33 +21,21 @@ import z from "zod";
|
|||||||
|
|
||||||
export const PrincipalSchema = z.object({
|
export const PrincipalSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
type: z.strictObject({
|
||||||
|
id: z.enum(PrincipalTypeId),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
roles: z.array(z.string()),
|
roles: z.array(z.string()),
|
||||||
attr: z.record(z.string(), z.any()),
|
attr: z.record(z.string(), z.any()),
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
| Utilities
|
| Parsers
|
||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
export const parsePrincipal = makeDocumentParser(PrincipalSchema);
|
||||||
* Get principal roles from the provided userId.
|
|
||||||
*
|
|
||||||
* @param userId - User to get principal roles from.
|
|
||||||
*/
|
|
||||||
export async function getPrincipalRoles(userId: string): Promise<string[]> {
|
|
||||||
return (await UserMetadata.getUserMetadata(userId)).metadata?.roles ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get principal attributes from the provided userId.
|
|
||||||
*
|
|
||||||
* @param userId - User to get principal attributes from.
|
|
||||||
*/
|
|
||||||
export async function getPrincipalAttributes(userId: string): Promise<Record<string, any>> {
|
|
||||||
return (await UserMetadata.getUserMetadata(userId)).metadata?.attr ?? {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
26
modules/identity/models/session.ts
Normal file
26
modules/identity/models/session.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Schema
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const SessionSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
userId: z.string(),
|
||||||
|
token: z.string(),
|
||||||
|
ipAddress: z.string().nullable().optional(),
|
||||||
|
userAgent: z.string().nullable().optional(),
|
||||||
|
createdAt: z.coerce.date(),
|
||||||
|
updatedAt: z.coerce.date(),
|
||||||
|
expiresAt: z.coerce.date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Types
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Session = z.infer<typeof SessionSchema>;
|
||||||
@@ -8,10 +8,14 @@
|
|||||||
"./server.ts": "./server.ts"
|
"./server.ts": "./server.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@platform/cerbos": "workspace:*",
|
||||||
"@platform/config": "workspace:*",
|
"@platform/config": "workspace:*",
|
||||||
"@platform/logger": "workspace:*",
|
"@platform/logger": "workspace:*",
|
||||||
"@platform/relay": "workspace:*",
|
"@platform/relay": "workspace:*",
|
||||||
"supertokens-node": "23.0.1",
|
"@platform/storage": "workspace:*",
|
||||||
|
"@platform/vault": "workspace:*",
|
||||||
|
"better-auth": "1.3.16",
|
||||||
|
"cookie": "1.0.2",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
import { ForbiddenError, NotFoundError } from "@platform/relay";
|
|
||||||
import { getPrincipalAttributes, getPrincipalRoles } from "@platform/supertoken/principal.ts";
|
|
||||||
import { getUserById } from "@platform/supertoken/users.ts";
|
|
||||||
|
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("session").handle(async ({ params: { id } }, { access }) => {
|
export default route.access("session").handle(async ({ params: { id } }, { session, principal, access }) => {
|
||||||
const user = await getUserById(id);
|
// const user = await getUserById(id);
|
||||||
if (user === undefined) {
|
// if (user === undefined) {
|
||||||
return new NotFoundError("Identity does not exist, or has been removed.");
|
// return new NotFoundError("Identity does not exist, or has been removed.");
|
||||||
}
|
// }
|
||||||
const decision = await access.isAllowed({ kind: "identity", id: user.id, attr: {} }, "read");
|
// const decision = await access.isAllowed({ kind: "identity", id: user.id, attr: {} }, "read");
|
||||||
if (decision === false) {
|
// if (decision === false) {
|
||||||
return new ForbiddenError("You do not have permission to view this identity.");
|
// return new ForbiddenError("You do not have permission to view this identity.");
|
||||||
}
|
// }
|
||||||
return {
|
// return {
|
||||||
id: user.id,
|
// id: user.id,
|
||||||
roles: await getPrincipalRoles(id),
|
// roles: await getPrincipalRoles(id),
|
||||||
attr: await getPrincipalAttributes(id),
|
// attr: await getPrincipalAttributes(id),
|
||||||
};
|
// };
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import { ForbiddenError } from "@platform/relay";
|
import { ForbiddenError, NotFoundError } from "@platform/relay";
|
||||||
import { getPrincipalAttributes } from "@platform/supertoken/principal.ts";
|
|
||||||
import UserMetadata from "supertokens-node/recipe/usermetadata";
|
|
||||||
|
|
||||||
|
import { getPrincipalById, setPrincipalAttributesById } from "../../../services/database.ts";
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("session").handle(async ({ params: { id }, body: ops }, { access }) => {
|
export default route.access("session").handle(async ({ params: { id }, body: ops }, { access }) => {
|
||||||
const decision = await access.isAllowed({ kind: "identity", id, attr: {} }, "update");
|
const principal = await getPrincipalById(id);
|
||||||
|
if (principal === undefined) {
|
||||||
|
return new NotFoundError();
|
||||||
|
}
|
||||||
|
const decision = await access.isAllowed({ kind: "identity", id: principal.id, attr: principal.attr }, "update");
|
||||||
if (decision === false) {
|
if (decision === false) {
|
||||||
return new ForbiddenError("You do not have permission to update this identity.");
|
return new ForbiddenError("You do not have permission to update this identity.");
|
||||||
}
|
}
|
||||||
const attr = await getPrincipalAttributes(id);
|
const attr = principal.attr;
|
||||||
for (const op of ops) {
|
for (const op of ops) {
|
||||||
switch (op.type) {
|
switch (op.type) {
|
||||||
case "add": {
|
case "add": {
|
||||||
@@ -36,5 +39,5 @@ export default route.access("session").handle(async ({ params: { id }, body: ops
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await UserMetadata.updateUserMetadata(id, { attr });
|
await setPrincipalAttributesById(id, attr);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +1,16 @@
|
|||||||
import { logger } from "@platform/logger";
|
|
||||||
import { NotFoundError } from "@platform/relay";
|
import { NotFoundError } from "@platform/relay";
|
||||||
import { getSessionHeaders } from "@platform/supertoken/session.ts";
|
|
||||||
import Passwordless from "supertokens-node/recipe/passwordless";
|
|
||||||
|
|
||||||
|
import { auth } from "../../../services/auth.ts";
|
||||||
|
import { logger } from "../../../services/logger.ts";
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("public").handle(async ({ body: { preAuthSessionId, deviceId, userInputCode } }) => {
|
export default route.access("public").handle(async ({ body: { email, otp } }) => {
|
||||||
const response = await Passwordless.consumeCode({ tenantId: "public", preAuthSessionId, deviceId, userInputCode });
|
const response = await auth.api.signInEmailOTP({ body: { email, otp }, asResponse: true, returnHeaders: true });
|
||||||
if (response.status !== "OK") {
|
if (response.status !== 200) {
|
||||||
|
logger.error("OTP Signin Failed", await response.json());
|
||||||
return new NotFoundError();
|
return new NotFoundError();
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info({
|
|
||||||
type: "code:claimed",
|
|
||||||
session: true,
|
|
||||||
message: "Identity resolved",
|
|
||||||
user: response.user.toJson(),
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 200,
|
headers: response.headers,
|
||||||
headers: await getSessionHeaders("public", response.recipeUserId),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ export default route
|
|||||||
.post("/api/v1/identity/login/code")
|
.post("/api/v1/identity/login/code")
|
||||||
.body(
|
.body(
|
||||||
z.strictObject({
|
z.strictObject({
|
||||||
deviceId: z.string(),
|
email: z.string(),
|
||||||
preAuthSessionId: z.string(),
|
otp: z.string(),
|
||||||
userInputCode: z.string(),
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query({
|
.query({
|
||||||
|
|||||||
@@ -1,23 +1,14 @@
|
|||||||
import { logger } from "@platform/logger";
|
import { auth } from "../../../services/auth.ts";
|
||||||
import Passwordless from "supertokens-node/recipe/passwordless";
|
import { logger } from "../../../services/logger.ts";
|
||||||
|
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("public").handle(async ({ body: { email } }) => {
|
export default route.access("public").handle(async ({ body: { email } }) => {
|
||||||
const response = await Passwordless.createCode({ tenantId: "public", email });
|
const response = await auth.api.sendVerificationOTP({ body: { email, type: "sign-in" } });
|
||||||
if (response.status !== "OK") {
|
if (response.success === false) {
|
||||||
return logger.info({
|
logger.info({
|
||||||
type: "auth:passwordless",
|
type: "auth:passwordless",
|
||||||
message: "Create code failed.",
|
message: "OTP Email verification failed.",
|
||||||
received: email,
|
received: email,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
logger.info({
|
|
||||||
type: "auth:passwordless",
|
|
||||||
data: {
|
|
||||||
deviceId: response.deviceId,
|
|
||||||
preAuthSessionId: response.preAuthSessionId,
|
|
||||||
userInputCode: response.userInputCode,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,48 +1,39 @@
|
|||||||
import { logger } from "@platform/logger";
|
|
||||||
import { NotFoundError } from "@platform/relay";
|
|
||||||
import { getSessionHeaders } from "@platform/supertoken/session.ts";
|
|
||||||
import Passwordless from "supertokens-node/recipe/passwordless";
|
|
||||||
|
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("public").handle(async ({ body: { email } }) => {
|
export default route.access("public").handle(async ({ body: { email } }) => {
|
||||||
const code = await Passwordless.createCode({ tenantId: "public", email });
|
// const code = await Passwordless.createCode({ tenantId: "public", email });
|
||||||
if (code.status !== "OK") {
|
// if (code.status !== "OK") {
|
||||||
return logger.info({
|
// return logger.info({
|
||||||
type: "auth:passwordless",
|
// type: "auth:passwordless",
|
||||||
message: "Create code failed.",
|
// message: "Create code failed.",
|
||||||
received: email,
|
// received: email,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
// logger.info({
|
||||||
logger.info({
|
// type: "auth:passwordless",
|
||||||
type: "auth:passwordless",
|
// data: {
|
||||||
data: {
|
// deviceId: code.deviceId,
|
||||||
deviceId: code.deviceId,
|
// preAuthSessionId: code.preAuthSessionId,
|
||||||
preAuthSessionId: code.preAuthSessionId,
|
// userInputCode: code.userInputCode,
|
||||||
userInputCode: code.userInputCode,
|
// },
|
||||||
},
|
// });
|
||||||
});
|
// const response = await Passwordless.consumeCode({
|
||||||
|
// tenantId: "public",
|
||||||
const response = await Passwordless.consumeCode({
|
// preAuthSessionId: code.preAuthSessionId,
|
||||||
tenantId: "public",
|
// deviceId: code.deviceId,
|
||||||
preAuthSessionId: code.preAuthSessionId,
|
// userInputCode: code.userInputCode,
|
||||||
deviceId: code.deviceId,
|
// });
|
||||||
userInputCode: code.userInputCode,
|
// if (response.status !== "OK") {
|
||||||
});
|
// return new NotFoundError();
|
||||||
if (response.status !== "OK") {
|
// }
|
||||||
return new NotFoundError();
|
// logger.info({
|
||||||
}
|
// type: "code:claimed",
|
||||||
|
// session: true,
|
||||||
logger.info({
|
// message: "Identity resolved",
|
||||||
type: "code:claimed",
|
// user: response.user.toJson(),
|
||||||
session: true,
|
// });
|
||||||
message: "Identity resolved",
|
// return new Response(null, {
|
||||||
user: response.user.toJson(),
|
// status: 200,
|
||||||
});
|
// headers: await getSessionHeaders("public", response.recipeUserId),
|
||||||
|
// });
|
||||||
return new Response(null, {
|
|
||||||
status: 200,
|
|
||||||
headers: await getSessionHeaders("public", response.recipeUserId),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import { ForbiddenError } from "@platform/relay";
|
import { ForbiddenError } from "@platform/relay";
|
||||||
import { getPrincipalRoles } from "@platform/supertoken/principal.ts";
|
import { NotFoundError } from "@platform/relay";
|
||||||
import UserMetadata from "supertokens-node/recipe/usermetadata";
|
|
||||||
|
|
||||||
|
import { getPrincipalById, setPrincipalRolesById } from "../../services/database.ts";
|
||||||
import route from "./spec.ts";
|
import route from "./spec.ts";
|
||||||
|
|
||||||
export default route.access("session").handle(async ({ params: { id }, body: ops }, { access }) => {
|
export default route.access("session").handle(async ({ params: { id }, body: ops }, { access }) => {
|
||||||
const decision = await access.isAllowed({ kind: "role", id, attr: {} }, "manage");
|
const principal = await getPrincipalById(id);
|
||||||
|
if (principal === undefined) {
|
||||||
|
return new NotFoundError();
|
||||||
|
}
|
||||||
|
const decision = await access.isAllowed({ kind: "role", id: principal.id, attr: principal.attr }, "manage");
|
||||||
if (decision === false) {
|
if (decision === false) {
|
||||||
return new ForbiddenError("You do not have permission to modify roles for this identity.");
|
return new ForbiddenError("You do not have permission to modify roles for this identity.");
|
||||||
}
|
}
|
||||||
const roles: Set<string> = new Set(await getPrincipalRoles(id));
|
const roles: Set<string> = new Set(principal.roles);
|
||||||
for (const op of ops) {
|
for (const op of ops) {
|
||||||
switch (op.type) {
|
switch (op.type) {
|
||||||
case "add": {
|
case "add": {
|
||||||
@@ -26,5 +30,5 @@ export default route.access("session").handle(async ({ params: { id }, body: ops
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await UserMetadata.updateUserMetadata(id, { roles: Array.from(roles) });
|
await setPrincipalRolesById(id, Array.from(roles));
|
||||||
});
|
});
|
||||||
|
|||||||
17
modules/identity/routes/session/resolve/handle.ts
Normal file
17
modules/identity/routes/session/resolve/handle.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { NotFoundError } from "@platform/relay";
|
||||||
|
|
||||||
|
import { config } from "../../../config.ts";
|
||||||
|
import { getPrincipalByUserId } from "../../../services/database.ts";
|
||||||
|
import { getSessionByRequestHeader } from "../../../services/session.ts";
|
||||||
|
import route from "./spec.ts";
|
||||||
|
|
||||||
|
export default route.access(["internal:public", config.internal.privateKey]).handle(async ({ request }) => {
|
||||||
|
const session = await getSessionByRequestHeader(request.headers);
|
||||||
|
if (session === undefined) {
|
||||||
|
return new NotFoundError();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
session,
|
||||||
|
principal: await getPrincipalByUserId(session.userId),
|
||||||
|
};
|
||||||
|
});
|
||||||
12
modules/identity/routes/session/resolve/spec.ts
Normal file
12
modules/identity/routes/session/resolve/spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { route } from "@platform/relay";
|
||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { PrincipalSchema } from "../../../models/principal.ts";
|
||||||
|
import { SessionSchema } from "../../../models/session.ts";
|
||||||
|
|
||||||
|
export default route.get("/api/v1/identity/session").response(
|
||||||
|
z.object({
|
||||||
|
session: SessionSchema,
|
||||||
|
principal: PrincipalSchema,
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -1,3 +1,43 @@
|
|||||||
|
import { HttpAdapter, makeClient } from "@platform/relay";
|
||||||
|
|
||||||
|
import { config } from "./config.ts";
|
||||||
|
import resolve from "./routes/session/resolve/spec.ts";
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Internal Session Resolver
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const identity = makeClient(
|
||||||
|
{
|
||||||
|
adapter: new HttpAdapter({
|
||||||
|
url: config.url,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolve: resolve.crypto({
|
||||||
|
publicKey: config.internal.publicKey,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Server Exports
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from "./services/access.ts";
|
||||||
|
export * from "./services/session.ts";
|
||||||
|
export * from "./types.ts";
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Module Server
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
(await import("./routes/identities/get/handle.ts")).default,
|
(await import("./routes/identities/get/handle.ts")).default,
|
||||||
@@ -8,5 +48,6 @@ export default {
|
|||||||
(await import("./routes/login/sudo/handle.ts")).default,
|
(await import("./routes/login/sudo/handle.ts")).default,
|
||||||
(await import("./routes/me/handle.ts")).default,
|
(await import("./routes/me/handle.ts")).default,
|
||||||
(await import("./routes/roles/handle.ts")).default,
|
(await import("./routes/roles/handle.ts")).default,
|
||||||
|
(await import("./routes/session/resolve/handle.ts")).default,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { cerbos } from "./cerbos.ts";
|
import { cerbos } from "@platform/cerbos";
|
||||||
import type { Principal } from "./principal.ts";
|
|
||||||
|
import { Principal } from "../models/principal.ts";
|
||||||
|
|
||||||
export function getAccessControlMethods(principal: Principal) {
|
export function getAccessControlMethods(principal: Principal) {
|
||||||
return {
|
return {
|
||||||
29
modules/identity/services/auth.ts
Normal file
29
modules/identity/services/auth.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { logger } from "@platform/logger";
|
||||||
|
import { betterAuth } from "better-auth";
|
||||||
|
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
||||||
|
import { emailOTP } from "better-auth/plugins";
|
||||||
|
|
||||||
|
import { db } from "./database.ts";
|
||||||
|
|
||||||
|
export const auth = betterAuth({
|
||||||
|
database: mongodbAdapter(db.db),
|
||||||
|
session: {
|
||||||
|
cookieCache: {
|
||||||
|
enabled: true,
|
||||||
|
maxAge: 5 * 60, // Cache duration in seconds
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
emailOTP({
|
||||||
|
async sendVerificationOTP({ email, otp, type }) {
|
||||||
|
if (type === "sign-in") {
|
||||||
|
logger.info({ email, otp, type });
|
||||||
|
} else if (type === "email-verification") {
|
||||||
|
// Send the OTP for email verification
|
||||||
|
} else {
|
||||||
|
// Send the OTP for password reset
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
61
modules/identity/services/database.ts
Normal file
61
modules/identity/services/database.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { getDatabaseAccessor } from "@platform/database/accessor.ts";
|
||||||
|
|
||||||
|
import {
|
||||||
|
parsePrincipal,
|
||||||
|
type Principal,
|
||||||
|
PRINCIPAL_TYPE_NAMES,
|
||||||
|
PrincipalSchema,
|
||||||
|
PrincipalTypeId,
|
||||||
|
} from "../models/principal.ts";
|
||||||
|
|
||||||
|
export const db = getDatabaseAccessor<{
|
||||||
|
principal: Principal;
|
||||||
|
}>("auth");
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Methods
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function getPrincipalById(id: string): Promise<Principal | undefined> {
|
||||||
|
return db
|
||||||
|
.collection("principal")
|
||||||
|
.findOne({ id })
|
||||||
|
.then((value) => parsePrincipal(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPrincipalRolesById(id: string, roles: string[]): Promise<void> {
|
||||||
|
await db.collection("principal").updateOne({ id }, { $set: { roles } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPrincipalAttributesById(id: string, attr: Record<string, any>): Promise<void> {
|
||||||
|
await db.collection("principal").updateOne({ id }, { $set: { attr } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a principal for a better-auth user.
|
||||||
|
*
|
||||||
|
* @param userId - User id from better-auth user list.
|
||||||
|
*/
|
||||||
|
export async function getPrincipalByUserId(userId: string): Promise<Principal> {
|
||||||
|
const principal = await db.collection("principal").findOneAndUpdate(
|
||||||
|
{ id: userId },
|
||||||
|
{
|
||||||
|
$setOnInsert: {
|
||||||
|
id: userId,
|
||||||
|
type: {
|
||||||
|
id: PrincipalTypeId.User,
|
||||||
|
name: PRINCIPAL_TYPE_NAMES[PrincipalTypeId.User],
|
||||||
|
},
|
||||||
|
roles: ["user"],
|
||||||
|
attr: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ upsert: true, returnDocument: "after" },
|
||||||
|
);
|
||||||
|
if (principal === null) {
|
||||||
|
throw new Error("Failed to resolve Principal");
|
||||||
|
}
|
||||||
|
return PrincipalSchema.parse(principal);
|
||||||
|
}
|
||||||
3
modules/identity/services/logger.ts
Normal file
3
modules/identity/services/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { logger as platformLogger } from "@platform/logger";
|
||||||
|
|
||||||
|
export const logger = platformLogger.prefix("Modules/Identity");
|
||||||
34
modules/identity/services/session.ts
Normal file
34
modules/identity/services/session.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import cookie from "cookie";
|
||||||
|
|
||||||
|
import { config } from "../config.ts";
|
||||||
|
import { auth } from "./auth.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session headers which can be applied on a Response object to apply
|
||||||
|
* an authenticated session to the respondent.
|
||||||
|
*
|
||||||
|
* @param accessToken - Token to apply to the cookie.
|
||||||
|
* @param maxAge - Max age of the token.
|
||||||
|
*/
|
||||||
|
export async function getSessionHeaders(accessToken: string, maxAge: number): Promise<Headers> {
|
||||||
|
return new Headers({
|
||||||
|
"set-cookie": cookie.serialize(
|
||||||
|
"better-auth.session_token",
|
||||||
|
encodeURIComponent(accessToken), // URL-encode the token
|
||||||
|
config.cookie(maxAge),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session container from request headers.
|
||||||
|
*
|
||||||
|
* @param headers - Request headers to extract session from.
|
||||||
|
*/
|
||||||
|
export async function getSessionByRequestHeader(headers: Headers) {
|
||||||
|
const response = await auth.api.getSession({ headers });
|
||||||
|
if (response === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return response.session;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import "@platform/relay";
|
import "@platform/relay";
|
||||||
import "@platform/storage";
|
import "@platform/storage";
|
||||||
|
|
||||||
import type Session from "supertokens-node/recipe/session";
|
import type { Session } from "better-auth";
|
||||||
|
|
||||||
import type { AccessControlMethods } from "./access.ts";
|
import type { AccessControlMethods } from "./access.ts";
|
||||||
import type { Principal } from "./principal.ts";
|
import type { Principal } from "./principal.ts";
|
||||||
@@ -11,7 +11,7 @@ declare module "@platform/storage" {
|
|||||||
/**
|
/**
|
||||||
* TODO ...
|
* TODO ...
|
||||||
*/
|
*/
|
||||||
session?: Session.SessionContainer;
|
session?: Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO ...
|
* TODO ...
|
||||||
@@ -35,7 +35,7 @@ declare module "@platform/relay" {
|
|||||||
/**
|
/**
|
||||||
* TODO ...
|
* TODO ...
|
||||||
*/
|
*/
|
||||||
session: Session.SessionContainer;
|
session: Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO ...
|
* TODO ...
|
||||||
@@ -16,18 +16,18 @@ resourcePolicy:
|
|||||||
roles: ["super", "admin", "user"]
|
roles: ["super", "admin", "user"]
|
||||||
condition:
|
condition:
|
||||||
match:
|
match:
|
||||||
expr: R.attr.id in P.attr.workspaceIds
|
expr: R.attr.workspaceId in P.attr.workspaceIds
|
||||||
|
|
||||||
- actions: ["update"]
|
- actions: ["update"]
|
||||||
effect: EFFECT_ALLOW
|
effect: EFFECT_ALLOW
|
||||||
roles: ["super", "admin"]
|
roles: ["super", "admin"]
|
||||||
condition:
|
condition:
|
||||||
match:
|
match:
|
||||||
expr: R.attr.id in P.attr.workspaceIds
|
expr: R.attr.workspaceId in P.attr.workspaceIds
|
||||||
|
|
||||||
- actions: ["delete"]
|
- actions: ["delete"]
|
||||||
effect: EFFECT_ALLOW
|
effect: EFFECT_ALLOW
|
||||||
roles: ["super"]
|
roles: ["super"]
|
||||||
condition:
|
condition:
|
||||||
match:
|
match:
|
||||||
expr: R.attr.id in P.attr.workspaceIds
|
expr: R.attr.workspaceId in P.attr.workspaceIds
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { container } from "@platform/database/container.ts";
|
import { mongo } from "@platform/database/client.ts";
|
||||||
import { EventFactory, EventStore, Prettify, Projector } from "@valkyr/event-store";
|
import { EventFactory, EventStore, Prettify, Projector } from "@valkyr/event-store";
|
||||||
import { MongoAdapter } from "@valkyr/event-store/mongo";
|
import { MongoAdapter } from "@valkyr/event-store/mongo";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ const eventFactory = new EventFactory([
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const eventStore = new EventStore({
|
export const eventStore = new EventStore({
|
||||||
adapter: new MongoAdapter(() => container.get("mongo"), `workspace:event-store`),
|
adapter: new MongoAdapter(() => mongo, `workspace:event-store`),
|
||||||
events: eventFactory,
|
events: eventFactory,
|
||||||
snapshot: "auto",
|
snapshot: "auto",
|
||||||
});
|
});
|
||||||
|
|||||||
1
platform/cerbos/mod.ts
Normal file
1
platform/cerbos/mod.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./cerbos.ts";
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@platform/supertoken",
|
"name": "@platform/auth",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./mod.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./mod.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cerbos/http": "0.23.1",
|
"@cerbos/http": "0.23.1",
|
||||||
"@platform/config": "workspace:*",
|
"@platform/logger": "workspace:*",
|
||||||
"cookie": "1.0.2",
|
|
||||||
"supertokens-node": "23.0.1",
|
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Collection, type CollectionOptions, type Db, type Document, type MongoClient } from "mongodb";
|
import { Collection, type CollectionOptions, type Db, type Document, type MongoClient } from "mongodb";
|
||||||
|
|
||||||
import { container } from "./container.ts";
|
import { mongo } from "./client.ts";
|
||||||
|
|
||||||
export function getDatabaseAccessor<TSchemas extends Record<string, Document>>(
|
export function getDatabaseAccessor<TSchemas extends Record<string, Document>>(
|
||||||
database: string,
|
database: string,
|
||||||
@@ -14,7 +14,7 @@ export function getDatabaseAccessor<TSchemas extends Record<string, Document>>(
|
|||||||
return instance;
|
return instance;
|
||||||
},
|
},
|
||||||
get client(): MongoClient {
|
get client(): MongoClient {
|
||||||
return container.get("mongo");
|
return mongo;
|
||||||
},
|
},
|
||||||
collection<TSchema extends keyof TSchemas>(
|
collection<TSchema extends keyof TSchemas>(
|
||||||
name: TSchema,
|
name: TSchema,
|
||||||
|
|||||||
4
platform/database/client.ts
Normal file
4
platform/database/client.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { config } from "./config.ts";
|
||||||
|
import { getMongoClient } from "./connection.ts";
|
||||||
|
|
||||||
|
export const mongo = getMongoClient(config.mongo);
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Container } from "@valkyr/inverse";
|
|
||||||
import { MongoClient } from "mongodb";
|
|
||||||
|
|
||||||
export const container = new Container<{
|
|
||||||
mongo: MongoClient;
|
|
||||||
}>("@platform/database");
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { config } from "./config.ts";
|
|
||||||
import { getMongoClient } from "./connection.ts";
|
|
||||||
import { container } from "./container.ts";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
bootstrap: async (): Promise<void> => {
|
|
||||||
container.set("mongo", getMongoClient(config.mongo));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import "@platform/supertoken/types.ts";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface ServerContext {}
|
export interface ServerContext {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./types.d.ts";
|
import "./types.ts";
|
||||||
|
|
||||||
import { context } from "@platform/relay";
|
import { context } from "@platform/relay";
|
||||||
import { InternalServerError } from "@platform/relay";
|
import { InternalServerError } from "@platform/relay";
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ declare module "@platform/storage" {
|
|||||||
|
|
||||||
declare module "@platform/relay" {
|
declare module "@platform/relay" {
|
||||||
interface ServerContext {
|
interface ServerContext {
|
||||||
isAuthenticated: boolean;
|
|
||||||
request: {
|
request: {
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
};
|
};
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { getEnvironmentVariable } from "@platform/config/environment.ts";
|
|
||||||
import type { SerializeOptions } from "cookie";
|
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
supertokens: {
|
|
||||||
connectionURI: getEnvironmentVariable({
|
|
||||||
key: "SUPERTOKEN_URI",
|
|
||||||
type: z.string(),
|
|
||||||
fallback: "http://localhost:3567",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
appInfo: {
|
|
||||||
appName: getEnvironmentVariable({
|
|
||||||
key: "PROJECT_NAME",
|
|
||||||
type: z.string(),
|
|
||||||
fallback: "Boilerplate",
|
|
||||||
}),
|
|
||||||
apiDomain: getEnvironmentVariable({
|
|
||||||
key: "API_DOMAIN",
|
|
||||||
type: z.string(),
|
|
||||||
fallback: "http://localhost:8370",
|
|
||||||
}),
|
|
||||||
websiteDomain: getEnvironmentVariable({
|
|
||||||
key: "APP_DOMAIN",
|
|
||||||
type: z.string(),
|
|
||||||
fallback: "http://localhost:3000",
|
|
||||||
}),
|
|
||||||
apiBasePath: "/api/v1/identity",
|
|
||||||
websiteBasePath: "/auth",
|
|
||||||
},
|
|
||||||
cookie: (maxAge: number) =>
|
|
||||||
({
|
|
||||||
httpOnly: true,
|
|
||||||
secure: getEnvironmentVariable({
|
|
||||||
key: "AUTH_COOKIE_SECURE",
|
|
||||||
type: z.coerce.boolean(),
|
|
||||||
fallback: "false",
|
|
||||||
}), // Set to true for HTTPS in production
|
|
||||||
maxAge,
|
|
||||||
path: "/",
|
|
||||||
sameSite: "strict",
|
|
||||||
}) satisfies SerializeOptions,
|
|
||||||
};
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
import "./types.ts";
|
|
||||||
|
|
||||||
import { UnauthorizedError } from "@platform/relay";
|
|
||||||
import { context } from "@platform/relay";
|
|
||||||
import { storage } from "@platform/storage";
|
|
||||||
import cookie from "cookie";
|
|
||||||
import supertokens from "supertokens-node";
|
|
||||||
import Passwordless from "supertokens-node/recipe/passwordless";
|
|
||||||
import Session from "supertokens-node/recipe/session";
|
|
||||||
|
|
||||||
import { getAccessControlMethods } from "./access.ts";
|
|
||||||
import { config } from "./config.ts";
|
|
||||||
import { getPrincipalAttributes, getPrincipalRoles, Principal } from "./principal.ts";
|
|
||||||
import { getSessionByAccessToken } from "./session.ts";
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
| Server Module
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default {
|
|
||||||
bootsrap: async () => {
|
|
||||||
bootstrapSuperTokens();
|
|
||||||
bootstrapStorageContext();
|
|
||||||
},
|
|
||||||
resolve: async (request: Request): Promise<void> => {
|
|
||||||
await resolveSession(request);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
| Bootstrap Methods
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
function bootstrapSuperTokens() {
|
|
||||||
supertokens.init({
|
|
||||||
framework: "custom",
|
|
||||||
supertokens: config.supertokens,
|
|
||||||
appInfo: config.appInfo,
|
|
||||||
recipeList: [
|
|
||||||
Passwordless.init({
|
|
||||||
flowType: "USER_INPUT_CODE",
|
|
||||||
contactMethod: "EMAIL",
|
|
||||||
}),
|
|
||||||
Session.init({
|
|
||||||
getTokenTransferMethod: () => "cookie",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function bootstrapStorageContext() {
|
|
||||||
Object.defineProperties(context, {
|
|
||||||
/**
|
|
||||||
* TODO ...
|
|
||||||
*/
|
|
||||||
isAuthenticated: {
|
|
||||||
get() {
|
|
||||||
return storage.getStore()?.principal !== undefined;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO ...
|
|
||||||
*/
|
|
||||||
session: {
|
|
||||||
get() {
|
|
||||||
const session = storage.getStore()?.session;
|
|
||||||
if (session === undefined) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO ...
|
|
||||||
*/
|
|
||||||
principal: {
|
|
||||||
get() {
|
|
||||||
const principal = storage.getStore()?.principal;
|
|
||||||
if (principal === undefined) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
}
|
|
||||||
return principal;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO ...
|
|
||||||
*/
|
|
||||||
access: {
|
|
||||||
get() {
|
|
||||||
const access = storage.getStore()?.access;
|
|
||||||
if (access === undefined) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
}
|
|
||||||
return access;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
| Request Middleware
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function resolveSession(request: Request): Promise<void> {
|
|
||||||
const accessToken = cookie.parse(request.headers.get("cookie") ?? "").sAccessToken;
|
|
||||||
if (accessToken !== undefined) {
|
|
||||||
const session = await getSessionByAccessToken(accessToken);
|
|
||||||
|
|
||||||
const store = storage.getStore();
|
|
||||||
if (store === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const principal: Principal = {
|
|
||||||
id: session.getUserId(),
|
|
||||||
roles: await getPrincipalRoles(session.getUserId()),
|
|
||||||
attr: await getPrincipalAttributes(session.getUserId()),
|
|
||||||
};
|
|
||||||
|
|
||||||
store.session = session;
|
|
||||||
store.principal = principal;
|
|
||||||
store.access = getAccessControlMethods(principal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import cookie from "cookie";
|
|
||||||
import type { RecipeUserId } from "supertokens-node/index.js";
|
|
||||||
import Session from "supertokens-node/recipe/session";
|
|
||||||
|
|
||||||
import { config } from "./config.ts";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session headers which can be applied on a Response object to apply
|
|
||||||
* an authenticted session to the respondant.
|
|
||||||
*
|
|
||||||
* @param tenantId - Tenant scope the session belongs to.
|
|
||||||
* @param recipeUserId - User recipe to apply to the session.
|
|
||||||
*/
|
|
||||||
export async function getSessionHeaders(tenantId: string, recipeUserId: RecipeUserId): Promise<Headers> {
|
|
||||||
const session = await Session.createNewSessionWithoutRequestResponse(tenantId, recipeUserId);
|
|
||||||
const tokens = session.getAllSessionTokensDangerously();
|
|
||||||
const options = config.cookie(await session.getExpiry());
|
|
||||||
|
|
||||||
const headers = new Headers({ "set-cookie": cookie.serialize("sAccessToken", tokens.accessToken, options) });
|
|
||||||
if (tokens.refreshToken !== undefined) {
|
|
||||||
headers.append("set-cookie", cookie.serialize("sRefreshToken", tokens.refreshToken, options));
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session container from access token.
|
|
||||||
*
|
|
||||||
* @param accessToken - Access token to resolve session from.
|
|
||||||
* @param antiCsrfToken - Optional CSRF token.
|
|
||||||
*/
|
|
||||||
export async function getSessionByAccessToken(
|
|
||||||
accessToken: string,
|
|
||||||
antiCsrfToken?: string,
|
|
||||||
): Promise<Session.SessionContainer> {
|
|
||||||
return Session.getSessionWithoutRequestResponse(accessToken, antiCsrfToken);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import supertokens, { type User } from "supertokens-node";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a user by provided user id.
|
|
||||||
*
|
|
||||||
* @param userId - User id to retrieve.
|
|
||||||
*/
|
|
||||||
export async function getUserById(userId: string): Promise<User | undefined> {
|
|
||||||
return supertokens.getUser(userId);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user