diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6554c1d9..a31acb19 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,26 +1,26 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu { - "name": "Ubuntu", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/base:jammy", - "features": { - "ghcr.io/devcontainers-contrib/features/node-asdf:0": {}, - "ghcr.io/devcontainers/features/docker-in-docker:2": {} - } + "name": "AdventureLog", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/base:jammy", + "features": { + "ghcr.io/devcontainers-contrib/features/node-asdf:0": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + } - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", - // Configure tool-specific properties. - // "customizations": {}, + // Configure tool-specific properties. + // "customizations": {}, - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" } diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..4cc714a2 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DATABASE_URL= \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4934d0e0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to AdventureLog + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Please make sure you create an issue first for your change so you can link any pull requests to this issue. There should be a clear relationship between pull requests and issues. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 009925d5..13920f81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AdventureLog: Embark, Explore, Remember. 🌍 -_**⚠️ AdvenutreLog is in early development and is not at all deployable in the current form!**_ +_**⚠️ AdventureLog is in early development and is not recommended for production use until version 1.0!**_ ### *"Never forget an adventure with AdventureLog - Your ultimate travel companion!"* ----- ## Installation diff --git a/docker-compose.yml b/docker-compose.yml index 2f5ee369..04e8cd22 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: - "3000:3000" environment: - DATABASE_URL=postgres://adventurelog:PO24VjITwGgk@db:5432/adventurelog + # ORIGIN is only necessary when not using a reverse proxy or hosting that includes https + - ORIGIN=http://localhost:3000 depends_on: - db db: diff --git a/migrations/0004_smart_maelstrom.sql b/migrations/0004_smart_maelstrom.sql new file mode 100644 index 00000000..5982edc5 --- /dev/null +++ b/migrations/0004_smart_maelstrom.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS "session" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "expires_at" timestamp with time zone NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "user" ( + "id" text PRIMARY KEY NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/migrations/0005_glamorous_pixie.sql b/migrations/0005_glamorous_pixie.sql new file mode 100644 index 00000000..c50335dc --- /dev/null +++ b/migrations/0005_glamorous_pixie.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" ADD COLUMN "username" text NOT NULL;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "hashed_password" text NOT NULL; \ No newline at end of file diff --git a/migrations/0006_melted_leech.sql b/migrations/0006_melted_leech.sql new file mode 100644 index 00000000..202d31bc --- /dev/null +++ b/migrations/0006_melted_leech.sql @@ -0,0 +1 @@ +ALTER TABLE "user" ALTER COLUMN "hashed_password" SET DATA TYPE varchar; \ No newline at end of file diff --git a/migrations/0007_nervous_scalphunter.sql b/migrations/0007_nervous_scalphunter.sql new file mode 100644 index 00000000..baf412d4 --- /dev/null +++ b/migrations/0007_nervous_scalphunter.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" ADD COLUMN "first_name" text NOT NULL;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "last_name" text NOT NULL; \ No newline at end of file diff --git a/migrations/0008_romantic_maria_hill.sql b/migrations/0008_romantic_maria_hill.sql new file mode 100644 index 00000000..da684568 --- /dev/null +++ b/migrations/0008_romantic_maria_hill.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS "userVisitedAdventures" ( + "user_id" text NOT NULL, + "adventure_name" text NOT NULL, + "location" text, + "adventure_visited" text +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "userVisitedAdventures" ADD CONSTRAINT "userVisitedAdventures_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/migrations/0009_spotty_madame_web.sql b/migrations/0009_spotty_madame_web.sql new file mode 100644 index 00000000..900b8d7b --- /dev/null +++ b/migrations/0009_spotty_madame_web.sql @@ -0,0 +1,5 @@ +ALTER TABLE "userVisitedAdventures" RENAME COLUMN "adventure_visited" TO "adventure_id";--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ADD PRIMARY KEY ("adventure_id");--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ALTER COLUMN "adventure_id" SET DATA TYPE serial;--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ALTER COLUMN "adventure_id" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ADD COLUMN "visited_date" text; \ No newline at end of file diff --git a/migrations/meta/0004_snapshot.json b/migrations/meta/0004_snapshot.json new file mode 100644 index 00000000..9d59b974 --- /dev/null +++ b/migrations/meta/0004_snapshot.json @@ -0,0 +1,123 @@ +{ + "id": "ccf7c336-c61f-452f-822b-b3b039bb20f9", + "prevId": "45d98527-f0a9-44fc-9658-d3c461afed95", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0005_snapshot.json b/migrations/meta/0005_snapshot.json new file mode 100644 index 00000000..116f836a --- /dev/null +++ b/migrations/meta/0005_snapshot.json @@ -0,0 +1,135 @@ +{ + "id": "e91dda33-e04e-4e99-a297-21a34aa35493", + "prevId": "ccf7c336-c61f-452f-822b-b3b039bb20f9", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0006_snapshot.json b/migrations/meta/0006_snapshot.json new file mode 100644 index 00000000..2b36a768 --- /dev/null +++ b/migrations/meta/0006_snapshot.json @@ -0,0 +1,135 @@ +{ + "id": "b0849b3e-02e1-42e1-b07c-6fa613c98e82", + "prevId": "e91dda33-e04e-4e99-a297-21a34aa35493", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0007_snapshot.json b/migrations/meta/0007_snapshot.json new file mode 100644 index 00000000..e4364756 --- /dev/null +++ b/migrations/meta/0007_snapshot.json @@ -0,0 +1,147 @@ +{ + "id": "2039600b-1f5f-4f37-84dd-bb40636855e7", + "prevId": "b0849b3e-02e1-42e1-b07c-6fa613c98e82", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0008_snapshot.json b/migrations/meta/0008_snapshot.json new file mode 100644 index 00000000..1580c9e0 --- /dev/null +++ b/migrations/meta/0008_snapshot.json @@ -0,0 +1,195 @@ +{ + "id": "ec5af5e4-8522-4383-af47-412fb43c7cc3", + "prevId": "2039600b-1f5f-4f37-84dd-bb40636855e7", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "userVisitedAdventures": { + "name": "userVisitedAdventures", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adventure_name": { + "name": "adventure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adventure_visited": { + "name": "adventure_visited", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "userVisitedAdventures_user_id_user_id_fk": { + "name": "userVisitedAdventures_user_id_user_id_fk", + "tableFrom": "userVisitedAdventures", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0009_snapshot.json b/migrations/meta/0009_snapshot.json new file mode 100644 index 00000000..46e26155 --- /dev/null +++ b/migrations/meta/0009_snapshot.json @@ -0,0 +1,201 @@ +{ + "id": "1d83805d-6ee2-4dae-87a6-5af6f2d5add3", + "prevId": "ec5af5e4-8522-4383-af47-412fb43c7cc3", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "userVisitedAdventures": { + "name": "userVisitedAdventures", + "schema": "", + "columns": { + "adventure_id": { + "name": "adventure_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adventure_name": { + "name": "adventure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visited_date": { + "name": "visited_date", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "userVisitedAdventures_user_id_user_id_fk": { + "name": "userVisitedAdventures_user_id_user_id_fk", + "tableFrom": "userVisitedAdventures", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 5af81129..226286d5 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -29,6 +29,48 @@ "when": 1712083977580, "tag": "0003_clammy_goblin_queen", "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1712103855532, + "tag": "0004_smart_maelstrom", + "breakpoints": true + }, + { + "idx": 5, + "version": "5", + "when": 1712104331399, + "tag": "0005_glamorous_pixie", + "breakpoints": true + }, + { + "idx": 6, + "version": "5", + "when": 1712105206127, + "tag": "0006_melted_leech", + "breakpoints": true + }, + { + "idx": 7, + "version": "5", + "when": 1712167204757, + "tag": "0007_nervous_scalphunter", + "breakpoints": true + }, + { + "idx": 8, + "version": "5", + "when": 1712186591227, + "tag": "0008_romantic_maria_hill", + "breakpoints": true + }, + { + "idx": 9, + "version": "5", + "when": 1712407140727, + "tag": "0009_spotty_madame_web", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e0848784..caa47072 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,9 @@ "name": "adventurelog", "version": "0.0.1", "dependencies": { + "@lucia-auth/adapter-drizzle": "^1.0.7", "drizzle-orm": "^0.30.6", + "oslo": "^1.2.0", "postgres": "^3.4.4" }, "devDependencies": { @@ -18,11 +20,13 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/typography": "^0.5.12", + "@types/pg": "^8.11.4", "autoprefixer": "^10.4.19", "daisyui": "^4.9.0", "dotenv": "^16.4.5", "drizzle-kit": "^0.20.14", - "pg": "^8.11.4", + "lucia": "^3.1.1", + "pg": "^8.11.5", "postcss": "^8.4.38", "svelte": "^4.2.7", "svelte-check": "^3.6.0", @@ -66,6 +70,24 @@ "superjson": "^2.2.1" } }, + "node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -908,6 +930,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lucia-auth/adapter-drizzle": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@lucia-auth/adapter-drizzle/-/adapter-drizzle-1.0.7.tgz", + "integrity": "sha512-X/V7fLBca8EC/gPXCntwbQpb0+F9oEuRoHElvsi9rCrdnGhCMNxHgwAvgiQ6pes+rIYpyvx4n3hvjqo/fPo03A==", + "peerDependencies": { + "lucia": "3.x" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -943,6 +973,513 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.2.tgz", + "integrity": "sha512-8JuczewTFIZ/XIjHQ+YlQUydHvlKx2hkcxtuGwh+t/t5zWyZct6YG4+xjHcq8xyc/e7FmFwf42Zj2YgICwmlvA==", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.1.0", + "@emnapi/runtime": "^1.1.0", + "@tybys/wasm-util": "^0.8.1" + } + }, + "node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.1.1.tgz", + "integrity": "sha512-eu4KjHfXg3I+UUR7vSuwZXpRo4c8h4Rtb5Lu2F7Z4JqJFl/eidquONEBiRs6viXKpWBC3BaJBy68xGJ2j56idw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", + "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/argon2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", + "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.0", + "@node-rs/argon2-android-arm64": "1.7.0", + "@node-rs/argon2-darwin-arm64": "1.7.0", + "@node-rs/argon2-darwin-x64": "1.7.0", + "@node-rs/argon2-freebsd-x64": "1.7.0", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", + "@node-rs/argon2-linux-arm64-gnu": "1.7.0", + "@node-rs/argon2-linux-arm64-musl": "1.7.0", + "@node-rs/argon2-linux-x64-gnu": "1.7.0", + "@node-rs/argon2-linux-x64-musl": "1.7.0", + "@node-rs/argon2-wasm32-wasi": "1.7.0", + "@node-rs/argon2-win32-arm64-msvc": "1.7.0", + "@node-rs/argon2-win32-ia32-msvc": "1.7.0", + "@node-rs/argon2-win32-x64-msvc": "1.7.0" + } + }, + "node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", + "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", + "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", + "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", + "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", + "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", + "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.9.0", + "@node-rs/bcrypt-android-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-x64": "1.9.0", + "@node-rs/bcrypt-freebsd-x64": "1.9.0", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-x64-musl": "1.9.0", + "@node-rs/bcrypt-wasm32-wasi": "1.9.0", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" + } + }, + "node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", + "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", + "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", + "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1887,6 +2424,15 @@ "node": ">=4" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.1.tgz", + "integrity": "sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1899,6 +2445,83 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/node": { + "version": "20.12.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz", + "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==", + "devOptional": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pg": { + "version": "8.11.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.4.tgz", + "integrity": "sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==", + "devOptional": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "devOptional": true, + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "devOptional": true, + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@types/pug": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", @@ -3548,6 +4171,12 @@ "node": ">=8" } }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "optional": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4028,6 +4657,495 @@ "es5-ext": "~0.10.2" } }, + "node_modules/lucia": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lucia/-/lucia-3.1.1.tgz", + "integrity": "sha512-Ygvgnqq7Ha7lYVaZATPwkPD2s2Qlsm71Z2o0byx/abNBfFldCRow5sNii6RqMsuMpK957RAI3Gw4/aWoagkc7A==", + "dependencies": { + "oslo": "1.0.1" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.2.tgz", + "integrity": "sha512-+H6pc3M1vIX9YnG59YW7prHhhpv19P8YyxlXHnnFzTimf2q+kKDF7mGWbhvN9STqIY+P70Patn0Q6qb6Ib5/4g==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.2", + "@node-rs/argon2-android-arm64": "1.7.2", + "@node-rs/argon2-darwin-arm64": "1.7.2", + "@node-rs/argon2-darwin-x64": "1.7.2", + "@node-rs/argon2-freebsd-x64": "1.7.2", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.2", + "@node-rs/argon2-linux-arm64-gnu": "1.7.2", + "@node-rs/argon2-linux-arm64-musl": "1.7.2", + "@node-rs/argon2-linux-x64-gnu": "1.7.2", + "@node-rs/argon2-linux-x64-musl": "1.7.2", + "@node-rs/argon2-wasm32-wasi": "1.7.2", + "@node-rs/argon2-win32-arm64-msvc": "1.7.2", + "@node-rs/argon2-win32-ia32-msvc": "1.7.2", + "@node-rs/argon2-win32-x64-msvc": "1.7.2" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.2.tgz", + "integrity": "sha512-WhW84XOzdR4AOGc4BJvIg5lCRVBL0pXp/PPCe8QCyWw493p7VdNCdYpr2xdtjS/0zImmY85HNB/6zpzjLRTT/A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.2.tgz", + "integrity": "sha512-CdtayHSMIyDuVhSYFirwA757c4foQuyTjpysgFJLHweP9C7uDiBf9WBYij+UyabpaCadJ0wPyK6Vakinvlk4/g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.2.tgz", + "integrity": "sha512-hUOhtgYHTEyzX5sgMZVdXunONOus2HWpWydF5D/RYJ1mZ76FXRnFpQE40DqbzisdPIraKdn40m7JqkPP7wqdyg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.2.tgz", + "integrity": "sha512-lfs5HX+t542yUfcv6Aa/NeGD1nUCwyQNgnPEGcik71Ow6V13hkR1bHgmT1u3CHN4fBts0gW+DQEDsq1xlVgkvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.2.tgz", + "integrity": "sha512-ROoF+4VaCBJUjddrTN1hjuqSl89ppRcjVXJscSPJjWzTlbzFmGGovJvIzUBmCr/Oq3yM1zKHj6MP9oRD5cB+/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.2.tgz", + "integrity": "sha512-CBSB8KPI8LS74Bcz3dYaa2/khULutz4vSDvFWUERlSLX+mPdDhoZi6UPuUPPF9e01w8AbiK1YCqlLUTm3tIMfw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.2.tgz", + "integrity": "sha512-6LBTug6ZiWFakP3X3Nqs7ZTM03gmcSWX4YvEn20HhhQE5NDrsrw3zNqGj0cJiNzKKIMSDDuj7uGy+ITEfNo4CA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.2.tgz", + "integrity": "sha512-KjhQ+ZPne29t9VRVeIif7JdKwQba+tM6CBNYBoJB1iON0CUKeqSQtZcHuTj9gkf2SNRG5bsU4ABcfxd0OKsKHg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.2.tgz", + "integrity": "sha512-BQvp+iLtKqomHz4q5t1aKoni9osgvUDU5sZtHAlFm5dRTlGHnympcQVATRE5GHyH9C6MIM9W7P1kqEeCLGPolQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.2.tgz", + "integrity": "sha512-yXJudpBZQ98g+lWaHn9EzZ5KsAyqRdlpub/K+5NP7gHehb8wzBRIFAejIHAG0fvzQEEc86VOnV2koWIVZxWAvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.2.tgz", + "integrity": "sha512-diXlVjJZY2GIV8ZDwUqXPhacXsFR0klGSv5D9f+XidwWXK4udtzDhkM/7N/Mb7h1HAWaxZ6IN9spYFjvWH1wqg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.2.tgz", + "integrity": "sha512-dhIBrY04P9nbmwzBpgERQDmmSu4YBZyeEE32t4TikMz5rQ07iaVC+JpGmtCBZoDIsLDHGC8cikENd3YEqpqIcA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.2.tgz", + "integrity": "sha512-o1tfqr8gyALCzuxBoQfvhxkeYMaw/0H8Gmt7klTYyEIBvEFu7SD5qytXO9Px7t5420nZL/Wy5cflg3IB1s57Pg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.2.tgz", + "integrity": "sha512-v0h53XUc7hNgWiWi0qcMcHvj9/kwuItI9NwLK4C+gtzT3UB0cedhfIL8HFMKThMXasy41ZdbpCF2Bi0kJoLNEg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.2.tgz", + "integrity": "sha512-FKUo9iCSIti+ldwoOlY1ztyIFhZxEgT7jZ/UCt/9bg1rLmNdbQQD2JKIMImDCqmTWuLPY4ZF4Q5MyOMIfDCd8Q==", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.9.2", + "@node-rs/bcrypt-android-arm64": "1.9.2", + "@node-rs/bcrypt-darwin-arm64": "1.9.2", + "@node-rs/bcrypt-darwin-x64": "1.9.2", + "@node-rs/bcrypt-freebsd-x64": "1.9.2", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.2", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.2", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.2", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.2", + "@node-rs/bcrypt-linux-x64-musl": "1.9.2", + "@node-rs/bcrypt-wasm32-wasi": "1.9.2", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.2", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.2", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.2" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.2.tgz", + "integrity": "sha512-er/Q2khwpan9pczvTTqY/DJE4UU65u31xd0NkZlHUTKyB7djRhWfzoGexGx2GN+k831/RR3U8kKE/8QUHeO3hQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.2.tgz", + "integrity": "sha512-OUYatOEG5vbLbF73q2TC8UqrDO81zUQxnaFD/OAB1hcm6J+ur0zJ8E53c35/DIqkTp7JarPMraC4rouJ2ugN4w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.2.tgz", + "integrity": "sha512-svJKsGbzMAxOB5oluOYneN4YkKUy26WSMgm3KOIhgoX30IeMilj+2jFN/5qrI0oDZ0Iczb3XyL5DuZFtEkdP8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.2.tgz", + "integrity": "sha512-9OrySjBi/rWix8NZWD/TrNbNcwMY0pAiMHdL09aJnJ07uPih83GGh1pq4UHCYFCMy7iTX8swOmDlGBUImkOZbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.2.tgz", + "integrity": "sha512-/djXV71RO6g5L1mI2pVvmp3x3pH7G4uKI3ODG1JBIXoz334oOcCMh40sB0uq0ljP8WEadker01p4T1rJE98fpg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.2.tgz", + "integrity": "sha512-F7wP950OTAooxEleUN4I2hqryGZK7hi1cSgRF13Wvbc597RFux35KiSxIXUA3mNt2DE7lV2PeceEtCOScaThWQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.2.tgz", + "integrity": "sha512-MehG+yQ0TgKMgKR1rO4hdvHkVsTM91Cof8qI9EJlS5+7+QSwfFA5O0zGwCkISD7bsyauJ5uJgcByGjpEobAHOg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-PRZTAJjOwKEGsIhmBvfNh81So+wGl4QyCFAt23j+KwBujLStjC0N3YaqtTlWVKG9tcriPtmMYiAQtXWIyIgg/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.2.tgz", + "integrity": "sha512-5WfGO+O1m7nJ55WZ8XDq+ItA98Z4O7sNWsR+1nIj9YGT+Tx5zkQ2RBhpK6oCWZMluuZ0eKQ0FDmyP6K+2NDRIA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-VjCn0388p6PMCVUYHgYmHZrKNc7WwNJRr2WLJsHbQRGDOKbpNL6YolCjQxUchcSPDhzwrq1cIdy4j0fpoXEsdw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.2.tgz", + "integrity": "sha512-P06aHfMzm9makwU+nM7WA65yQnS1xuqJ8l/6I/LvXjnl+lfB3DtJ2B0CSLtjnUGpUgcHbWl5gEbNnTPxSAirjQ==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.2.tgz", + "integrity": "sha512-Iyo/Q5/eNw27VRd3mLBgh1b9b5fnT3QHTVwxv3Siv/MRAIfJXH/cTOe18qSwYQzNh0ZioW4yemFPYCWSZi7szA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.2.tgz", + "integrity": "sha512-6LHWMaPylyyHoS5863YpxAACVB8DWCxro5W6pQ4h8WKSgHpJp8Um9jphTdN0A2w45HZjUnfcFuiFFC+TbftjCw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.2.tgz", + "integrity": "sha512-vZ9T1MOaYkLO9FTyl28YX0SYJneiYTKNFgM8PUv8nas8xrD+7OzokA0fEtlNp6413T7IKSD/iG9qi8nTWsiyGg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/oslo": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.0.1.tgz", + "integrity": "sha512-esfzZry+HfGgK/GCYkg7BRlLd3RH5aHa08wgLJPYjENXybi0BvXxGk0LbUj+lXfz2TkjPDHe4rB/o6JxRLHxBg==", + "dependencies": { + "@node-rs/argon2": "1.7.2", + "@node-rs/bcrypt": "1.9.2" + } + }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", @@ -4070,6 +5188,27 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "optional": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memfs-browser": { + "version": "3.5.10302", + "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", + "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", + "optional": true, + "dependencies": { + "memfs": "3.5.3" + } + }, "node_modules/memoizee": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", @@ -4343,6 +5482,12 @@ "node": ">= 6" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "devOptional": true + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4352,6 +5497,15 @@ "wrappy": "1" } }, + "node_modules/oslo": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.0.tgz", + "integrity": "sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==", + "dependencies": { + "@node-rs/argon2": "1.7.0", + "@node-rs/bcrypt": "1.9.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4416,12 +5570,12 @@ } }, "node_modules/pg": { - "version": "8.11.4", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.4.tgz", - "integrity": "sha512-pWb7JKPxGk1UFbtq7jQ0m3IfPpb7LLACCEyN8/u9DYEom+Q/BSKy+4TRl4+Hh003AOYhppB/z+QK87/hx/bk0w==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "devOptional": true, "dependencies": { - "pg-connection-string": "^2.6.3", + "pg-connection-string": "^2.6.4", "pg-pool": "^3.6.2", "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", @@ -4450,9 +5604,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.3.tgz", - "integrity": "sha512-77FxhhKJQH+xJx6tDqkhhMa0nZvv3U1HYLDQgwZxZafVD583++O5LXn5oo5HaQZ0vXwYcZA1koYAJM3JvD6Gtw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", "devOptional": true }, "node_modules/pg-int8": { @@ -4464,6 +5618,15 @@ "node": ">=4.0.0" } }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "devOptional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/pg-pool": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", @@ -4740,6 +5903,12 @@ "node": ">=0.10.0" } }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "devOptional": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5593,7 +6762,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "devOptional": true }, "node_modules/type": { "version": "2.7.2", @@ -5614,6 +6783,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 9613ca69..6b54d1a3 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/typography": "^0.5.12", + "@types/pg": "^8.11.4", "autoprefixer": "^10.4.19", "daisyui": "^4.9.0", "dotenv": "^16.4.5", "drizzle-kit": "^0.20.14", - "pg": "^8.11.4", + "lucia": "^3.1.1", + "pg": "^8.11.5", "postcss": "^8.4.38", "svelte": "^4.2.7", "svelte-check": "^3.6.0", @@ -36,7 +38,9 @@ }, "type": "module", "dependencies": { + "@lucia-auth/adapter-drizzle": "^1.0.7", "drizzle-orm": "^0.30.6", + "oslo": "^1.2.0", "postgres": "^3.4.4" } } diff --git a/src/app.d.ts b/src/app.d.ts index ede601ab..65be1f93 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,12 +1,9 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces declare global { namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} + interface Locals { + user: import("lucia").User | null; + session: import("lucia").Session | null; + } } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 00000000..4b48cbc7 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,32 @@ +import { lucia } from "$lib/server/auth"; +import type { Handle } from "@sveltejs/kit"; + +export const handle: Handle = async ({ event, resolve }) => { + const sessionId = event.cookies.get(lucia.sessionCookieName); + if (!sessionId) { + event.locals.user = null; + event.locals.session = null; + return resolve(event); + } + + const { session, user } = await lucia.validateSession(sessionId); + if (session && session.fresh) { + const sessionCookie = lucia.createSessionCookie(session.id); + // sveltekit types deviates from the de-facto standard + // you can use 'as any' too + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + } + if (!session) { + const sessionCookie = lucia.createBlankSessionCookie(); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + } + event.locals.user = user; + event.locals.session = session; + return resolve(event); +}; diff --git a/src/lib/assets/info.svg b/src/lib/assets/info.svg new file mode 100644 index 00000000..255123c9 --- /dev/null +++ b/src/lib/assets/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/lib/components/InfoModal.svelte b/src/lib/components/InfoModal.svelte new file mode 100644 index 00000000..abcae569 --- /dev/null +++ b/src/lib/components/InfoModal.svelte @@ -0,0 +1,60 @@ + + + + + + + diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 3a47ee9e..54960eeb 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,7 +1,13 @@ @@ -34,10 +63,11 @@ class="btn btn-primary my-2 md:my-0 md:mr-4 md:ml-2" on:click={goHome}>Home - + {#if user} + + {/if} @@ -45,7 +75,20 @@ + + {#if infoModalOpen} + + {/if} diff --git a/src/lib/components/UserAvatar.svelte b/src/lib/components/UserAvatar.svelte new file mode 100644 index 00000000..0df60ef3 --- /dev/null +++ b/src/lib/components/UserAvatar.svelte @@ -0,0 +1,36 @@ + + + diff --git a/src/lib/config.ts b/src/lib/config.ts new file mode 100644 index 00000000..15e14b9a --- /dev/null +++ b/src/lib/config.ts @@ -0,0 +1,3 @@ +export let appVersion = "Web 0.0.1"; +export let appTitle = "AdventureLog"; +export let copyrightYear = "2024" \ No newline at end of file diff --git a/src/lib/db/db.server.ts b/src/lib/db/db.server.ts index 9288f103..f4fbc866 100644 --- a/src/lib/db/db.server.ts +++ b/src/lib/db/db.server.ts @@ -1,8 +1,9 @@ import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import dotenv from "dotenv"; +import * as schema from "$lib/db/schema"; dotenv.config(); const { DATABASE_URL } = process.env; -const client = postgres(DATABASE_URL); -export const db = drizzle(client, {}); +const client = postgres(DATABASE_URL || ""); // Pass DATABASE_URL as a string argument +export const db = drizzle(client, { schema }); diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index f2890861..2800cadc 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -1,4 +1,11 @@ -import { pgTable, json, text, serial } from "drizzle-orm/pg-core"; +import { + pgTable, + text, + timestamp, + json, + serial, + varchar, +} from "drizzle-orm/pg-core"; export const featuredAdventures = pgTable("featuredAdventures", { id: serial("id").primaryKey(), @@ -10,3 +17,34 @@ export const sharedAdventures = pgTable("sharedAdventures", { id: text("id").primaryKey(), data: json("data").notNull(), }); + +export const userTable = pgTable("user", { + id: text("id").primaryKey(), + username: text("username").notNull(), + first_name: text("first_name").notNull(), + last_name: text("last_name").notNull(), + hashed_password: varchar("hashed_password").notNull(), +}); + +// export type SelectUser = typeof userTable.$inferSelect; + +export const sessionTable = pgTable("session", { + id: text("id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => userTable.id), + expiresAt: timestamp("expires_at", { + withTimezone: true, + mode: "date", + }).notNull(), +}); + +export const userVisitedAdventures = pgTable("userVisitedAdventures", { + adventureID: serial("adventure_id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => userTable.id), + adventureName: text("adventure_name").notNull(), + location: text("location"), + visitedDate: text("visited_date"), +}); diff --git a/src/lib/index.ts b/src/lib/index.ts index c1e2c9f5..44323dc5 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -10,3 +10,77 @@ export function generateRandomString() { } return randomString; } + +const inspirationalQuotes = [ + "Believe you can and you're halfway there. - Theodore Roosevelt", + "The only way to do great work is to love what you do. - Steve Jobs", + "In the middle of every difficulty lies opportunity. - Albert Einstein", + "The future belongs to those who believe in the beauty of their dreams. - Eleanor Roosevelt", + "It does not matter how slowly you go as long as you do not stop. - Confucius", + "Success is not final, failure is not fatal: It is the courage to continue that counts. - Winston Churchill", + "The only limit to our realization of tomorrow will be our doubts of today. - Franklin D. Roosevelt", + "Don't watch the clock; do what it does. Keep going. - Sam Levenson", + "You are never too old to set another goal or to dream a new dream. - C.S. Lewis", + "The only person you are destined to become is the person you decide to be. - Ralph Waldo Emerson", + "Happiness is not something ready-made. It comes from your own actions. - Dalai Lama", + "Life is what happens when you're busy making other plans. - John Lennon", + "You miss 100% of the shots you don't take. - Wayne Gretzky", + "The best time to plant a tree was 20 years ago. The second best time is now. - Chinese Proverb", + "The only way to achieve the impossible is to believe it is possible. - Charles Kingsleigh", + "Don't count the days, make the days count. - Muhammad Ali", + "You don't have to be great to start, but you have to start to be great. - Zig Ziglar", + "You can't go back and change the beginning, but you can start where you are and change the ending. - C.S. Lewis", + "Dream big and dare to fail. - Norman Vaughan", + "The secret of getting ahead is getting started. - Mark Twain", + "Everything you can imagine is real. - Pablo Picasso", + "You must be the change you wish to see in the world. - Mahatma Gandhi", + "If you want to lift yourself up, lift up someone else. - Booker T. Washington", + "Believe in yourself and all that you are. Know that there is something inside you that is greater than any obstacle. - Christian D. Larson", + "The journey of a thousand miles begins with one step. - Lao Tzu", + "Life isn't about waiting for the storm to pass, it's about learning to dance in the rain. - Vivian Greene", + "You are never too old to set another goal or to dream a new dream. - Les Brown", + "Your time is limited, don't waste it living someone else's life. - Steve Jobs", + "Don't let yesterday take up too much of today. - Will Rogers", + "The only thing standing between you and your goal is the story you keep telling yourself as to why you can't achieve it. - Jordan Belfort", + "The future belongs to those who prepare for it today. - Malcolm X", + "The greatest glory in living lies not in never falling, but in rising every time we fall. - Nelson Mandela", + "It's not what happens to you, but how you react to it that matters. - Epictetus", + "The only way to do great work is to love what you do. - Steve Jobs", + "When one door of happiness closes, another opens, but often we look so long at the closed door that we do not see the one that has been opened for us. - Helen Keller", + "The only thing that stands between you and your dream is the will to try and the belief that it is actually possible. - Joel Brown", + "Success is walking from failure to failure with no loss of enthusiasm. - Winston Churchill", + "Believe in yourself! Have faith in your abilities! Without a humble but reasonable confidence in your own powers you cannot be successful or happy. - Norman Vincent Peale", + "The greatest adventure is what lies ahead. - J.R.R. Tolkien", + "The only way to do great work is to love what you do. - Steve Jobs", + "What you get by achieving your goals is not as important as what you become by achieving your goals. - Zig Ziglar", + "To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment. - Ralph Waldo Emerson", + "What lies behind us and what lies before us are tiny matters compared to what lies within us. - Ralph Waldo Emerson", + "The only person you are destined to become is the person you decide to be. - Ralph Waldo Emerson", + "The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart. - Helen Keller", + "The only limit to our realization of tomorrow will be our doubts of today. - Franklin D. Roosevelt", + "It always seems impossible until it is done. - Nelson Mandela", + "I can't change the direction of the wind, but I can adjust my sails to always reach my destination. - Jimmy Dean", + "Believe you can and you're halfway there. - Theodore Roosevelt", + "The only way to achieve the impossible is to believe it is possible. - Charles Kingsleigh", + "If you're going through hell, keep going. - Winston Churchill", + "Nothing is impossible, the word itself says 'I'm possible'! - Audrey Hepburn", + "The only thing standing in the way between you and your goal is the story you keep telling yourself as to why you can't achieve it. - Jordan Belfort", + "The future belongs to those who believe in the beauty of their dreams. - Eleanor Roosevelt", + "Success is not final, failure is not fatal: It is the courage to continue that counts. - Winston Churchill", + "Keep your face always toward the sunshine - and shadows will fall behind you. - Walt Whitman", + "Success is not the key to happiness. Happiness is the key to success. If you love what you are doing, you will be successful. - Albert Schweitzer", + "Don't watch the clock; do what it does. Keep going. - Sam Levenson", + "You are never too old to set another goal or to dream a new dream. - C.S. Lewis", + "You are never too old to set another goal or to dream a new dream. - C.S. Lewis", + "The only person you are destined to become is the person you decide to be. - Ralph Waldo Emerson", + "Happiness is not something ready-made. It comes from your own actions. - Dalai Lama", + "Life is what happens when you're busy making other plans. - John Lennon", + "You miss 100% of the shots you don't take. - Wayne Gretzky", + "The best time to plant a tree was 20 years ago. The second best time is now. - Chinese Proverb", + "The only way to achieve the impossible is to believe it is possible. - Charles Kings", +]; + +export function getRandomQuote() { + const randomIndex = Math.floor(Math.random() * inspirationalQuotes.length); + return inspirationalQuotes[randomIndex]; +} diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts new file mode 100644 index 00000000..ccb1616c --- /dev/null +++ b/src/lib/server/auth.ts @@ -0,0 +1,39 @@ +import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; +import { Lucia, TimeSpan } from "lucia"; +import { dev } from "$app/environment"; +import { userTable, sessionTable } from "$lib/db/schema"; +import { db } from "$lib/db/db.server"; + +const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, userTable); + +export const lucia = new Lucia(adapter, { + sessionCookie: { + attributes: { + secure: !dev, + }, + }, + getUserAttributes: (attributes) => { + return { + // attributes has the type of DatabaseUserAttributes + username: attributes.username, + id: attributes.id, + first_name: attributes.first_name, + last_name: attributes.last_name, + }; + }, +}); + +declare module "lucia" { + interface Register { + Lucia: typeof lucia; + DatabaseUserAttributes: DatabaseUser; + } +} + +export interface DatabaseUser { + id: string; + username: string; + first_name: string; + last_name: string; + hashed_password: string; +} diff --git a/src/lib/utils/stores/visitCountStore.ts b/src/lib/utils/stores/visitCountStore.ts index a61dbd52..973db8f1 100644 --- a/src/lib/utils/stores/visitCountStore.ts +++ b/src/lib/utils/stores/visitCountStore.ts @@ -1,3 +1,5 @@ import { writable } from "svelte/store"; -export const visitCount = writable(0); +let value = 0; +export const visitCount = writable(value); + diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 00000000..c79020bc --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,18 @@ + + +
+

{$page.error.message}

+
+ +
+ +
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts new file mode 100644 index 00000000..d843f2a8 --- /dev/null +++ b/src/routes/+layout.server.ts @@ -0,0 +1,12 @@ +import type { LayoutServerLoad, PageServerLoad } from "./$types"; + +export const load: LayoutServerLoad = async (event) => { + if (event.locals.user) { + return { + user: event.locals.user, + }; + } + return { + user: null, + }; +}; \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b262a24f..abcd0fc6 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,12 +1,13 @@ - - + +
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts new file mode 100644 index 00000000..48385754 --- /dev/null +++ b/src/routes/+page.server.ts @@ -0,0 +1,31 @@ +import { lucia } from "$lib/server/auth"; +import { fail, redirect } from "@sveltejs/kit"; + +import type { Actions, PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async (event) => { + if (event.locals.user) + return { + user: event.locals.user, + }; + return { + user: null, + }; +}; + +// handle the logout action +export const actions: Actions = { + default: async (event) => { + if (!event.locals.session) { + return fail(401); + } + + await lucia.invalidateSession(event.locals.session.id); + const sessionCookie = lucia.createBlankSessionCookie(); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + return redirect(302, "/login"); + }, +}; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d6ed621f..22a9617d 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,4 +1,8 @@
-
-

Welcome. Let's get Exploring!

-
+ {#if data.user && data.user.username != ""} +

+ Welcome {data.user.first_name}. Let's get Exploring! +

+ {:else} +

Welcome. Let's get Exploring!

+ {/if} + Logo diff --git a/src/routes/api/clearvisits/+server.ts b/src/routes/api/clearvisits/+server.ts new file mode 100644 index 00000000..64fed2f9 --- /dev/null +++ b/src/routes/api/clearvisits/+server.ts @@ -0,0 +1,27 @@ +import type { RequestEvent } from "@sveltejs/kit"; +import { db } from "$lib/db/db.server"; +import { eq } from "drizzle-orm"; +import { userVisitedAdventures } from "$lib/db/schema"; + +export async function DELETE(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + let res = await db + .delete(userVisitedAdventures) + .where( + eq(userVisitedAdventures.userId, event.locals.user.id), + ) + .execute(); + return new Response(JSON.stringify({ res: res }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + } \ No newline at end of file diff --git a/src/routes/api/userinfo/+server.ts b/src/routes/api/userinfo/+server.ts new file mode 100644 index 00000000..58fa8f3e --- /dev/null +++ b/src/routes/api/userinfo/+server.ts @@ -0,0 +1,39 @@ +import { lucia } from "$lib/server/auth"; +import type { RequestEvent } from "@sveltejs/kit"; + +export async function GET(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + try { + return new Response( + JSON.stringify({ + message: "Welcome user info page!", + userId: event.locals.user.id, + username: event.locals.user.username, + firstName: event.locals.user.first_name, + lastName: event.locals.user.last_name, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + } catch (e) { + console.error(e); + return new Response(JSON.stringify({ error: "Internal server error" }), { + status: 500, + headers: { + "Content-Type": "application/json", + }, + }); + } +} diff --git a/src/routes/api/visitcount/+server.ts b/src/routes/api/visitcount/+server.ts new file mode 100644 index 00000000..7d28582d --- /dev/null +++ b/src/routes/api/visitcount/+server.ts @@ -0,0 +1,34 @@ +import type { RequestEvent } from "@sveltejs/kit"; +import { count } from 'drizzle-orm'; +import { eq } from "drizzle-orm"; +import { userVisitedAdventures } from "$lib/db/schema"; +import { db } from "$lib/db/db.server"; + +export async function GET(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + // get the count of the number of adventures the user has visited + let result = await db + .select({ count: count() }) + .from(userVisitedAdventures) + .where(eq(userVisitedAdventures.userId,event.locals.user.id)) + .execute(); + + return new Response( + JSON.stringify({ + visitCount: result[0].count, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + } \ No newline at end of file diff --git a/src/routes/api/visits/+server.ts b/src/routes/api/visits/+server.ts new file mode 100644 index 00000000..72db3201 --- /dev/null +++ b/src/routes/api/visits/+server.ts @@ -0,0 +1,177 @@ +import { lucia } from "$lib/server/auth"; +import type { RequestEvent } from "@sveltejs/kit"; +import { userVisitedAdventures } from "$lib/db/schema"; +import { db } from "$lib/db/db.server"; +import { and, eq } from "drizzle-orm"; +import type { Adventure } from "$lib/utils/types"; + +// Gets all the adventures that the user has visited +export async function GET(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + let result = await db + .select() + .from(userVisitedAdventures) + .where(eq(userVisitedAdventures.userId, event.locals.user.id)) + .execute(); + return new Response( + JSON.stringify({ + adventures: result.map((item) => ({ + id: item.adventureID, + name: item.adventureName, + location: item.location, + created: item.visitedDate, + })), + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); +} + +// deletes the adventure given the adventure id and the user object +export async function DELETE(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + // get id from the body + const { id } = await event.request.json(); + + if (!id) { + return new Response(JSON.stringify({ error: "No id found" }), { + status: 400, + headers: { + "Content-Type": "application/json", + }, + }); + } + + let res = await db + .delete(userVisitedAdventures) + .where( + and( + eq(userVisitedAdventures.userId, event.locals.user.id), + eq(userVisitedAdventures.adventureID, Number(id)) + ) + ) + .execute(); + + return new Response(JSON.stringify({ id: id, res: res }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +} + +// add the adventure to the user's visited list +export async function POST(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + // get properties from the body + const { name, location, created } = await event.request.json(); + + // insert the adventure to the user's visited list + await db + .insert(userVisitedAdventures) + .values({ + userId: event.locals.user.id, + adventureName: name, + location: location, + visitedDate: created, + }) + .execute(); +let res = await db + .select() + .from(userVisitedAdventures) + .where( + and( + eq(userVisitedAdventures.userId, event.locals.user.id), + eq(userVisitedAdventures.adventureName, name), + eq(userVisitedAdventures.location, location), + eq(userVisitedAdventures.visitedDate, created) + ) + ) + .execute(); + +// return a response with the adventure object values + return new Response( + JSON.stringify({ + adventure: { name, location, created }, + message: { message: "Adventure added" }, + id: res[0].adventureID + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); +} + +// put route to update existing adventure +export async function PUT(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + // get properties from the body + const { id, name, location, created } = await event.request.json(); + + // update the adventure in the user's visited list + await db + .update(userVisitedAdventures) + .set({ + adventureName: name, + location: location, + visitedDate: created, + }) + .where( + and( + eq(userVisitedAdventures.userId, event.locals.user.id), + eq(userVisitedAdventures.adventureID, Number(id)) + ) + ) + .execute(); + + return new Response( + JSON.stringify({ + adventure: { id, name, location, created }, + message: { message: "Adventure updated" }, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); +} \ No newline at end of file diff --git a/src/routes/featured/+page.svelte b/src/routes/featured/+page.svelte index 93f0ad0e..f16c409a 100644 --- a/src/routes/featured/+page.svelte +++ b/src/routes/featured/+page.svelte @@ -1,19 +1,34 @@ diff --git a/src/routes/log/+page.server.ts b/src/routes/log/+page.server.ts new file mode 100644 index 00000000..fa7fff87 --- /dev/null +++ b/src/routes/log/+page.server.ts @@ -0,0 +1,15 @@ +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; +import type { Adventure } from "$lib/utils/types"; + +export const load: PageServerLoad = async (event) => { + if (!event.locals.user) { + return redirect(302, "/login"); + } + const response = await event.fetch("/api/visits"); + const result = await response.json(); + // let array = result.adventures as Adventure[]; + return { + result, + }; +}; diff --git a/src/routes/log/+page.svelte b/src/routes/log/+page.svelte index 5af4f817..3aa3989c 100644 --- a/src/routes/log/+page.svelte +++ b/src/routes/log/+page.svelte @@ -1,4 +1,7 @@ @@ -184,8 +268,8 @@ name={adventure.name} location={adventure.location} created={adventure.created} - on:remove={triggerRemoveAdventure} on:edit={editAdventure} + on:remove={removeAdventure} /> {/each}
diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 00000000..e5211ec8 --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,82 @@ +import { lucia } from "$lib/server/auth"; +import { fail, redirect } from "@sveltejs/kit"; +import { Argon2id } from "oslo/password"; +import { db } from "$lib/db/db.server"; + +import type { Actions, PageServerLoad } from "./$types"; +import type { DatabaseUser } from "$lib/server/auth"; +import { userTable } from "$lib/db/schema"; +import { eq } from "drizzle-orm"; + +export const load: PageServerLoad = async (event) => { + if (event.locals.user) { + return redirect(302, "/"); + } + return {}; +}; + +export const actions: Actions = { + default: async (event) => { + const formData = await event.request.formData(); + const username = formData.get("username"); + const password = formData.get("password"); + + if (!username || !password) { + return fail(400, { + message: "Invalid request", + }); + } + + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return fail(400, { + message: "Invalid username", + }); + } + if ( + typeof password !== "string" || + password.length < 6 || + password.length > 255 + ) { + return fail(400, { + message: "Invalid password", + }); + } + + const existingUser = await db + .select() + .from(userTable) + .where(eq(userTable.username, username)) + .limit(1) + .then((results) => results[0] as unknown as DatabaseUser | undefined); + + if (!existingUser) { + return fail(400, { + message: "Incorrect username or password", + }); + } + + const validPassword = await new Argon2id().verify( + existingUser.hashed_password, + password + ); + if (!validPassword) { + return fail(400, { + message: "Incorrect username or password", + }); + } + + const session = await lucia.createSession(existingUser.id, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + + return redirect(302, "/"); + }, +}; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 00000000..591ecd14 --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,42 @@ + + + +
+

Sign in

+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ {#if quote != ""} + "{quote}" + {/if} + +
+
diff --git a/src/routes/settings/+page.server.ts b/src/routes/settings/+page.server.ts new file mode 100644 index 00000000..dc603db1 --- /dev/null +++ b/src/routes/settings/+page.server.ts @@ -0,0 +1,61 @@ +import { redirect, type Actions } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; +import { db } from "$lib/db/db.server"; +import { userTable } from "$lib/db/schema"; +import { eq } from "drizzle-orm"; +import { Argon2id } from "oslo/password"; + +export const load: PageServerLoad = async (event) => { + if (event.locals.user) + return { + user: event.locals.user, + }; + return redirect(302, "/login"); +}; + +export const actions: Actions = { + default: async (event: { request: { formData: () => any; }; }) => { + const formData = await event.request.formData(); + let userId = formData.get("user_id"); + let username = formData.get("username"); + let firstName = formData.get("first_name"); + let lastName = formData.get("last_name"); + + let password = formData.get("password"); + + if (!userId) { + return { + status: 400, + body: { + message: "User ID is required" + } + }; + } + + if (password) { + let hashedPassword = await new Argon2id().hash(password); + console.log(hashedPassword) + await db.update(userTable) + .set({ + hashed_password: hashedPassword + }) + .where(eq(userTable.id, userId)); + } + + await db.update(userTable) + .set({ + username: username, + first_name: firstName, + last_name: lastName, + }) + .where(eq(userTable.id, userId)); + + return { + status: 200, + body: { + message: "User updated" + + } + }; + } +}; \ No newline at end of file diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte new file mode 100644 index 00000000..8e2fdee5 --- /dev/null +++ b/src/routes/settings/+page.svelte @@ -0,0 +1,59 @@ + + +

Settings Page

+ +

User Account Settings

+ +
+
+ +
+ +
+ +
+ +
+ + + +
+
+ +For Debug Use: UUID={user_id} diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts new file mode 100644 index 00000000..990d5cdb --- /dev/null +++ b/src/routes/signup/+page.server.ts @@ -0,0 +1,105 @@ +// routes/signup/+page.server.ts +import { lucia } from "$lib/server/auth"; +import { fail, redirect } from "@sveltejs/kit"; +import { generateId } from "lucia"; +import { Argon2id } from "oslo/password"; +import { db } from "$lib/db/db.server"; +import type { DatabaseUser } from "$lib/server/auth"; + +import type { Actions } from "./$types"; +import { userTable } from "$lib/db/schema"; +import { eq } from "drizzle-orm"; + +export const actions: Actions = { + default: async (event) => { + const formData = await event.request.formData(); + const username = formData.get("username"); + const password = formData.get("password"); + const firstName = formData.get("first_name"); + const lastName = formData.get("last_name"); + // username must be between 4 ~ 31 characters, and only consists of lowercase letters, 0-9, -, and _ + // keep in mind some database (e.g. mysql) are case insensitive + + // check all to make sure all fields are provided + if (!username || !password || !firstName || !lastName) { + return fail(400, { + message: "All fields are required", + }); + } + + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return fail(400, { + message: "Invalid username", + }); + } + if ( + typeof password !== "string" || + password.length < 6 || + password.length > 255 + ) { + return fail(400, { + message: "Invalid password", + }); + } + + if ( + typeof firstName !== "string" || + firstName.length < 1 || + firstName.length > 255 + ) { + return fail(400, { + message: "Invalid first name", + }); + } + + if ( + typeof lastName !== "string" || + lastName.length < 1 || + lastName.length > 255 + ) { + return fail(400, { + message: "Invalid last name", + }); + } + + const userId = generateId(15); + const hashedPassword = await new Argon2id().hash(password); + + const usernameTaken = await db + .select() + .from(userTable) + .where(eq(userTable.username, username)) + .limit(1) + .then((results) => results[0] as unknown as DatabaseUser | undefined); + + if (usernameTaken) { + return fail(400, { + message: "Username already taken", + }); + } + await db + .insert(userTable) + .values({ + id: userId, + username: username, + first_name: firstName, + last_name: lastName, + hashed_password: hashedPassword, + } as DatabaseUser) + .execute(); + + const session = await lucia.createSession(userId, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + + redirect(302, "/"); + }, +}; diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte new file mode 100644 index 00000000..2dffe4c9 --- /dev/null +++ b/src/routes/signup/+page.svelte @@ -0,0 +1,17 @@ + + + +

Sign up

+
+ + +
+ +
+
+ +
+ +
diff --git a/src/services/adventureService.ts b/src/services/adventureService.ts index c994ee98..9e984f45 100644 --- a/src/services/adventureService.ts +++ b/src/services/adventureService.ts @@ -7,13 +7,7 @@ import { visitCount } from "$lib/utils/stores/visitCountStore"; // Check if localStorage is available (browser environment) const isBrowser = typeof window !== "undefined"; -// Load adventures from localStorage on startup (only in the browser) -if (isBrowser) { - const storedAdventures = localStorage.getItem("adventures"); - if (storedAdventures) { - adventures = JSON.parse(storedAdventures); - } -} +// export function getNextId() { let nextId = Math.max(0, ...adventures.map((adventure) => adventure.id)) + 1; @@ -37,36 +31,6 @@ export function getAdventures(): Adventure[] { return adventures; } -export function removeAdventure(event: { detail: number }) { - adventures = adventures.filter((adventure) => adventure.id !== event.detail); - if (isBrowser) { - localStorage.setItem("adventures", JSON.stringify(adventures)); - visitCount.update((n) => n - 1); - } -} - -export function saveEdit(adventure: Adventure) { - let editId = adventure.id; - console.log("saving edit"); - let editName = adventure.name; - let editLocation = adventure.location; - let editCreated = adventure.created; - let oldAdventure: Adventure | undefined = adventures.find( - (adventure) => adventure.id === editId - ); - console.log("old" + oldAdventure); - if (oldAdventure) { - oldAdventure.name = editName; - oldAdventure.location = editLocation; - oldAdventure.created = editCreated; - } - editId = NaN; - console.log("done"); - if (isBrowser) { - localStorage.setItem("adventures", JSON.stringify(adventures)); - } -} - export function clearAdventures() { adventures = []; if (isBrowser) { diff --git a/startup.sh b/startup.sh index fbc29bbf..07e73f86 100644 --- a/startup.sh +++ b/startup.sh @@ -17,8 +17,12 @@ echo "Starting AdventureLog" # Wait for the database to start up wait_for_db +# generate the schema +npm run generate + # Run database migration npm run migrate +echo "The orgin to be set is: $ORIGIN" # Start the application -node build/index.js +ORIGIN=$ORIGIN node build diff --git a/svelte.config.js b/svelte.config.js index c2bb7a19..fe1cd454 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -14,6 +14,7 @@ const config = { preprocess: vitePreprocess(), kit: { adapter: adapter(), + csrf: { checkOrigin: true, } }, }; diff --git a/tailwind.config.js b/tailwind.config.js index 2d48b1b8..389e6e93 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,6 +6,6 @@ export default { }, plugins: [require("@tailwindcss/typography"), require("daisyui")], daisyui: { - themes: ["night"], + themes: ["sunset"], }, };