diff --git a/asset-api/index.js b/asset-api/index.js index ee553d6..43b8816 100644 --- a/asset-api/index.js +++ b/asset-api/index.js @@ -60,8 +60,8 @@ app.get("/crypto/algo", () => { app.put("/asset", { async handler(request, reply) { - let user = await userFromSessionKey(request.query.session_key); - if (user.assets.length >= user.limits.assetCount) { + let { user, limits } = await userFromSessionKey(request.query.session_key); + if (user.assets.length >= limits.maxAssetCount) { reply.code(403); return "Max asset count reached. Contact support or upgrade your plan"; } @@ -102,7 +102,7 @@ app.put("/asset", { app.get("/asset", { async handler(request, reply) { - let user = await userFromSessionKey(request.query.session_key); + let { user, limits } = await userFromSessionKey(request.query.session_key); if ('statusCode' in user) { reply.code(500); diff --git a/identity-api/.env.example b/identity-api/.env.example index 5699528..e1d5eec 100644 --- a/identity-api/.env.example +++ b/identity-api/.env.example @@ -2,4 +2,5 @@ IDENTITY_API_LANDING_MESSAGE = "identity-api v1.0.0" IDENTITY_API_JWT_SECRET = "cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2" IDENTITY_API_JWT_ALG = "HS256" IDENTITY_API_ASSET_API_ENDPOINT = "http://localhost:3001" -IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS = 60000 \ No newline at end of file +IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS = 60000 +IDENTITY_SQLITE_PATH = ".database/identity.sqlite" \ No newline at end of file diff --git a/identity-api/.gitignore b/identity-api/.gitignore index 94bf562..b2f3032 100644 --- a/identity-api/.gitignore +++ b/identity-api/.gitignore @@ -1,3 +1,5 @@ node_modules/ +dist/ .yarn -.env \ No newline at end of file +.env +.database \ No newline at end of file diff --git a/identity-api/docs/database/schema.dbml b/identity-api/docs/database/schema.dbml new file mode 100644 index 0000000..7d65005 --- /dev/null +++ b/identity-api/docs/database/schema.dbml @@ -0,0 +1,76 @@ +// You can render this DBML file using specialized software or websites such as: +// https://dbdiagram.io/ + +Table limits { + id varchar [primary key] + current_asset_count integer [not null] + max_asset_count integer [not null] +} + +Table users { + id varchar [primary key] + created_at timestamp [not null] + last_connected_at timestamp [not null] + email varchar [not null] + password varchar [not null] + name varchar [not null] + limits varchar [not null] + assets varchar [not null, note: 'Comma separated list'] +} + +Table session_keys { + key varchar [primary key] + user_id varchar [not null] +} + +Table heirs { + id varchar [primary key] + user_id varchar [not null] + created_at timestamp [not null] + name varchar [not null] + email varchar +} + +Table entries { + id varchar [primary key] + user_id varchar [not null] + created_at timestamp [not null] + feelings text [not null, note: 'Comma separated JSON-encoded list'] + assets text [not null, note: 'Comma separated list'] + title text + description text + + kind varchar [not null, note: 'Kind of entry'] + music_entry varchar + location_entry varchar + date_entry varchar +} + +Table music_entries { + id varchar [primary key] + artist varchar [not null] + title varchar [not null] + links text [not null, note: 'Comma separated list'] + universal_ids text [not null, note: 'Comma separated JSON-encoded list of Universal IDs'] +} + +Table location_entries { + id varchar [primary key] + location_text text + location_coordinates varchar [note: 'JSON encoded location'] +} + +Table date_entries { + id varchar [primary key] + referenced_date timestamp +} + +Ref: users.limits > limits.id + +Ref: heirs.user_id > users.id +Ref: entries.user_id > users.id +Ref: session_keys.user_id > users.id + +Ref: entries.music_entry > music_entries.id +Ref: entries.location_entry > location_entries.id +Ref: entries.date_entry > date_entries.id \ No newline at end of file diff --git a/identity-api/package.json b/identity-api/package.json index 43ca908..73b7297 100644 --- a/identity-api/package.json +++ b/identity-api/package.json @@ -8,19 +8,26 @@ "packageManager": "yarn@4.3.0", "dependencies": { "@fastify/cors": "^9.0.1", + "@fastify/type-provider-typebox": "^4.0.0", + "@sinclair/typebox": "^0.32.34", + "argon2": "^0.40.3", + "better-sqlite3": "^11.1.1", "dotenv": "^16.4.5", + "drizzle-orm": "^0.31.2", "fastify": "^4.27.0", "jose": "^5.4.0" }, "scripts": { - "start": "node src/index.js", + "start": "tsc && node dist/index.js", "lint:fix": "eslint --fix && prettier . --write", "lint": "eslint && prettier . --check" }, "devDependencies": { "@eslint/js": "^9.5.0", + "@types/node": "^20.14.9", "eslint": "9.x", "globals": "^15.5.0", - "prettier": "3.3.2" + "prettier": "3.3.2", + "typescript": "^5.5.2" } } diff --git a/identity-api/src/app.js b/identity-api/src/app.ts similarity index 80% rename from identity-api/src/app.js rename to identity-api/src/app.ts index 6044e24..9d364e8 100644 --- a/identity-api/src/app.js +++ b/identity-api/src/app.ts @@ -14,8 +14,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import Fastify from "fastify"; -export default Fastify({ +let app = Fastify({ logger: true, -}); +}).withTypeProvider() + +export type AppInterface = typeof app; +export default app; diff --git a/identity-api/src/auth.js b/identity-api/src/auth.js deleted file mode 100644 index 6610b15..0000000 --- a/identity-api/src/auth.js +++ /dev/null @@ -1,101 +0,0 @@ -// Identity. Store your memories and mental belongings -// Copyright (C) 2024 Sofía Aritz -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import assert from "node:assert"; -import { randomUUID } from "node:crypto"; -import * as Jose from "jose"; -import { JWT_ALG, JWT_SECRET } from "./consts.js"; - -export async function startAuth() { - let session_keys = { - "uid:005d6417-a23c-48bd-b348-eafeae649b94": "e381ba8c-e18a-4bca-afce-b212b37bc26b", - "key:e381ba8c-e18a-4bca-afce-b212b37bc26b": "005d6417-a23c-48bd-b348-eafeae649b94", - }; - - let users = { - "jane@identity.net": { - uid: "005d6417-a23c-48bd-b348-eafeae649b94", - email: "jane@identity.net", - password: "12345678901234567890", - name: "Jane Doe", - assets: ["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"], - limits: { - assetCount: 5, - }, - heirs: [ - { - contactMethod: "email", - name: "Sofía Aritz", - value: "sofi@sofiaritz.com", - }, - ], - entries: JSON.parse( - `[{"id":"0","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0a","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1a","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2a","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3a","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4a","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5a","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6a","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0b","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1b","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2b","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3b","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4b","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5b","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6b","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0c","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1c","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2c","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3c","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4c","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5c","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6c","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0d","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1d","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2d","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3d","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4d","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5d","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6d","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0e","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1e","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2e","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3e","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4e","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5e","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6e","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0f","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1f","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2f","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3f","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4f","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5f","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6f","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]},{"id":"0g","creationDate":"2024-04-13T00:00:00.000Z","feelings":["active","happy"],"base":{"kind":"song","id":[{"provider":"spotify","id":"53mChDyESfwn9Dz8poHRf6"}],"link":["https://open.spotify.com/track/53mChDyESfwn9Dz8poHRf6"],"title":"Taking What's Not Yours","artist":"TV Girl"},"assets":[]},{"id":"1g","creationDate":"2024-04-13T00:00:00.000Z","feelings":[],"base":{"kind":"album","id":[{"provider":"spotify","id":"1d2PspdXmwrBEcOtquCvzT"}],"link":["https://open.spotify.com/album/1d2PspdXmwrBEcOtquCvzT"],"title":"CHASER","artist":"femtanyl"},"assets":[]},{"id":"2g","creationDate":"2024-04-26T00:00:00.000Z","feelings":["excited"],"title":"SalmorejoTech 2024","description":"SalmorejoTech is a great tech-event. I met some people and everything went great! :)","base":{"kind":"event"},"assets":[]},{"id":"3g","creationDate":"2024-06-26T00:00:00.000Z","feelings":["happy","relaxed"],"title":"Playing Minecraft with Mr. Pablo","description":"Mr. Pablo is my friend, she is a great friend. We spent a good time playing Minecraft. I am lucky to have a friend like him.","base":{"kind":"memory"},"assets":[]},{"id":"4g","creationDate":"2024-01-01T00:00:00.000Z","feelings":["excited","nervous"],"description":"New Year, New me! I'm really excited about what's going to happen this year, lots of changes. Changes may be scary, but they usually are for good!","base":{"kind":"feeling"},"assets":["f9d040d6-598c-4483-952f-08e7d35d5420.jpg"]},{"id":"5g","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy","relaxed"],"title":"The park","description":"The park is a really chill place where I can go and relax for a bit before going to work.","base":{"kind":"environment"},"assets":[]},{"id":"6g","creationDate":"2024-04-28T00:00:00.000Z","feelings":["happy"],"description":"This day has been a great day! I've talked with my friends.","base":{"kind":"date","referencedDate":"2024-04-27T00:00:00.000Z"},"assets":[]}]`, - ), - }, - }; - - let funcs = { - user: async (uid) => { - let user = Object.values(users).filter((v) => v.uid === uid); - assert(user.length <= 1); - - return structuredClone(user[0]); - }, - findUserByEmail: async (email) => { - return structuredClone(users[email]); - }, - findUserBySessionKey: async (session_key) => { - let uid = session_keys[`key:${session_key}`]; - return await funcs.user(uid); - }, - updateUser: async (uid, newUser) => { - let user = await funcs.user(uid); - users[user.email] = newUser; - }, - addUser: async (user) => { - user.uid = randomUUID().toString(); - users[user.email] = user; - - return structuredClone(users[user.email]); - }, - createSessionKey: async (uid) => { - let key = randomUUID().toString(); - session_keys[`uid:${uid}`] = key; - session_keys[`key:${key}`] = uid; - - return key; - }, - createJwt: async (uid) => { - let user = await funcs.user(uid); - - return await new Jose.SignJWT({ - uid: user.uid, - email: user.email, - name: user.name, - }) - .setProtectedHeader({ alg: JWT_ALG }) - .setIssuedAt() - .setExpirationTime("4w") - .sign(JWT_SECRET); - }, - verifyJwt: async (jwt) => { - return await Jose.jwtVerify(jwt, JWT_SECRET); - }, - }; - - return funcs; -} diff --git a/identity-api/src/auth.ts b/identity-api/src/auth.ts new file mode 100644 index 0000000..ab1ea0b --- /dev/null +++ b/identity-api/src/auth.ts @@ -0,0 +1,121 @@ +// Identity. Store your memories and mental belongings +// Copyright (C) 2024 Sofía Aritz +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import * as argon2 from "argon2"; +import { randomUUID } from "node:crypto"; +import * as Jose from "jose"; +import { JWT_ALG, JWT_SECRET } from "./consts.js"; +import { DatabaseInterface, toDBList } from "./database.js"; + + +export type AuthInterface = Awaited>; + +export type User = { + id: string, + createdAt: number, + lastConnected: number, + name: string, + email: string, + password: string, + limitID: string, +} + +export type NewUser = { + name: string, + email: string, + password: string, +} + +export type UpdateUser = { + name?: string, + email?: string, + password?: string, + assets?: string[], +} + +export async function startAuth(database: DatabaseInterface) { + let funcs = { + user: async (uid: string) => await database.user(uid) satisfies User, + findUserByEmail: async (email: string) => await database.findUserByEmail(email) satisfies User, + findUserBySessionKey: async (sessionKey: string) => { + let key = await database.sessionKey(sessionKey); + return await database.user(key.userID); + }, + updateUser: async (uid: string, newUser: UpdateUser) => { + let user = newUser as any; + user.assets = toDBList(user.assets); + + return await database.updateUser(uid, user); + }, + addUser: async (user: NewUser) => { + let result = await database.insertUser({ + id: randomUUID(), + createdAt: Date.now(), + lastConnected: Date.now(), + name: user.name, + email: user.email, + password: await argon2.hash(user.password), + assets: toDBList([]), + // FIXME: This shouldn't be required, the DB interface overwrites it. + limitID: "", + }, { + id: randomUUID(), + currentAssetCount: 0, + maxAssetCount: 5, + }); + + return result satisfies User; + }, + verifyPassword: async (user: User, password: string) => { + return await argon2.verify(user.password, password); + }, + createSessionKey: async (uid) => { + return await database.insertSessionKey({ + userID: uid, + key: randomUUID(), + }); + }, + createJwt: async (uid) => { + let user = await funcs.user(uid); + + return await new Jose.SignJWT({ + uid: user.id, + email: user.email, + name: user.name, + }) + .setProtectedHeader({ alg: JWT_ALG }) + .setIssuedAt() + .setExpirationTime("4w") + .sign(JWT_SECRET); + }, + verifyJwt: async (jwt) => { + return await Jose.jwtVerify<{ + uid: string, + email: string, + name: string, + }>(jwt, JWT_SECRET); + }, + cleanUser: (user: User) => { + let clean = user as any; + clean.password = undefined; + clean.limitID = undefined; + + return clean; + } + }; + + return funcs; +} diff --git a/identity-api/src/consts.js b/identity-api/src/consts.ts similarity index 79% rename from identity-api/src/consts.js rename to identity-api/src/consts.ts index 291da48..fb56113 100644 --- a/identity-api/src/consts.js +++ b/identity-api/src/consts.ts @@ -17,7 +17,12 @@ import "dotenv/config"; import app from "./app.js"; -const REQUIRED_VARS = ["IDENTITY_API_JWT_SECRET", "IDENTITY_API_ASSET_API_ENDPOINT", "IDENTITY_API_JWT_ALG"]; +const REQUIRED_VARS = [ + "IDENTITY_API_JWT_SECRET", + "IDENTITY_API_ASSET_API_ENDPOINT", + "IDENTITY_API_JWT_ALG", + "IDENTITY_SQLITE_PATH", +]; REQUIRED_VARS.forEach((element) => { if ( @@ -32,7 +37,8 @@ REQUIRED_VARS.forEach((element) => { export const IDENTITY_API_LANDING_MESSAGE = process.env["IDENTITY_API_LANDING_MESSAGE"] || "identity-api v1.0.0"; export const JWT_SECRET = new TextEncoder().encode(process.env["IDENTITY_API_JWT_SECRET"]); export const JWT_ALG = process.env["IDENTITY_API_JWT_ALG"]; -export const LISTEN_PORT = process.env["IDENTITY_API_LISTEN_PORT"] || 3000; +export const LISTEN_PORT = Number(process.env["IDENTITY_API_LISTEN_PORT"]) || 3000; export const ASSET_API_ENDPOINT = process.env["IDENTITY_API_ASSET_API_ENDPOINT"]; export const ASSET_API_M2M_REFRESH_INTERVAL = - process.env["IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS"] || 60 * 1000; + Number(process.env["IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS"]) || 60 * 1000; +export const SQLITE_PATH = process.env["IDENTITY_SQLITE_PATH"]; diff --git a/identity-api/src/database.ts b/identity-api/src/database.ts new file mode 100644 index 0000000..915ddd2 --- /dev/null +++ b/identity-api/src/database.ts @@ -0,0 +1,316 @@ +// Identity. Store your memories and mental belongings +// Copyright (C) 2024 Sofía Aritz +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { drizzle } from "drizzle-orm/better-sqlite3"; +import Database from "better-sqlite3"; +import { SQLITE_PATH } from "./consts.js"; +import { sqliteTable } from "drizzle-orm/sqlite-core"; +import { text, integer } from "drizzle-orm/sqlite-core"; +import { asc, desc, eq, sql } from "drizzle-orm"; + +export type DatabaseInterface = Awaited> + +export function toDBList(input: any[]): string { + return JSON.stringify(input); +} + +export function fromDBList(input: string): Array { + return JSON.parse(input) +} + +export async function startDatabase() { + let sqlite = new Database(SQLITE_PATH); + let database = drizzle(sqlite); + + const limits = sqliteTable("limits", { + id: text("id").primaryKey(), + currentAssetCount: integer("current_asset_count").notNull(), + maxAssetCount: integer("max_asset_count").notNull(), + }); + + const users = sqliteTable("users", { + id: text("id").primaryKey(), + createdAt: integer("created_at").notNull(), + lastConnected: integer("last_connected_at").notNull(), + name: text("name").notNull(), + email: text("email").notNull(), + password: text("password").notNull(), + assets: text("assets").notNull(), + limitID: text("limits") + .notNull() + .references(() => limits.id), + }); + + const session_keys = sqliteTable("session_keys", { + key: text("key").primaryKey(), + userID: text("user_id") + .notNull() + .references(() => users.id), + }); + + const heirs = sqliteTable("heirs", { + id: text("id").primaryKey(), + userID: text("user_id") + .notNull() + .references(() => users.id), + createdAt: integer("created_at").notNull(), + name: text("name").notNull(), + email: text("email"), + }); + + const musicEntries = sqliteTable("music_entries", { + id: text("id").primaryKey(), + artist: text("artist").notNull(), + title: text("title").notNull(), + links: text("links").notNull(), + universalIDs: text("universal_ids").notNull(), + }); + + const locationEntries = sqliteTable("location_entries", { + id: text("id").primaryKey(), + locationText: text("location_text"), + locationCoordinates: text("location_coordinates"), + }); + + const dateEntries = sqliteTable("date_entries", { + id: text("id").primaryKey(), + referencedDate: integer("referenced_date"), + }); + + const entries = sqliteTable("entries", { + id: text("id").primaryKey(), + userID: text("user_id") + .notNull() + .references(() => users.id), + createdAt: integer("created_at").notNull(), + feelings: text("feelings").notNull(), + assets: text("assets").notNull(), + title: text("title"), + description: text("description"), + kind: text("kind").notNull(), + musicEntry: text("music_entry").references(() => musicEntries.id), + locationEntry: text("location_entry").references(() => locationEntries.id), + dateEntry: text("date_entry").references(() => dateEntries.id), + }); + + await runMigrations(database); + + let funcs = { + insertHeir: async (heir: typeof heirs.$inferInsert) => { + let result = await database.insert(heirs).values(heir).returning({ id: heirs.id }); + return result[0].id; + }, + removeHeir: async (id: string) => { + await database.delete(heirs).where(eq(heirs.id, id)); + }, + listHeirs: async (userID: string) => { + return await database.select().from(heirs).where(eq(heirs.userID, userID)); + }, + insertUser: async (user: typeof users.$inferInsert, limit: typeof limits.$inferInsert) => { + let limitsResult = await database.insert(limits).values(limit).returning({ id: limits.id }); + user.limitID = limitsResult[0].id; + + let userResult = await database.insert(users).values(user).returning(); + return userResult[0]; + }, + user: async (userID: string) => { + let result = await database.select().from(users).where(eq(users.id, userID)); + return result[0]; + }, + userLimits: async (limitsID: string) => { + let result = await database.select().from(limits).where(eq(limits.id, limitsID)); + return result[0]; + }, + updateUser: async (userID: string, newUser: { + name?: string, + email?: string, + password?: string, + }) => { + let result = await database.update(users).set(newUser).where(eq(users.id, userID)).returning(); + return result[0]; + }, + findUserByEmail: async (email: string) => { + let result = await database.select().from(users).where(eq(users.email, email)); + return result[0]; + }, + insertSessionKey: async (key: typeof session_keys.$inferInsert) => { + let result = await database.insert(session_keys).values(key).returning({ key: session_keys.key }); + return result[0].key; + }, + sessionKey: async (key: string) => { + let result = await database.select().from(session_keys).where(eq(session_keys.key, key)); + return result[0]; + }, + findSessionKeyByUserID: async (userID: string) => { + return await database.select().from(session_keys).where(eq(session_keys.userID, userID)); + }, + insertEntry: async (entry: typeof entries.$inferInsert, musicEntry?: typeof musicEntries.$inferInsert, locationEntry?: typeof locationEntries.$inferInsert, dateEntry?: typeof dateEntries.$inferInsert) => { + if (entry.kind === "album" || entry.kind === "song") { + let result = await database.insert(musicEntries).values(musicEntry).returning({ id: musicEntries.id }); + entry.musicEntry = result[0].id; + } else if (entry.kind === "environment") { + let result = await database + .insert(locationEntries) + .values(locationEntry) + .returning({ id: locationEntries.id }); + entry.locationEntry = result[0].id; + } else if (entry.kind === "date") { + let result = await database.insert(dateEntries).values(dateEntry).returning({ id: dateEntries.id }); + entry.dateEntry = result[0].id; + } + + let result = await database.insert(entries).values(entry).returning({ id: entries.id }); + return result[0].id; + }, + removeEntry: async (entryID: string) => { + await database.delete(entries).where(eq(entries.id, entryID)); + }, + entryPage: async (userID: string, offset: number, limit: number) => { + let result = await database + .select() + .from(entries) + .where(eq(entries.userID, userID)) + .limit(limit) + .offset(offset) + .orderBy(desc(entries.createdAt)); + + for (let key in result) { + if (!result.hasOwnProperty(key)) { continue; } + + let entry = structuredClone(result[key]); + let base = {}; + if (entry.musicEntry != null) { + let musicDetails = (await database.select().from(musicEntries).where(eq(musicEntries.id, entry.musicEntry)))[0]; + (musicDetails["link"] as any) = fromDBList(musicDetails.links); + (musicDetails["id"] as any) = fromDBList(musicDetails.universalIDs); + + musicDetails["links"] = undefined; + musicDetails["universalIDs"] = undefined; + base = musicDetails; + } else if (entry.locationEntry != null) { + let locationDetails = (await database.select().from(locationEntries).where(eq(locationEntries.id, entry.locationEntry)))[0]; + + if (locationDetails.locationCoordinates != null) { + base = { location: JSON.parse(locationDetails.locationCoordinates) } + } else if (locationDetails.locationText != null) { + base = { location: locationDetails.locationText } + } + } else if (entry.dateEntry != null) { + let dateDetails = (await database.select().from(dateEntries).where(eq(dateEntries.id, entry.dateEntry)))[0]; + + base = { + referencedDate: new Date(dateDetails["referencedDate"]).toISOString() + }; + } + + base["kind"] = entry.kind + + result[key]["creationDate"] = new Date(entry.createdAt).toISOString(); + (result[key] as any)["feelings"] = fromDBList(entry.feelings); + (result[key] as any)["assets"] = fromDBList(entry.assets); + result[key]["base"] = base; + + result[key].kind = undefined; + result[key].userID = undefined; + result[key].musicEntry = undefined; + result[key].locationEntry = undefined; + result[key].dateEntry = undefined; + } + + return result; + }, + }; + + return funcs; +} + +async function runMigrations(database) { + await database.run(sql`CREATE TABLE IF NOT EXISTS limits ( + id varchar PRIMARY KEY, + current_asset_count integer NOT NULL, + max_asset_count integer NOT NULL + );`); + + await database.run(sql`CREATE TABLE IF NOT EXISTS users ( + id varchar PRIMARY KEY, + created_at timestamp NOT NULL, + last_connected_at timestamp NOT NULL, + email varchar NOT NULL, + password varchar NOT NULL, + name varchar NOT NULL, + limits varchar NOT NULL, + assets varchar NOT NULL, + FOREIGN KEY (limits) REFERENCES limits (id) + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS session_keys ( + key varchar PRIMARY KEY, + user_id varchar NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS heirs ( + id varchar PRIMARY KEY, + user_id varchar NOT NULL, + created_at timestamp NOT NULL, + name varchar NOT NULL, + email varchar, + FOREIGN KEY (user_id) REFERENCES users (id) + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS entries ( + id varchar PRIMARY KEY, + user_id varchar NOT NULL, + created_at timestamp NOT NULL, + feelings text NOT NULL, + assets text NOT NULL, + title text, + description text, + kind varchar NOT NULL, + music_entry varchar, + location_entry varchar, + date_entry varchar, + FOREIGN KEY (user_id) REFERENCES users (id), + FOREIGN KEY (music_entry) REFERENCES music_entries (id), + FOREIGN KEY (location_entry) REFERENCES location_entries (id), + FOREIGN KEY (date_entry) REFERENCES date_entries (id) + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS music_entries ( + id varchar PRIMARY KEY, + artist varchar NOT NULL, + title varchar NOT NULL, + links text NOT NULL, + universal_ids text NOT NULL + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS location_entries ( + id varchar PRIMARY KEY, + location_text text, + location_coordinates varchar + );`); + + await database.run(sql` + CREATE TABLE IF NOT EXISTS date_entries ( + id varchar PRIMARY KEY, + referenced_date timestamp + );`); +} diff --git a/identity-api/src/index.js b/identity-api/src/index.ts similarity index 89% rename from identity-api/src/index.js rename to identity-api/src/index.ts index f5d4476..eac16ed 100644 --- a/identity-api/src/index.js +++ b/identity-api/src/index.ts @@ -20,8 +20,10 @@ import { startAuth } from "./auth.js"; import cors from "@fastify/cors"; import { registerRoutes } from "./routes/index.js"; +import { startDatabase } from "./database.js"; -let auth = await startAuth(); +let database = await startDatabase(); +let auth = await startAuth(database); app.addSchema({ $id: "schema://identity/authorization", @@ -36,7 +38,7 @@ app.register(cors, { origin: true, }); -registerRoutes(app, auth); +registerRoutes(app, auth, database); app.get("/", async () => { return IDENTITY_API_LANDING_MESSAGE; diff --git a/identity-api/src/m2m.js b/identity-api/src/m2m.ts similarity index 100% rename from identity-api/src/m2m.js rename to identity-api/src/m2m.ts diff --git a/identity-api/src/routes/asset/endpoint.js b/identity-api/src/routes/asset/endpoint.ts similarity index 90% rename from identity-api/src/routes/asset/endpoint.js rename to identity-api/src/routes/asset/endpoint.ts index aacf129..be0b2d8 100644 --- a/identity-api/src/routes/asset/endpoint.js +++ b/identity-api/src/routes/asset/endpoint.ts @@ -14,10 +14,11 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - import { ASSET_API_ENDPOINT } from "../../consts.js"; -export default function register(app) { +import type { AppInterface } from "../../app.js"; + +export default function register(app: AppInterface) { app.get("/asset/endpoint", { async handler() { return ASSET_API_ENDPOINT; diff --git a/identity-api/src/routes/asset/index.js b/identity-api/src/routes/asset/index.ts similarity index 86% rename from identity-api/src/routes/asset/index.js rename to identity-api/src/routes/asset/index.ts index 08965f4..1c1c937 100644 --- a/identity-api/src/routes/asset/index.js +++ b/identity-api/src/routes/asset/index.ts @@ -14,9 +14,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import type { AppInterface } from "../../app.js"; import endpoint from "./endpoint.js"; -export default function registerRoutes(app, auth) { - endpoint(app, auth); +export default function registerRoutes(app: AppInterface) { + endpoint(app); } diff --git a/identity-api/src/routes/auth/account.js b/identity-api/src/routes/auth/account.ts similarity index 85% rename from identity-api/src/routes/auth/account.js rename to identity-api/src/routes/auth/account.ts index 8a86148..4b5e9fc 100644 --- a/identity-api/src/routes/auth/account.js +++ b/identity-api/src/routes/auth/account.ts @@ -14,8 +14,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; -export default function register(app, auth) { +export default function register(app: AppInterface, auth: AuthInterface) { app.get("/auth/account", { async handler(request, reply) { let jwt = request.headers["authorization"].replace("Bearer", "").trim(); @@ -27,9 +29,7 @@ export default function register(app, auth) { } let user = await auth.user(payload.uid); - user.password = undefined; - user.entries = undefined; - return user; + return auth.cleanUser(user); }, schema: { headers: { $ref: "schema://identity/authorization" }, diff --git a/identity-api/src/routes/auth/genkey.js b/identity-api/src/routes/auth/genkey.ts similarity index 87% rename from identity-api/src/routes/auth/genkey.js rename to identity-api/src/routes/auth/genkey.ts index 484f200..d07f299 100644 --- a/identity-api/src/routes/auth/genkey.js +++ b/identity-api/src/routes/auth/genkey.ts @@ -14,8 +14,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; -export default function register(app, auth) { +export default function register(app: AppInterface, auth: AuthInterface) { app.get("/auth/genkey", { async handler(request) { let jwt = request.headers["authorization"].replace("Bearer", "").trim(); diff --git a/identity-api/src/routes/auth/heirs.js b/identity-api/src/routes/auth/heirs.js deleted file mode 100644 index 90c786b..0000000 --- a/identity-api/src/routes/auth/heirs.js +++ /dev/null @@ -1,41 +0,0 @@ -// Identity. Store your memories and mental belongings -// Copyright (C) 2024 Sofía Aritz -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -export default function register(app, auth) { - app.post("/auth/heirs", { - async handler(request, reply) { - let jwt = request.headers["authorization"].replace("Bearer", "").trim(); - let { payload } = await auth.verifyJwt(jwt); - - if (payload.uid == null) { - reply.status(401); - return; - } - - let user = await auth.user(payload.uid); - user.heirs = request.body; - - await auth.updateUser(payload.uid, user); - }, - schema: { - headers: { $ref: "schema://identity/authorization" }, - body: { - type: "array", - }, - }, - }); -} diff --git a/identity-api/src/routes/auth/heirs.ts b/identity-api/src/routes/auth/heirs.ts new file mode 100644 index 0000000..9e2a8b3 --- /dev/null +++ b/identity-api/src/routes/auth/heirs.ts @@ -0,0 +1,90 @@ +// Identity. Store your memories and mental belongings +// Copyright (C) 2024 Sofía Aritz +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { randomUUID } from "node:crypto"; +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { DatabaseInterface } from "../../database.js"; +import { Static, Type } from "@sinclair/typebox"; + +const Body = Type.Object({ + contactMethod: Type.String(), + name: Type.String(), + value: Type.String(), +}) + +type BodyType = Static; + +export default function register(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { + app.put<{ Body: BodyType }>("/auth/heirs", { + async handler(request, reply) { + let jwt = request.headers["authorization"].replace("Bearer", "").trim(); + let { payload } = await auth.verifyJwt(jwt); + + if (payload.uid == null) { + reply.status(401); + return; + } + + if (request.body.contactMethod !== "email") { + reply.status(400); + return; + } + + await database.insertHeir({ + id: randomUUID(), + createdAt: Date.now(), + userID: payload.uid, + name: request.body.name, + email: request.body.value, + }); + + return (await database.listHeirs(payload.uid)) + .map(v => v["contactMethod"] = "email") + .map(v => v["value"] = v["email"]) + .map(v => v["email"] = undefined); + }, + schema: { + headers: { $ref: "schema://identity/authorization" }, + body: Body, + }, + }); + + app.delete<{ Body: string }>("/auth/heirs", { + async handler(request, reply) { + let jwt = request.headers["authorization"].replace("Bearer", "").trim(); + let { payload } = await auth.verifyJwt(jwt); + + if (payload.uid == null) { + reply.status(401); + return; + } + + await database.removeHeir(request.body); + + return (await database.listHeirs(payload.uid)) + .map(v => v["contactMethod"] = "email") + .map(v => v["value"] = v["email"]) + .map(v => v["email"] = undefined); + }, + schema: { + headers: { $ref: "schema://identity/authorization" }, + body: { + type: "string", + }, + }, + }); +} diff --git a/identity-api/src/routes/auth/index.js b/identity-api/src/routes/auth/index.ts similarity index 77% rename from identity-api/src/routes/auth/index.js rename to identity-api/src/routes/auth/index.ts index e9a7f16..45ad505 100644 --- a/identity-api/src/routes/auth/index.js +++ b/identity-api/src/routes/auth/index.ts @@ -14,6 +14,9 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { DatabaseInterface } from "../../database.js"; import account from "./account.js"; import genkey from "./genkey.js"; @@ -21,10 +24,10 @@ import heirs from "./heirs.js"; import login from "./login.js"; import register from "./register.js"; -export default function registerRoutes(app, auth) { +export default function registerRoutes(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { account(app, auth); genkey(app, auth); - heirs(app, auth); + heirs(app, auth, database); login(app, auth); register(app, auth); } diff --git a/identity-api/src/routes/auth/login.js b/identity-api/src/routes/auth/login.ts similarity index 65% rename from identity-api/src/routes/auth/login.js rename to identity-api/src/routes/auth/login.ts index 53445dd..a6931ca 100644 --- a/identity-api/src/routes/auth/login.js +++ b/identity-api/src/routes/auth/login.ts @@ -14,14 +14,24 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import { Static, Type } from "@sinclair/typebox"; +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; -export default function register(app, auth) { - app.post("/auth/login", { +const Body = Type.Object({ + email: Type.String({ format: "email" }), + password: Type.String(), +}); + +type BodyType = Static; + +export default function register(app: AppInterface, auth: AuthInterface) { + app.post<{ Body: BodyType }>("/auth/login", { async handler(request, reply) { let user = await auth.findUserByEmail(request.body.email); - if (user != null && user.password == request.body.password) { - let token = await auth.createJwt(user.uid); + if (user != null && auth.verifyPassword(user, request.body.password)) { + let token = await auth.createJwt(user.id); return { token, @@ -34,14 +44,7 @@ export default function register(app, auth) { }; }, schema: { - body: { - type: "object", - properties: { - email: { type: "string" }, - password: { type: "string" }, - }, - }, - required: ["email", "password"], + body: Body, }, }); } diff --git a/identity-api/src/routes/auth/register.js b/identity-api/src/routes/auth/register.ts similarity index 67% rename from identity-api/src/routes/auth/register.js rename to identity-api/src/routes/auth/register.ts index 426d94f..f3a0b68 100644 --- a/identity-api/src/routes/auth/register.js +++ b/identity-api/src/routes/auth/register.ts @@ -14,23 +14,29 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import { Static, Type } from "@sinclair/typebox"; +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; -export default function register(app, auth) { - app.post("/auth/register", { +const Body = Type.Object({ + name: Type.String(), + email: Type.String({ format: "email" }), + password: Type.String(), +}); + +type BodyType = Static; + +export default function register(app: AppInterface, auth: AuthInterface) { + app.post<{ Body: BodyType }>("/auth/register", { async handler(request, reply) { if ((await auth.findUserByEmail(request.body.email)) == null) { let user = await auth.addUser({ email: request.body.email, password: request.body.password, name: request.body.name, - assets: [], - limits: { - assetCount: 5, - }, - entries: [], }); - - return { token: await auth.createJwt(user.uid) }; + + return { token: await auth.createJwt(user.id) }; } reply.code(400); @@ -39,15 +45,7 @@ export default function register(app, auth) { }; }, schema: { - body: { - type: "object", - properties: { - name: { type: "string" }, - email: { type: "string" }, - password: { type: "string" }, - }, - required: ["name", "email", "password"], - }, + body: Body, }, }); } diff --git a/identity-api/src/routes/entry/index.js b/identity-api/src/routes/entry/index.js deleted file mode 100644 index 3c29844..0000000 --- a/identity-api/src/routes/entry/index.js +++ /dev/null @@ -1,68 +0,0 @@ -// Identity. Store your memories and mental belongings -// Copyright (C) 2024 Sofía Aritz -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -import { randomUUID } from "node:crypto"; -import list from "./list.js"; - -export default function registerRoutes(app, auth) { - list(app, auth); - - app.delete("/entry", { - async handler(request) { - let jwt = request.headers["authorization"].replace("Bearer", "").trim(); - let { payload } = await auth.verifyJwt(jwt); - - let user = await auth.user(payload.uid); - user.entries = user.entries.filter((v) => v.id !== request.query.entry_id); - - await auth.updateUser(payload.uid, user); - }, - schema: { - headers: { $ref: "schema://identity/authorization" }, - query: { - type: "object", - properties: { - entry_id: { type: "string" }, - }, - required: ["entry_id"], - }, - }, - }); - - app.put("/entry", { - async handler(request) { - let jwt = request.headers["authorization"].replace("Bearer", "").trim(); - let { payload } = await auth.verifyJwt(jwt); - - let user = await auth.user(payload.uid); - request.body.entry.id = randomUUID().toString(); - user.entries = [request.body.entry, ...user.entries]; - - await auth.updateUser(payload.uid, user); - }, - schema: { - headers: { $ref: "schema://identity/authorization" }, - body: { - type: "object", - properties: { - entry: { type: "object" }, - }, - required: ["entry"], - }, - }, - }); -} diff --git a/identity-api/src/routes/entry/index.ts b/identity-api/src/routes/entry/index.ts new file mode 100644 index 0000000..32d8b08 --- /dev/null +++ b/identity-api/src/routes/entry/index.ts @@ -0,0 +1,145 @@ +// Identity. Store your memories and mental belongings +// Copyright (C) 2024 Sofía Aritz +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { randomUUID } from "node:crypto"; +import list from "./list.js"; + +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { toDBList, type DatabaseInterface } from "../../database.js"; +import { Static, Type } from "@sinclair/typebox"; + +const EntryIDQuery = Type.Object({ + entry_id: Type.String(), +}) + +type EntryIDQueryType = Static; + +const PutEntryBody = Type.Object({ + entry: Type.Object({ + title: Type.Optional(Type.String()), + description: Type.Optional(Type.String()), + creationDate: Type.String(), + assets: Type.Array(Type.String()), + feelings: Type.Array( + Type.Union([ + Type.String(), + Type.Object({ + identifier: Type.String(), + description: Type.String(), + backgroundColor: Type.String(), + textColor: Type.String(), + }), + ]) + ), + base: Type.Union([ + Type.Object({ + kind: Type.String(), + + }), + Type.Object({ + kind: Type.String(), + artist: Type.String(), + title: Type.String(), + link: Type.Array(Type.String()), + id: Type.Array(Type.Object({ + provider: Type.String(), + id: Type.String(), + })), + }), + Type.Object({ + kind: Type.String(), + location: Type.Union([ + Type.String(), + Type.Object({ + latitude: Type.Number(), + longitude: Type.Number(), + }), + ]), + }), + Type.Object({ + kind: Type.String(), + referencedDate: Type.String(), + }), + ]), + }) +}); + +type PutEntryBodyType = Static; + +export default function registerRoutes(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { + list(app, auth, database); + + app.delete<{ Querystring: EntryIDQueryType }>("/entry", { + async handler(request) { + let jwt = request.headers["authorization"].replace("Bearer", "").trim(); + let { payload } = await auth.verifyJwt(jwt); + + let user = await auth.user(payload.uid); + database.removeEntry(request.query.entry_id); + }, + schema: { + headers: { $ref: "schema://identity/authorization" }, + querystring: EntryIDQuery, + }, + }); + + app.put<{ Body: PutEntryBodyType }>("/entry", { + async handler(request) { + let jwt = request.headers["authorization"].replace("Bearer", "").trim(); + let { payload } = await auth.verifyJwt(jwt); + + let entry = request.body.entry; + + let musicEntry, locationEntry, dateEntry; + if ((entry.base.kind === "album" || entry.base.kind === "song") && 'artist' in entry.base) { + musicEntry = { + id: randomUUID(), + title: entry.base.title, + artist: entry.base.artist, + links: toDBList(entry.base.link), + universalIDs: toDBList(entry.base.id), + } + } else if (entry.base.kind === "environment" && 'location' in entry.base) { + locationEntry = { + id: randomUUID(), + locationText: typeof entry.base.location === "string" ? entry.base.location : undefined, + locationCoordinates: typeof entry.base.location === "string" ? undefined : JSON.stringify(entry.base.location), + } + } else if (entry.base.kind === "date" && 'referencedDate' in entry.base) { + dateEntry = { + id: randomUUID(), + referencedDate: Date.parse(entry.base.referencedDate), + } + } + + await database.insertEntry({ + id: randomUUID(), + userID: payload.uid, + feelings: toDBList(entry.feelings), + assets: toDBList(entry.assets), + title: entry.title, + description: entry.description, + kind: entry.base.kind, + createdAt: Date.now(), + }, musicEntry, locationEntry, dateEntry); + }, + schema: { + headers: { $ref: "schema://identity/authorization" }, + body: PutEntryBody, + }, + }); +} diff --git a/identity-api/src/routes/entry/list.js b/identity-api/src/routes/entry/list.ts similarity index 65% rename from identity-api/src/routes/entry/list.js rename to identity-api/src/routes/entry/list.ts index 47dbe37..9b9680f 100644 --- a/identity-api/src/routes/entry/list.js +++ b/identity-api/src/routes/entry/list.ts @@ -14,9 +14,20 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import { Static, Type } from "@sinclair/typebox"; +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import type { DatabaseInterface } from "../../database.js"; -export default function register(app, auth) { - app.get("/entry/list", { +const Query = Type.Object({ + limit: Type.Number(), + offset: Type.Number(), +}) + +type QueryType = Static; + +export default function register(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { + app.get<{ Querystring: QueryType }>("/entry/list", { async handler(request, reply) { if (request.query.offset < 0 || request.query.limit <= 0) { reply.status(400); @@ -26,19 +37,11 @@ export default function register(app, auth) { let jwt = request.headers["authorization"].replace("Bearer", "").trim(); let { payload } = await auth.verifyJwt(jwt); - let user = await auth.user(payload.uid); - return user.entries.slice(request.query.offset, request.query.offset + request.query.limit); + return await database.entryPage(payload.uid, request.query.offset, request.query.limit) }, schema: { headers: { $ref: "schema://identity/authorization" }, - query: { - type: "object", - properties: { - limit: { type: "number" }, - offset: { type: "number" }, - }, - required: ["limit", "offset"], - }, + querystring: Query, }, }); } diff --git a/identity-api/src/routes/index.js b/identity-api/src/routes/index.ts similarity index 71% rename from identity-api/src/routes/index.js rename to identity-api/src/routes/index.ts index 75f2a13..406fc61 100644 --- a/identity-api/src/routes/index.js +++ b/identity-api/src/routes/index.ts @@ -14,15 +14,18 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - import m2m from "./m2m/index.js"; import asset from "./asset/index.js"; import entry from "./entry/index.js"; import authRoutes from "./auth/index.js"; -export function registerRoutes(app, auth) { - m2m(app, auth); - asset(app, auth); - entry(app, auth); - authRoutes(app, auth); +import type { AppInterface } from "../app.js"; +import type { DatabaseInterface } from "../database.js"; +import type { AuthInterface } from "../auth.js"; + +export function registerRoutes(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { + m2m(app, auth, database); + asset(app); + entry(app, auth, database); + authRoutes(app, auth, database); } diff --git a/identity-api/src/routes/m2m/account.js b/identity-api/src/routes/m2m/account.ts similarity index 67% rename from identity-api/src/routes/m2m/account.js rename to identity-api/src/routes/m2m/account.ts index 623d42a..cf4e4c9 100644 --- a/identity-api/src/routes/m2m/account.js +++ b/identity-api/src/routes/m2m/account.ts @@ -14,24 +14,30 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - import { contentFromSigned, verifySignature } from "../../m2m.js"; -export default function register(app, auth) { +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { DatabaseInterface, fromDBList } from "../../database.js"; + +export default function register(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { app.post("/m2m/account", { async handler(request, reply) { if (!verifySignature(request.body)) { - reply.statusCode(401); + reply.status(401); return; } let body = JSON.parse(contentFromSigned(request.body)); - + let user = await auth.findUserBySessionKey(body.session_key); - user.password = undefined; - user.entries = undefined; + let limits = await database.userLimits(user.limitID); + (user.assets as any) = fromDBList(user.assets); - return user; + return { + user: auth.cleanUser(user), + limits, + }; }, }); } diff --git a/identity-api/src/routes/m2m/asset.js b/identity-api/src/routes/m2m/asset.ts similarity index 72% rename from identity-api/src/routes/m2m/asset.js rename to identity-api/src/routes/m2m/asset.ts index 13de09e..bb25280 100644 --- a/identity-api/src/routes/m2m/asset.js +++ b/identity-api/src/routes/m2m/asset.ts @@ -14,23 +14,29 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - import { contentFromSigned, verifySignature } from "../../m2m.js"; -export default function register(app, auth) { +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { fromDBList } from "../../database.js"; + +export default function register(app: AppInterface, auth: AuthInterface) { app.put("/m2m/asset", { async handler(request, reply) { if (!verifySignature(request.body)) { - reply.statusCode(401); + reply.status(401); return; } let body = JSON.parse(contentFromSigned(request.body)); let user = await auth.findUserBySessionKey(body.session_key); - user.assets.push(body.asset_id); - - await auth.updateUser(user.uid, user); + let assets = fromDBList(user.assets) as string[]; + assets.push(body.asset_id); + + await auth.updateUser(user.id, { + assets, + }); app.log.info((await auth.findUserBySessionKey(body.session_key)).assets); }, }); diff --git a/identity-api/src/routes/m2m/index.js b/identity-api/src/routes/m2m/index.ts similarity index 74% rename from identity-api/src/routes/m2m/index.js rename to identity-api/src/routes/m2m/index.ts index 356e771..c50b56a 100644 --- a/identity-api/src/routes/m2m/index.js +++ b/identity-api/src/routes/m2m/index.ts @@ -14,11 +14,14 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - import asset from "./asset.js"; import account from "./account.js"; -export default function registerRoutes(app, auth) { +import type { AppInterface } from "../../app.js"; +import type { AuthInterface } from "../../auth.js"; +import { DatabaseInterface } from "../../database.js"; + +export default function registerRoutes(app: AppInterface, auth: AuthInterface, database: DatabaseInterface) { asset(app, auth); - account(app, auth); + account(app, auth, database); } diff --git a/identity-api/tsconfig.json b/identity-api/tsconfig.json new file mode 100644 index 0000000..7df0b1a --- /dev/null +++ b/identity-api/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "target": "ES2021", + "moduleResolution": "NodeNext", + "sourceMap": true, + "outDir": "dist", + "types": ["node"], + "skipLibCheck": true + }, + "include": ["./src/**/*"], + "exclude": ["./node_modules/**/*"] +} \ No newline at end of file diff --git a/identity-api/yarn.lock b/identity-api/yarn.lock index 9bf0a0a..2a2021b 100644 --- a/identity-api/yarn.lock +++ b/identity-api/yarn.lock @@ -111,6 +111,15 @@ __metadata: languageName: node linkType: hard +"@fastify/type-provider-typebox@npm:^4.0.0": + version: 4.0.0 + resolution: "@fastify/type-provider-typebox@npm:4.0.0" + peerDependencies: + "@sinclair/typebox": ">=0.26 <=0.32" + checksum: 10c0/1134f9e8873c4057a2dc18fc85729e3ea3a33bcaaf91efd66bfca63ac2bf9715f33f3412643ecba0a5120e7861235580b13f4bc1a46dd12c5c6ecc4ed5574a46 + languageName: node + linkType: hard + "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -125,6 +134,20 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -152,6 +175,65 @@ __metadata: languageName: node linkType: hard +"@npmcli/agent@npm:^2.0.0": + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0": + version: 3.1.1 + resolution: "@npmcli/fs@npm:3.1.1" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99 + languageName: node + linkType: hard + +"@phc/format@npm:^1.0.0": + version: 1.0.0 + resolution: "@phc/format@npm:1.0.0" + checksum: 10c0/56cec2687ac3767298f77b2202c0d6f282878c1375948b01da16e03475dc293628847d33150469a793d55095700dce65ac1760ada271deeab15bd0c6d1ceb306 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.32.34": + version: 0.32.34 + resolution: "@sinclair/typebox@npm:0.32.34" + checksum: 10c0/7d0e2e950c8f7aed74501c1abc59171f1090e521b58700cf24a47c6a4e2c3b6da5b6b0e0f7c0878e64eaa811ae4c65ceca34ca396f000d254601638393b8dad6 + languageName: node + linkType: hard + +"@types/node@npm:^20.14.9": + version: 20.14.9 + resolution: "@types/node@npm:20.14.9" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/911ffa444dc032897f4a23ed580c67903bd38ea1c5ec99b1d00fa10b83537a3adddef8e1f29710cbdd8e556a61407ed008e06537d834e48caf449ce59f87d387 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -186,6 +268,25 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: "npm:^2.0.0" + indent-string: "npm:^4.0.0" + checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039 + languageName: node + linkType: hard + "ajv-formats@npm:^2.1.1": version: 2.1.1 resolution: "ajv-formats@npm:2.1.1" @@ -245,7 +346,14 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^4.1.0": +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" dependencies: @@ -254,6 +362,25 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c + languageName: node + linkType: hard + +"argon2@npm:^0.40.3": + version: 0.40.3 + resolution: "argon2@npm:0.40.3" + dependencies: + "@phc/format": "npm:^1.0.0" + node-addon-api: "npm:^8.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.0" + checksum: 10c0/994cf1ae5625aeb5ec2d0e0de67f2cf64b8723e0cc8f3cca844b99b00f57e8ad5cc6eb43b2287c9bd0fb9bd15d147e9341327115a447a6208c47ca7cc78705f5 + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -292,6 +419,37 @@ __metadata: languageName: node linkType: hard +"better-sqlite3@npm:^11.1.1": + version: 11.1.1 + resolution: "better-sqlite3@npm:11.1.1" + dependencies: + bindings: "npm:^1.5.0" + node-gyp: "npm:latest" + prebuild-install: "npm:^7.1.1" + checksum: 10c0/60b67d89f22d78afb810cb18dd446e397d35b9f95895793a3934da17f257193ac0d112798f115af3fea5ea1f2318e98051abd632a443d530ff20760a08be7d57 + languageName: node + linkType: hard + +"bindings@npm:^1.5.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: "npm:1.0.0" + checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba + languageName: node + linkType: hard + +"bl@npm:^4.0.3": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: "npm:^5.5.0" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -302,6 +460,25 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f + languageName: node + linkType: hard + +"buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e + languageName: node + linkType: hard + "buffer@npm:^6.0.3": version: 6.0.3 resolution: "buffer@npm:6.0.3" @@ -312,6 +489,26 @@ __metadata: languageName: node linkType: hard +"cacache@npm:^18.0.0": + version: 18.0.3 + resolution: "cacache@npm:18.0.3" + dependencies: + "@npmcli/fs": "npm:^3.1.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^4.0.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + unique-filename: "npm:^3.0.0" + checksum: 10c0/dfda92840bb371fb66b88c087c61a74544363b37a265023223a99965b16a16bbb87661fe4948718d79df6e0cc04e85e62784fbcf1832b2a5e54ff4c46fbb45b7 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -329,6 +526,27 @@ __metadata: languageName: node linkType: hard +"chownr@npm:^1.1.1": + version: 1.1.4 + resolution: "chownr@npm:1.1.4" + checksum: 10c0/ed57952a84cc0c802af900cf7136de643d3aba2eecb59d29344bc2f3f9bf703a301b9d84cdc71f82c3ffc9ccde831b0d92f5b45f91727d6c9da62f23aef9d9db + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1 + languageName: node + linkType: hard + "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -359,7 +577,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.2": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -370,7 +588,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.3.1, debug@npm:^4.3.2": +"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.5 resolution: "debug@npm:4.3.5" dependencies: @@ -382,6 +600,22 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" + dependencies: + mimic-response: "npm:^3.1.0" + checksum: 10c0/bd89d23141b96d80577e70c54fb226b2f40e74a6817652b80a116d7befb8758261ad073a8895648a29cc0a5947021ab66705cb542fa9c143c82022b27c5b175e + languageName: node + linkType: hard + +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -389,6 +623,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.0": + version: 2.0.3 + resolution: "detect-libc@npm:2.0.3" + checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 + languageName: node + linkType: hard + "dotenv@npm:^16.4.5": version: 16.4.5 resolution: "dotenv@npm:16.4.5" @@ -396,6 +637,146 @@ __metadata: languageName: node linkType: hard +"drizzle-orm@npm:^0.31.2": + version: 0.31.2 + resolution: "drizzle-orm@npm:0.31.2" + peerDependencies: + "@aws-sdk/client-rds-data": ">=3" + "@cloudflare/workers-types": ">=3" + "@electric-sql/pglite": ">=0.1.1" + "@libsql/client": "*" + "@neondatabase/serverless": ">=0.1" + "@op-engineering/op-sqlite": ">=2" + "@opentelemetry/api": ^1.4.1 + "@planetscale/database": ">=1" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/react": ">=18" + "@types/sql.js": "*" + "@vercel/postgres": ">=0.8.0" + "@xata.io/client": "*" + better-sqlite3: ">=7" + bun-types: "*" + expo-sqlite: ">=13.2.0" + knex: "*" + kysely: "*" + mysql2: ">=2" + pg: ">=8" + postgres: ">=3" + react: ">=18" + sql.js: ">=1" + sqlite3: ">=5" + peerDependenciesMeta: + "@aws-sdk/client-rds-data": + optional: true + "@cloudflare/workers-types": + optional: true + "@electric-sql/pglite": + optional: true + "@libsql/client": + optional: true + "@neondatabase/serverless": + optional: true + "@op-engineering/op-sqlite": + optional: true + "@opentelemetry/api": + optional: true + "@planetscale/database": + optional: true + "@tidbcloud/serverless": + optional: true + "@types/better-sqlite3": + optional: true + "@types/pg": + optional: true + "@types/react": + optional: true + "@types/sql.js": + optional: true + "@vercel/postgres": + optional: true + "@xata.io/client": + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + react: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + checksum: 10c0/7fb7604dc50204047fc78745c1a956f395dd582f05db9dbf76cfcc658fb180fc3012cfec03fd669140e67184d2ca32419bb3c09aafeb5db5dbe0c2db73f2fe5a + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -528,6 +909,20 @@ __metadata: languageName: node linkType: hard +"expand-template@npm:^2.0.3": + version: 2.0.3 + resolution: "expand-template@npm:2.0.3" + checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579 + languageName: node + linkType: hard + "fast-content-type-parse@npm:^1.1.0": version: 1.1.0 resolution: "fast-content-type-parse@npm:1.1.0" @@ -650,6 +1045,13 @@ __metadata: languageName: node linkType: hard +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 + languageName: node + linkType: hard + "find-my-way@npm:^8.0.0": version: 8.2.0 resolution: "find-my-way@npm:8.2.0" @@ -688,6 +1090,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.2.1 + resolution: "foreground-child@npm:3.2.1" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/9a53a33dbd87090e9576bef65fb4a71de60f6863a8062a7b11bc1cbe3cc86d428677d7c0b9ef61cdac11007ac580006f78bd5638618d564cfd5e6fd713d6878f + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -695,6 +1107,38 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 + languageName: node + linkType: hard + +"github-from-package@npm:0.0.0": + version: 0.0.0 + resolution: "github-from-package@npm:0.0.0" + checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12 + languageName: node + linkType: hard + "glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" @@ -704,6 +1148,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.2.2, glob@npm:^10.3.10": + version: 10.4.2 + resolution: "glob@npm:10.4.2" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/2c7296695fa75a935f3ad17dc62e4e170a8bb8752cf64d328be8992dd6ad40777939003754e10e9741ff8fbe43aa52fba32d6930d0ffa0e3b74bc3fb5eebaa2f + languageName: node + linkType: hard + "globals@npm:^14.0.0": version: 14.0.0 resolution: "globals@npm:14.0.0" @@ -718,6 +1178,13 @@ __metadata: languageName: node linkType: hard +"graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -725,22 +1192,65 @@ __metadata: languageName: node linkType: hard +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" + dependencies: + agent-base: "npm:^7.0.2" + debug: "npm:4" + checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + languageName: node + linkType: hard + "identity-api@workspace:.": version: 0.0.0-use.local resolution: "identity-api@workspace:." dependencies: "@eslint/js": "npm:^9.5.0" "@fastify/cors": "npm:^9.0.1" + "@fastify/type-provider-typebox": "npm:^4.0.0" + "@sinclair/typebox": "npm:^0.32.34" + "@types/node": "npm:^20.14.9" + argon2: "npm:^0.40.3" + better-sqlite3: "npm:^11.1.1" dotenv: "npm:^16.4.5" + drizzle-orm: "npm:^0.31.2" eslint: "npm:9.x" fastify: "npm:^4.27.0" globals: "npm:^15.5.0" jose: "npm:^5.4.0" prettier: "npm:3.3.2" + typescript: "npm:^5.5.2" languageName: unknown linkType: soft -"ieee754@npm:^1.2.1": +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb @@ -771,6 +1281,37 @@ __metadata: languageName: node linkType: hard +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f + languageName: node + linkType: hard + +"inherits@npm:^2.0.3, inherits@npm:^2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + "ipaddr.js@npm:1.9.1": version: 1.9.1 resolution: "ipaddr.js@npm:1.9.1" @@ -785,6 +1326,13 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + "is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -794,6 +1342,13 @@ __metadata: languageName: node linkType: hard +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d + languageName: node + linkType: hard + "is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" @@ -808,6 +1363,26 @@ __metadata: languageName: node linkType: hard +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.0 + resolution: "jackspeak@npm:3.4.0" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/7e42d1ea411b4d57d43ea8a6afbca9224382804359cb72626d0fc45bb8db1de5ad0248283c3db45fe73e77210750d4fcc7c2b4fe5d24fda94aaa24d658295c5f + languageName: node + linkType: hard + "jose@npm:^5.4.0": version: 5.4.0 resolution: "jose@npm:5.4.0" @@ -826,6 +1401,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -909,6 +1491,40 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.3.0 + resolution: "lru-cache@npm:10.3.0" + checksum: 10c0/02d57024d90672774d66e0b76328a8975483b782c68118078363be17b8e0efb4f2bee89d98ce87e72f42d68fe7cb4ad14b1205d43e4f9954f5c91e3be4eaceb8 + languageName: node + linkType: hard + +"make-fetch-happen@npm:^13.0.0": + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" + dependencies: + "@npmcli/agent": "npm:^2.0.0" + cacache: "npm:^18.0.0" + http-cache-semantics: "npm:^4.1.1" + is-lambda: "npm:^1.0.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^0.6.3" + proc-log: "npm:^4.2.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^10.0.0" + checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e + languageName: node + linkType: hard + +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 10c0/0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 + languageName: node + linkType: hard + "minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -918,6 +1534,122 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + +"minimist@npm:^1.2.0, minimist@npm:^1.2.3": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e + languageName: node + linkType: hard + +"minipass-fetch@npm:^3.0.0": + version: 3.0.5 + resolution: "minipass-fetch@npm:3.0.5" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^2.1.2" + dependenciesMeta: + encoding: + optional: true + checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 + languageName: node + linkType: hard + +"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": + version: 0.5.3 + resolution: "mkdirp-classic@npm:0.5.3" + checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf + languageName: node + linkType: hard + "mnemonist@npm:0.39.6": version: 0.39.6 resolution: "mnemonist@npm:0.39.6" @@ -934,6 +1666,13 @@ __metadata: languageName: node linkType: hard +"napi-build-utils@npm:^1.0.1": + version: 1.0.2 + resolution: "napi-build-utils@npm:1.0.2" + checksum: 10c0/37fd2cd0ff2ad20073ce78d83fd718a740d568b225924e753ae51cb69d68f330c80544d487e5e5bd18e28702ed2ca469c2424ad948becd1862c1b0209542b2e9 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -941,6 +1680,73 @@ __metadata: languageName: node linkType: hard +"negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + languageName: node + linkType: hard + +"node-abi@npm:^3.3.0": + version: 3.65.0 + resolution: "node-abi@npm:3.65.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/112672015d8f27d6be2f18d64569f28f5d6a15a94cc510da513c69c3e3ab5df6dac196ef13ff115a8fadb69b554974c47ef89b4f6350a2b02de2bca5c23db1e5 + languageName: node + linkType: hard + +"node-addon-api@npm:^8.0.0": + version: 8.0.0 + resolution: "node-addon-api@npm:8.0.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/20eb231362cc07c62d9839164473744d985be5d82685214f3750d990d9f61ef366e0ba112a766c925d640ed29b2a500b83568e895dc2444dcd5db01e615aac2b + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.8.0": + version: 4.8.1 + resolution: "node-gyp-build@npm:4.8.1" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10c0/e36ca3d2adf2b9cca316695d7687207c19ac6ed326d6d7c68d7112cebe0de4f82d6733dff139132539fcc01cf5761f6c9082a21864ab9172edf84282bc849ce7 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 10.1.0 + resolution: "node-gyp@npm:10.1.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + glob: "npm:^10.3.10" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^13.0.0" + nopt: "npm:^7.0.0" + proc-log: "npm:^3.0.0" + semver: "npm:^7.3.5" + tar: "npm:^6.1.2" + which: "npm:^4.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c + languageName: node + linkType: hard + +"nopt@npm:^7.0.0": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 + languageName: node + linkType: hard + "obliterator@npm:^2.0.1": version: 2.0.4 resolution: "obliterator@npm:2.0.4" @@ -955,6 +1761,15 @@ __metadata: languageName: node linkType: hard +"once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -987,6 +1802,22 @@ __metadata: languageName: node linkType: hard +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: "npm:^3.0.0" + checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -1010,6 +1841,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + "pino-abstract-transport@npm:^1.2.0": version: 1.2.0 resolution: "pino-abstract-transport@npm:1.2.0" @@ -1048,6 +1889,28 @@ __metadata: languageName: node linkType: hard +"prebuild-install@npm:^7.1.1": + version: 7.1.2 + resolution: "prebuild-install@npm:7.1.2" + dependencies: + detect-libc: "npm:^2.0.0" + expand-template: "npm:^2.0.3" + github-from-package: "npm:0.0.0" + minimist: "npm:^1.2.3" + mkdirp-classic: "npm:^0.5.3" + napi-build-utils: "npm:^1.0.1" + node-abi: "npm:^3.3.0" + pump: "npm:^3.0.0" + rc: "npm:^1.2.7" + simple-get: "npm:^4.0.0" + tar-fs: "npm:^2.0.0" + tunnel-agent: "npm:^0.6.0" + bin: + prebuild-install: bin.js + checksum: 10c0/e64868ba9ef2068fd7264f5b03e5298a901e02a450acdb1f56258d88c09dea601eefdb3d1dfdff8513fdd230a92961712be0676192626a3b4d01ba154d48bdd3 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -1064,6 +1927,20 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc + languageName: node + linkType: hard + +"proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9 + languageName: node + linkType: hard + "process-warning@npm:^3.0.0": version: 3.0.0 resolution: "process-warning@npm:3.0.0" @@ -1078,6 +1955,16 @@ __metadata: languageName: node linkType: hard +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 + languageName: node + linkType: hard + "proxy-addr@npm:^2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -1088,6 +1975,16 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -1109,6 +2006,31 @@ __metadata: languageName: node linkType: hard +"rc@npm:^1.2.7": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 + languageName: node + linkType: hard + +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 + languageName: node + linkType: hard + "readable-stream@npm:^4.0.0": version: 4.5.2 resolution: "readable-stream@npm:4.5.2" @@ -1150,6 +2072,13 @@ __metadata: languageName: node linkType: hard +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + "reusify@npm:^1.0.4": version: 1.0.4 resolution: "reusify@npm:1.0.4" @@ -1173,7 +2102,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.2.0": +"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 @@ -1196,6 +2125,13 @@ __metadata: languageName: node linkType: hard +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + "secure-json-parse@npm:^2.7.0": version: 2.7.0 resolution: "secure-json-parse@npm:2.7.0" @@ -1203,7 +2139,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.4": +"semver@npm:^7.3.5, semver@npm:^7.5.4": version: 7.6.2 resolution: "semver@npm:7.6.2" bin: @@ -1235,6 +2171,59 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"simple-concat@npm:^1.0.0": + version: 1.0.1 + resolution: "simple-concat@npm:1.0.1" + checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 + languageName: node + linkType: hard + +"simple-get@npm:^4.0.0": + version: 4.0.1 + resolution: "simple-get@npm:4.0.1" + dependencies: + decompress-response: "npm:^6.0.0" + once: "npm:^1.3.1" + simple-concat: "npm:^1.0.0" + checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.4 + resolution: "socks-proxy-agent@npm:8.0.4" + dependencies: + agent-base: "npm:^7.1.1" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/345593bb21b95b0508e63e703c84da11549f0a2657d6b4e3ee3612c312cb3a907eac10e53b23ede3557c6601d63252103494caa306b66560f43af7b98f53957a + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 + languageName: node + linkType: hard + "sonic-boom@npm:^4.0.1": version: 4.0.1 resolution: "sonic-boom@npm:4.0.1" @@ -1251,7 +2240,45 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.3.0": +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec + languageName: node + linkType: hard + +"ssri@npm:^10.0.0": + version: 10.0.6 + resolution: "ssri@npm:10.0.6" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" dependencies: @@ -1260,7 +2287,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -1269,6 +2296,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -1276,6 +2312,13 @@ __metadata: languageName: node linkType: hard +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -1285,6 +2328,45 @@ __metadata: languageName: node linkType: hard +"tar-fs@npm:^2.0.0": + version: 2.1.1 + resolution: "tar-fs@npm:2.1.1" + dependencies: + chownr: "npm:^1.1.1" + mkdirp-classic: "npm:^0.5.2" + pump: "npm:^3.0.0" + tar-stream: "npm:^2.1.4" + checksum: 10c0/871d26a934bfb7beeae4c4d8a09689f530b565f79bd0cf489823ff0efa3705da01278160da10bb006d1a793fa0425cf316cec029b32a9159eacbeaff4965fb6d + languageName: node + linkType: hard + +"tar-stream@npm:^2.1.4": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: "npm:^4.0.3" + end-of-stream: "npm:^1.4.1" + fs-constants: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692 + languageName: node + linkType: hard + +"tar@npm:^6.1.11, tar@npm:^6.1.2": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -1308,6 +2390,15 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -1317,6 +2408,51 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.5.2": + version: 5.5.2 + resolution: "typescript@npm:5.5.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/8ca39b27b5f9bd7f32db795045933ab5247897660627251e8254180b792a395bf061ea7231947d5d7ffa5cb4cc771970fd4ef543275f9b559f08c9325cccfce3 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.5.2#optional!builtin": + version: 5.5.2 + resolution: "typescript@patch:typescript@npm%3A5.5.2#optional!builtin::version=5.5.2&hash=b45daf" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6721ac8933a70c252d7b640b345792e103d881811ff660355617c1836526dbb71c2044e2e77a8823fb3570b469f33276875a4cab6d3c4de4ae7d7ee1c3074ae4 + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" + dependencies: + unique-slug: "npm:^4.0.0" + checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f + languageName: node + linkType: hard + +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2, uri-js@npm:^4.4.1": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -1326,6 +2462,13 @@ __metadata: languageName: node linkType: hard +"util-deprecate@npm:^1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -1337,6 +2480,17 @@ __metadata: languageName: node linkType: hard +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a + languageName: node + linkType: hard + "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" @@ -1344,6 +2498,42 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" diff --git a/identity-web/src/lib/api.ts b/identity-web/src/lib/api.ts index 131af7e..3d47c8a 100644 --- a/identity-web/src/lib/api.ts +++ b/identity-web/src/lib/api.ts @@ -14,7 +14,14 @@ export type Credentials = { token: string; }; +export type InsertHeir = { + contactMethod: 'email'; + name: string; + value: string; +}; + export type AccountHeir = { + id: string; contactMethod: 'email'; name: string; value: string; @@ -22,8 +29,8 @@ export type AccountHeir = { export type Account = { uid: string; + email: string; name: string; - heirs: AccountHeir[]; }; function sendRequest( @@ -119,14 +126,21 @@ export async function deleteEntry(credentials: Credentials, entry_id: string): P await sendRequest('/entry', credentials, { method: 'DELETE' }, `?entry_id=${entry_id}`); } -export async function updateHeirs(credentials: Credentials, heirs: AccountHeir[]): Promise { - await sendRequest('/auth/heirs', credentials, { - method: 'POST', +export async function insertHeir(credentials: Credentials, heirs: InsertHeir): Promise { + return await asJson(sendRequest('/auth/heirs', credentials, { + method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(heirs) - }); + })); +} + +export async function removeHeir(credentials: Credentials, heirID: string): Promise { + return await asJson(sendRequest('/auth/heirs', credentials, { + method: 'DELETE', + body: heirID, + })); } export async function uploadAsset(session_key: string, file: File): Promise { diff --git a/identity-web/src/routes/auth/account/+page.svelte b/identity-web/src/routes/auth/account/+page.svelte index a808714..7b4e6fc 100644 --- a/identity-web/src/routes/auth/account/+page.svelte +++ b/identity-web/src/routes/auth/account/+page.svelte @@ -5,7 +5,7 @@