mirror of
https://github.com/BradNut/TofuStack
synced 2025-09-08 17:40:26 +00:00
added deployment config
This commit is contained in:
parent
2df57eac73
commit
c2eb0df2da
9 changed files with 189 additions and 115 deletions
1
.node-version
Normal file
1
.node-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
22.1.0
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
version: "3.8"
|
||||
version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
|
|
@ -7,37 +7,15 @@ services:
|
|||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- "6379:6379"
|
||||
- '6379:6379'
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
minio:
|
||||
image: docker.io/bitnami/minio
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
networks:
|
||||
- minionetwork
|
||||
volumes:
|
||||
- 'minio_data:/data'
|
||||
environment:
|
||||
- MINIO_ROOT_USER=user
|
||||
- MINIO_ROOT_PASSWORD=password
|
||||
- MINIO_DEFAULT_BUCKETS=dev
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
minio_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
minionetwork:
|
||||
driver: bridge
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"test": "npm run test:integration && npm run test:unit",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"initialize": "pnpm install && docker-compose up --no-recreate -d && pnpm db:migrate",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write .",
|
||||
"test:integration": "playwright test",
|
||||
|
|
|
|||
27
render.yaml
Normal file
27
render.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
- type: web
|
||||
name: web
|
||||
runtime: node
|
||||
region: oregon # optional (defaults to oregon)
|
||||
plan: starter # optional (defaults to starter instance type)
|
||||
branch: main # optional (defaults to master)
|
||||
buildCommand: npm install --force && npm run build
|
||||
startCommand: npm run db:migrate && node build/index.js
|
||||
healthCheckPath: /
|
||||
envVars:
|
||||
- key: DATABASE_URL
|
||||
fromDatabase:
|
||||
name: db
|
||||
property: connectionString
|
||||
- key: PUBLIC_ORIGIN
|
||||
fromDatabase:
|
||||
name: web
|
||||
property: host
|
||||
- type: redis
|
||||
name: private redis
|
||||
ipAllowList: [] # Only allow internal connections
|
||||
|
||||
databases:
|
||||
- name: db
|
||||
databaseName: postgres
|
||||
ipAllowList: []
|
||||
|
|
@ -1,18 +1,30 @@
|
|||
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
CREATE EXTENSION IF NOT EXISTS "citext";
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "email_verifications" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"hashed_token" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL
|
||||
"requested_email" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "email_verifications_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "tokens" (
|
||||
CREATE TABLE IF NOT EXISTS "login_requests" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"hashed_token" text NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "tokens_token_unique" UNIQUE("token")
|
||||
CONSTRAINT "login_requests_email_unique" UNIQUE("email")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
|
|
@ -26,13 +38,13 @@ CREATE TABLE IF NOT EXISTS "users" (
|
|||
);
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
ALTER TABLE "email_verifications" ADD CONSTRAINT "email_verifications_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "tokens" ADD CONSTRAINT "tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
|
@ -1,9 +1,141 @@
|
|||
{
|
||||
"id": "8bb6c6c1-e68f-4b94-a390-b51666d00dbb",
|
||||
"id": "2e0c1e11-ed33-45bf-8084-c3200d8f65a8",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "6",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.email_verifications": {
|
||||
"name": "email_verifications",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"hashed_token": {
|
||||
"name": "hashed_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"requested_email": {
|
||||
"name": "requested_email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"email_verifications_user_id_users_id_fk": {
|
||||
"name": "email_verifications_user_id_users_id_fk",
|
||||
"tableFrom": "email_verifications",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"email_verifications_user_id_unique": {
|
||||
"name": "email_verifications_user_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"public.login_requests": {
|
||||
"name": "login_requests",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"hashed_token": {
|
||||
"name": "hashed_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"login_requests_email_unique": {
|
||||
"name": "login_requests_email_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"public.sessions": {
|
||||
"name": "sessions",
|
||||
"schema": "",
|
||||
|
|
@ -46,82 +178,6 @@
|
|||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.tokens": {
|
||||
"name": "tokens",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"token": {
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"tokens_user_id_users_id_fk": {
|
||||
"name": "tokens_user_id_users_id_fk",
|
||||
"tableFrom": "tokens",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"tokens_token_unique": {
|
||||
"name": "tokens_token_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"token"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1716599372513,
|
||||
"tag": "0000_first_crystal",
|
||||
"when": 1719436322147,
|
||||
"tag": "0000_sudden_human_fly",
|
||||
"breakpoints": false
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -20,11 +20,10 @@ export class EmailVerificationsRepository implements Repository {
|
|||
}
|
||||
|
||||
// finds a valid record by token and userId
|
||||
async findValidRecord(userId: string, hashedToken: string) {
|
||||
async findValidRecord(userId: string) {
|
||||
return this.db.select().from(emailVerificationsTable).where(
|
||||
and(
|
||||
eq(emailVerificationsTable.userId, userId),
|
||||
eq(emailVerificationsTable.hashedToken, hashedToken),
|
||||
lte(emailVerificationsTable.expiresAt, new Date())
|
||||
)).then(takeFirst)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export class EmailVerificationsService {
|
|||
private async findAndBurnEmailVerificationToken(userId: string, token: string) {
|
||||
return this.db.transaction(async (trx) => {
|
||||
// find a valid record
|
||||
const emailVerificationRecord = await this.emailVerificationsRepository.trxHost(trx).findValidRecord(userId, token);
|
||||
const emailVerificationRecord = await this.emailVerificationsRepository.trxHost(trx).findValidRecord(userId);
|
||||
if (!emailVerificationRecord) return null;
|
||||
|
||||
// check if the token is valid
|
||||
|
|
|
|||
Loading…
Reference in a new issue