1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-04 20:55:19 +02:00

Merge pull request #39 from seanmorley15/development

Development
This commit is contained in:
Sean Morley 2024-04-18 19:48:41 -04:00 committed by GitHub
commit c210c655af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 892 additions and 117 deletions

View file

@ -10,5 +10,7 @@ _**⚠️ AdventureLog is in early development and is not recommended for produc
2. Edit the `docker-compose.yml` file and change the database password
3. Run `docker compose up -d` to build the image and start the container
4. Wait for the app to start up and migrate then visit the port and enjoy!
5. After navigating to the app, fill out the form to create the admin user.
**Note**: The `ORIGIN` variable is required for CSRF protection. It can be omitted if using a reverse proxy or other HTTPS service.

15
compose-dev.yml Normal file
View file

@ -0,0 +1,15 @@
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=
# ORIGIN is only necessary when not using a reverse proxy or hosting that includes https
- ORIGIN=http://localhost:3000
- SKIP_DB_WAIT=true
# Only necessary for externaly hosted databases such as NeonDB
volumes:
- ./sql:/sql
# docker compose -f ./compose-dev.yml up --build

View file

@ -7,6 +7,8 @@ services:
- 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
- SKIP_DB_WAIT=false
# Only necessary for externaly hosted databases such as NeonDB
depends_on:
- db
volumes:

View file

@ -0,0 +1,3 @@
ALTER TABLE "user" ADD COLUMN "signup_date" timestamp with time zone NOT NULL;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "last_login" timestamp with time zone;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "role" text NOT NULL;

View file

@ -0,0 +1,409 @@
{
"id": "72cc9a9a-a12e-4ca2-b644-b704673af4d7",
"prevId": "91692e84-91ec-4411-ad9d-7b8b14f67dda",
"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": {
"featuredAdventures_name_unique": {
"name": "featuredAdventures_name_unique",
"nullsNotDistinct": false,
"columns": [
"name"
]
}
}
},
"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
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"date": {
"name": "date",
"type": "text",
"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
},
"icon": {
"name": "icon",
"type": "text",
"primaryKey": false,
"notNull": false
},
"hashed_password": {
"name": "hashed_password",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"signup_date": {
"name": "signup_date",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true
},
"last_login": {
"name": "last_login",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"role": {
"name": "role",
"type": "text",
"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": {}
},
"userVisitedWorldTravel": {
"name": "userVisitedWorldTravel",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"country_code": {
"name": "country_code",
"type": "text",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"region_id": {
"name": "region_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"userVisitedWorldTravel_country_code_worldTravelCountries_country_code_fk": {
"name": "userVisitedWorldTravel_country_code_worldTravelCountries_country_code_fk",
"tableFrom": "userVisitedWorldTravel",
"tableTo": "worldTravelCountries",
"columnsFrom": [
"country_code"
],
"columnsTo": [
"country_code"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"userVisitedWorldTravel_user_id_user_id_fk": {
"name": "userVisitedWorldTravel_user_id_user_id_fk",
"tableFrom": "userVisitedWorldTravel",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"userVisitedWorldTravel_region_id_worldTravelCountryRegions_id_fk": {
"name": "userVisitedWorldTravel_region_id_worldTravelCountryRegions_id_fk",
"tableFrom": "userVisitedWorldTravel",
"tableTo": "worldTravelCountryRegions",
"columnsFrom": [
"region_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"worldTravelCountries": {
"name": "worldTravelCountries",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"country_code": {
"name": "country_code",
"type": "text",
"primaryKey": false,
"notNull": true
},
"continent": {
"name": "continent",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"worldTravelCountries_country_code_unique": {
"name": "worldTravelCountries_country_code_unique",
"nullsNotDistinct": false,
"columns": [
"country_code"
]
}
}
},
"worldTravelCountryRegions": {
"name": "worldTravelCountryRegions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"country_code": {
"name": "country_code",
"type": "text",
"primaryKey": false,
"notNull": true
},
"info": {
"name": "info",
"type": "json",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"worldTravelCountryRegions_country_code_worldTravelCountries_country_code_fk": {
"name": "worldTravelCountryRegions_country_code_worldTravelCountries_country_code_fk",
"tableFrom": "worldTravelCountryRegions",
"tableTo": "worldTravelCountries",
"columnsFrom": [
"country_code"
],
"columnsTo": [
"country_code"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -22,6 +22,13 @@
"when": 1713125110816,
"tag": "0002_far_mephistopheles",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1713402092365,
"tag": "0003_easy_iron_monger",
"breakpoints": true
}
]
}

167
package-lock.json generated
View file

@ -1,14 +1,16 @@
{
"name": "adventurelog",
"version": "0.0.1",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "adventurelog",
"version": "0.0.1",
"version": "0.1.0",
"dependencies": {
"@lucia-auth/adapter-drizzle": "^1.0.7",
"@vercel/analytics": "^1.2.2",
"@vercel/speed-insights": "^1.0.10",
"drizzle-orm": "^0.30.6",
"oslo": "^1.2.0",
"postgres": "^3.4.4"
@ -52,7 +54,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"devOptional": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
@ -886,7 +888,7 @@
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"devOptional": true,
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@ -900,7 +902,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=6.0.0"
}
@ -909,7 +911,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=6.0.0"
}
@ -918,13 +920,13 @@
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
"devOptional": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"devOptional": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@ -1529,7 +1531,7 @@
"version": "1.0.0-next.25",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
"dev": true
"devOptional": true
},
"node_modules/@rollup/plugin-commonjs": {
"version": "25.0.7",
@ -2329,7 +2331,7 @@
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.5.tgz",
"integrity": "sha512-ULe3PB00q4+wYRL+IS5FDPsCEVnhEITofm7b9Yz8malcH3r1SAnW/JJ6T13hIMeu8QNRIuVQWo+P4+2VklbnLQ==",
"dev": true,
"devOptional": true,
"hasInstallScript": true,
"dependencies": {
"@types/cookie": "^0.6.0",
@ -2361,7 +2363,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.2.tgz",
"integrity": "sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==",
"dev": true,
"devOptional": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^2.0.0",
"debug": "^4.3.4",
@ -2383,7 +2385,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz",
"integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==",
"dev": true,
"devOptional": true,
"dependencies": {
"debug": "^4.3.4"
},
@ -2437,13 +2439,13 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"dev": true
"devOptional": true
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
"devOptional": true
},
"node_modules/@types/node": {
"version": "20.12.3",
@ -2534,6 +2536,26 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true
},
"node_modules/@vercel/analytics": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.2.2.tgz",
"integrity": "sha512-X0rctVWkQV1e5Y300ehVNqpOfSOufo7ieA5PIdna8yX/U7Vjz0GFsGf4qvAhxV02uQ2CVt7GYcrFfddXXK2Y4A==",
"dependencies": {
"server-only": "^0.0.1"
},
"peerDependencies": {
"next": ">= 13",
"react": "^18 || ^19"
},
"peerDependenciesMeta": {
"next": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/@vercel/nft": {
"version": "0.26.4",
"resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.26.4.tgz",
@ -2588,6 +2610,40 @@
"node": ">=8"
}
},
"node_modules/@vercel/speed-insights": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.0.10.tgz",
"integrity": "sha512-4uzdKB0RW6Ff2FkzshzjZ+RlJfLPxgm/00i0XXgxfMPhwnnsk92YgtqsxT9OcPLdJUyVU1DqFlSWWjIQMPkh0g==",
"hasInstallScript": true,
"peerDependencies": {
"@sveltejs/kit": "^1 || ^2",
"next": ">= 13",
"react": "^18 || ^19",
"svelte": "^4",
"vue": "^3",
"vue-router": "^4"
},
"peerDependenciesMeta": {
"@sveltejs/kit": {
"optional": true
},
"next": {
"optional": true
},
"react": {
"optional": true
},
"svelte": {
"optional": true
},
"vue": {
"optional": true
},
"vue-router": {
"optional": true
}
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -2598,7 +2654,7 @@
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"devOptional": true,
"bin": {
"acorn": "bin/acorn"
},
@ -2699,7 +2755,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"devOptional": true,
"dependencies": {
"dequal": "^2.0.3"
}
@ -2751,7 +2807,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
"dev": true,
"devOptional": true,
"dependencies": {
"dequal": "^2.0.3"
}
@ -2979,7 +3035,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
"dev": true,
"devOptional": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"@types/estree": "^1.0.1",
@ -3046,7 +3102,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">= 0.6"
}
@ -3094,7 +3150,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"devOptional": true,
"dependencies": {
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
@ -3160,7 +3216,7 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"devOptional": true,
"dependencies": {
"ms": "2.1.2"
},
@ -3177,7 +3233,7 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=0.10.0"
}
@ -3192,7 +3248,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=6"
}
@ -3219,7 +3275,7 @@
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz",
"integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==",
"dev": true
"devOptional": true
},
"node_modules/didyoumean": {
"version": "1.2.2",
@ -3965,7 +4021,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"dev": true,
"devOptional": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@ -4024,7 +4080,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz",
"integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==",
"dev": true
"devOptional": true
},
"node_modules/esniff": {
"version": "2.0.1",
@ -4045,7 +4101,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
@ -4321,13 +4377,13 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==",
"dev": true
"devOptional": true
},
"node_modules/globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true
"devOptional": true
},
"node_modules/graceful-fs": {
"version": "4.2.11",
@ -4402,7 +4458,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
"integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==",
"dev": true,
"devOptional": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@ -4518,7 +4574,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/estree": "*"
}
@ -4589,7 +4645,7 @@
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=6"
}
@ -4613,7 +4669,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
"dev": true
"devOptional": true
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
@ -5150,7 +5206,7 @@
"version": "0.30.8",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
"integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
"dev": true,
"devOptional": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
@ -5186,7 +5242,7 @@
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
"devOptional": true
},
"node_modules/memfs": {
"version": "3.5.3",
@ -5327,7 +5383,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=4"
}
@ -5336,7 +5392,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=10"
}
@ -5345,7 +5401,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"devOptional": true
},
"node_modules/mz": {
"version": "2.7.0",
@ -5362,7 +5418,7 @@
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"devOptional": true,
"funding": [
{
"type": "github",
@ -5562,7 +5618,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^3.0.0",
@ -5671,7 +5727,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
"devOptional": true
},
"node_modules/picomatch": {
"version": "2.3.1",
@ -5707,7 +5763,7 @@
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"devOptional": true,
"funding": [
{
"type": "opencollective",
@ -6025,7 +6081,7 @@
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz",
"integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/estree": "1.0.5"
},
@ -6082,7 +6138,7 @@
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
"dev": true,
"devOptional": true,
"dependencies": {
"mri": "^1.1.0"
},
@ -6149,6 +6205,11 @@
"node": ">=10"
}
},
"node_modules/server-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@ -6159,7 +6220,7 @@
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==",
"dev": true
"devOptional": true
},
"node_modules/shebang-command": {
"version": "2.0.0",
@ -6198,7 +6259,7 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
"dev": true,
"devOptional": true,
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
@ -6242,7 +6303,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=0.10.0"
}
@ -6479,7 +6540,7 @@
"version": "4.2.12",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz",
"integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==",
"dev": true,
"devOptional": true,
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
@ -6526,7 +6587,7 @@
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
"integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==",
"dev": true,
"devOptional": true,
"engines": {
"node": "^12.20 || ^14.13.1 || >= 16"
},
@ -6719,7 +6780,7 @@
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
"dev": true,
"devOptional": true,
"dependencies": {
"globalyzer": "0.1.0",
"globrex": "^0.1.2"
@ -6741,7 +6802,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=6"
}
@ -6829,7 +6890,7 @@
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz",
"integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==",
"dev": true,
"devOptional": true,
"dependencies": {
"esbuild": "^0.20.1",
"postcss": "^8.4.38",
@ -6884,7 +6945,7 @@
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
"dev": true,
"devOptional": true,
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
},

View file

@ -39,6 +39,8 @@
"type": "module",
"dependencies": {
"@lucia-auth/adapter-drizzle": "^1.0.7",
"@vercel/analytics": "^1.2.2",
"@vercel/speed-insights": "^1.0.10",
"drizzle-orm": "^0.30.6",
"oslo": "^1.2.0",
"postgres": "^3.4.4"

1
src/app.d.ts vendored
View file

@ -3,6 +3,7 @@ declare global {
interface Locals {
user: import("lucia").User | null;
session: import("lucia").Session | null;
isServerSetup: boolean | null;
}
}
}

View file

@ -1,6 +1,9 @@
import { lucia } from "$lib/server/auth";
import type { Handle } from "@sveltejs/kit";
import { redirect, type Handle } from "@sveltejs/kit";
import { sequence } from '@sveltejs/kit/hooks';
import { db } from "$lib/db/db.server";
import { userTable } from "$lib/db/schema";
import { eq } from "drizzle-orm";
export const authHook: Handle = async ({ event, resolve }) => {
const sessionId = event.cookies.get(lucia.sessionCookieName);
@ -53,4 +56,27 @@ export const themeHook: Handle = async ({ event, resolve }) => {
return await resolve(event);
}
export const handle = sequence(authHook, themeHook);
export const setupAdminUser: Handle = async ({ event, resolve }) => {
// Check if an admin user exists
/* let result = await db
.select()
.from(userVisitedAdventures)
.where(eq(userVisitedAdventures.userId, event.locals.user.id))
.execute();*/
let adminUser = await db
.select()
.from(userTable)
.where(eq(userTable.role, 'admin'))
.execute();
// If an admin user exists, return the resolved event
if (adminUser != null && adminUser.length > 0) {
event.locals.isServerSetup = true;
return await resolve(event);
}
console.log("No admin user found");
event.locals.isServerSetup = false;
return await resolve(event);
};
export const handle = sequence(setupAdminUser, authHook, themeHook);

View file

@ -36,7 +36,12 @@
<p class="text-lg ml-4 font-bold">Hi, {user.first_name} {user.last_name}</p>
<li><a>Profile</a></li>
<li><button on:click={navToLog}>My Log</button></li>
<li><button on:click={navToSettings}>Settings</button></li>
<li><button on:click={navToSettings}>User Settings</button></li>
{#if user.role == "admin"}
<li>
<button on:click={() => goto("/settings/admin")}>Admin Settings</button>
</li>
{/if}
<form method="post">
<li><button formaction="/?/logout">Logout</button></li>
</form>

View file

@ -28,6 +28,15 @@ export const userTable = pgTable("user", {
last_name: text("last_name").notNull(),
icon: text("icon"),
hashed_password: varchar("hashed_password").notNull(),
signup_date: timestamp("signup_date", {
withTimezone: true,
mode: "date",
}).notNull(),
last_login: timestamp("last_login", {
withTimezone: true,
mode: "date",
}),
role: text("role").notNull(),
});
// export type SelectUser = typeof userTable.$inferSelect;

View file

@ -20,6 +20,9 @@ export const lucia = new Lucia(adapter, {
first_name: attributes.first_name,
last_name: attributes.last_name,
icon: attributes.icon,
signup_date: attributes.signup_date,
last_login: attributes.last_login,
role: attributes.role,
};
},
});
@ -38,4 +41,7 @@ export interface DatabaseUser {
last_name: string;
icon: string;
hashed_password: string;
signup_date: Date;
last_login: Date;
role: string;
}

View file

@ -1,12 +1,21 @@
import type { LayoutServerLoad, PageServerLoad } from "./$types";
import { inject } from "@vercel/analytics";
import { injectSpeedInsights } from "@vercel/speed-insights/sveltekit";
if (process.env.USING_VERCEL === "true") {
inject();
injectSpeedInsights();
}
export const load: LayoutServerLoad = async (event) => {
if (event.locals.user) {
return {
user: event.locals.user,
isServerSetup: event.locals.isServerSetup,
};
}
return {
user: null,
isServerSetup: event.locals.isServerSetup,
};
};
};

View file

@ -4,6 +4,21 @@
import Navbar from "$lib/components/Navbar.svelte";
import type { SubmitFunction } from "@sveltejs/kit";
import "../app.css";
import { goto } from "$app/navigation";
import { onMount } from "svelte";
import { page } from "$app/stores";
let isServerSetup = data.isServerSetup;
onMount(() => {
console.log("isServerSetup", isServerSetup);
if (!isServerSetup && $page.url.pathname !== "/setup") {
goto("/setup");
}
if (isServerSetup && $page.url.pathname == "/setup") {
goto("/");
}
});
</script>
<!-- passes the user object to the navbar component -->

View file

@ -47,7 +47,7 @@ export const actions: Actions = {
});
}
const existingUser = await db
const existingUser:any = await db
.select()
.from(userTable)
.where(eq(userTable.username, username))
@ -70,6 +70,14 @@ export const actions: Actions = {
});
}
await db
.update(userTable)
.set({
last_login: new Date(),
})
.where(eq(userTable.id, existingUser.id))
.execute();
const session = await lucia.createSession(existingUser.id, {});
const sessionCookie = lucia.createSessionCookie(session.id);
event.cookies.set(sessionCookie.name, sessionCookie.value, {

View file

@ -4,6 +4,7 @@ import { db } from "$lib/db/db.server";
import { userTable } from "$lib/db/schema";
import { eq } from "drizzle-orm";
import { Argon2id } from "oslo/password";
import type { DatabaseUser } from "$lib/server/auth";
export const load: PageServerLoad = async (event) => {
if (event.locals.user)
@ -42,6 +43,22 @@ export const actions: Actions = {
};
}
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 {
status: 400,
body: {
message: "Username taken!"
}
};
}
if (password) {
let hashedPassword = await new Argon2id().hash(password);
console.log(hashedPassword)

View file

@ -1,68 +1,72 @@
<script>
import { enhance } from "$app/forms";
import { enhance } from "$app/forms";
import AdventureCard from "$lib/components/AdventureCard.svelte";
export let data;
let username = data.user?.username;
let first_name = data.user?.first_name;
let last_name = data.user?.last_name;
let user_id = data.user?.id;
let icon = data.user?.icon;
export let data;
let username = data.user?.username;
let first_name = data.user?.first_name;
let last_name = data.user?.last_name;
let user_id = data.user?.id;
let icon = data.user?.icon;
let signup_date = data.user?.signup_date;
let role = data.user?.role;
</script>
<h1 class="text-center font-extrabold text-4xl mb-6">Settings Page</h1>
<h1 class="text-center font-extrabold text-xl">User Account Settings</h1>
<div class="flex justify-center">
<form method="post" use:enhance class="w-full max-w-xs">
<label for="username">Username</label>
<input
bind:value={username}
name="username"
id="username"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="first_name">First Name</label>
<input
type="text"
bind:value={first_name}
name="first_name"
id="first_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="last_name">Last Name</label>
<input
type="text"
bind:value={last_name}
name="last_name"
id="last_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="icon">Profile Icon (emoji)</label>
<input
type="emoji"
bind:value={icon}
name="icon"
id="icon"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="password">Password</label>
<input
type="password"
name="password"
id="password"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<!-- make hidden input where the user id is -->
<input
type="hidden"
bind:value={user_id}
name="user_id"
id="user_id"
class="block mb-2 input input-bordered w-full max-w-xs"
/>
<button class="py-2 px-4 btn btn-primary">Update</button>
</form>
<form method="post" use:enhance class="w-full max-w-xs">
<label for="username">Username</label>
<input
bind:value={username}
name="username"
id="username"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="first_name">First Name</label>
<input
type="text"
bind:value={first_name}
name="first_name"
id="first_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="last_name">Last Name</label>
<input
type="text"
bind:value={last_name}
name="last_name"
id="last_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="icon">Profile Icon (emoji)</label>
<input
type="emoji"
bind:value={icon}
name="icon"
id="icon"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="password">Password</label>
<input
type="password"
name="password"
id="password"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<!-- make hidden input where the user id is -->
<input
type="hidden"
bind:value={user_id}
name="user_id"
id="user_id"
class="block mb-2 input input-bordered w-full max-w-xs"
/>
<button class="py-2 px-4 btn btn-primary">Update</button>
</form>
</div>
<small class="text-center">For Debug Use: UUID={user_id}</small>
<small class="text-center"
><b>For Debug Use:</b> UUID={user_id} Signup Date={signup_date} Role={role}</small
>

View file

@ -0,0 +1,121 @@
// 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",
});
}
let adminUser = await db
.select()
.from(userTable)
.where(eq(userTable.role, 'admin'))
.execute();
if (adminUser != null && adminUser.length > 0) {
return fail(400, {
message: "Admin user already exists",
});
}
await db
.insert(userTable)
.values({
id: userId,
username: username,
first_name: firstName,
last_name: lastName,
hashed_password: hashedPassword,
signup_date: new Date(),
role: "admin",
last_login: new Date(),
} 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, "/");
},
};

View file

@ -0,0 +1,44 @@
<script lang="ts">
import { enhance } from "$app/forms";
</script>
<h1 class="text-center font-bold text-4xl">AdventureLog Setup</h1>
<!-- centered text descripton welcomeing the user -->
<p class="text-center mt-4">
Welcome to AdventureLog! Please follow the steps below to setup your server.
</p>
<!-- step 1 create admin user -->
<h2 class="text-center font-bold text-2xl mt-6">Create Admin User</h2>
<div class="flex justify-center">
<form method="post" use:enhance class="w-full max-w-xs">
<label for="username">Username</label>
<input
name="username"
id="username"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="first_name">First Name</label>
<input
name="first_name"
id="first_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="last_name">Last Name</label>
<input
name="last_name"
id="last_name"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<label for="password">Password</label>
<input
type="password"
name="password"
id="password"
class="block mb-2 input input-bordered w-full max-w-xs"
/><br />
<button class="py-2 px-4 btn btn-primary">Signup</button>
</form>
</div>

View file

@ -90,6 +90,9 @@ export const actions: Actions = {
first_name: firstName,
last_name: lastName,
hashed_password: hashedPassword,
signup_date: new Date(),
role: "user",
last_login: new Date(),
} as DatabaseUser)
.execute();

View file

@ -24,12 +24,18 @@ run_sql_scripts() {
echo "Finished running SQL scripts."
}
# Start your application here
# Print message
echo "Starting AdventureLog"
# Wait for the database to start up
wait_for_db
if [ -z "$SKIP_DB_WAIT" ] || [ "$SKIP_DB_WAIT" = "false" ]; then
wait_for_db
fi
# Wait for the database to start up
# generate the schema
# npm run generate