added deployment config

This commit is contained in:
rykuno 2024-06-26 16:57:52 -05:00
parent 2df57eac73
commit c2eb0df2da
9 changed files with 189 additions and 115 deletions

1
.node-version Normal file
View file

@ -0,0 +1 @@
22.1.0

View file

@ -1,4 +1,4 @@
version: "3.8" version: '3.8'
services: services:
postgres: postgres:
image: postgres:latest image: postgres:latest
@ -7,37 +7,15 @@ services:
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres POSTGRES_DB: postgres
ports: ports:
- "5432:5432" - '5432:5432'
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
redis: redis:
image: redis:latest image: redis:latest
ports: ports:
- "6379:6379" - '6379:6379'
volumes: volumes:
- redis_data:/data - 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: volumes:
postgres_data: postgres_data:
redis_data: redis_data:
minio_data:
driver: local
networks:
minionetwork:
driver: bridge

View file

@ -13,6 +13,7 @@
"test": "npm run test:integration && npm run test:unit", "test": "npm run test:integration && npm run test:unit",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "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 .", "lint": "prettier --check . && eslint .",
"format": "prettier --write .", "format": "prettier --write .",
"test:integration": "playwright test", "test:integration": "playwright test",

27
render.yaml Normal file
View 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: []

View file

@ -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, "id" text PRIMARY KEY NOT NULL,
"hashed_token" text NOT NULL,
"user_id" 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, "id" text PRIMARY KEY NOT NULL,
"token" text NOT NULL, "hashed_token" text NOT NULL,
"user_id" text NOT NULL,
"email" text NOT NULL, "email" text NOT NULL,
"expires_at" timestamp with time zone NOT NULL, "expires_at" timestamp with time zone NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_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" ( CREATE TABLE IF NOT EXISTS "users" (
@ -26,13 +38,13 @@ CREATE TABLE IF NOT EXISTS "users" (
); );
DO $$ BEGIN 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 EXCEPTION
WHEN duplicate_object THEN null; WHEN duplicate_object THEN null;
END $$; END $$;
DO $$ BEGIN 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 EXCEPTION
WHEN duplicate_object THEN null; WHEN duplicate_object THEN null;
END $$; END $$;

View file

@ -1,9 +1,141 @@
{ {
"id": "8bb6c6c1-e68f-4b94-a390-b51666d00dbb", "id": "2e0c1e11-ed33-45bf-8084-c3200d8f65a8",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "6", "version": "6",
"dialect": "postgresql", "dialect": "postgresql",
"tables": { "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": { "public.sessions": {
"name": "sessions", "name": "sessions",
"schema": "", "schema": "",
@ -46,82 +178,6 @@
"compositePrimaryKeys": {}, "compositePrimaryKeys": {},
"uniqueConstraints": {} "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": { "public.users": {
"name": "users", "name": "users",
"schema": "", "schema": "",

View file

@ -5,8 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "6", "version": "6",
"when": 1716599372513, "when": 1719436322147,
"tag": "0000_first_crystal", "tag": "0000_sudden_human_fly",
"breakpoints": false "breakpoints": false
} }
] ]

View file

@ -20,11 +20,10 @@ export class EmailVerificationsRepository implements Repository {
} }
// finds a valid record by token and userId // 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( return this.db.select().from(emailVerificationsTable).where(
and( and(
eq(emailVerificationsTable.userId, userId), eq(emailVerificationsTable.userId, userId),
eq(emailVerificationsTable.hashedToken, hashedToken),
lte(emailVerificationsTable.expiresAt, new Date()) lte(emailVerificationsTable.expiresAt, new Date())
)).then(takeFirst) )).then(takeFirst)
} }

View file

@ -63,7 +63,7 @@ export class EmailVerificationsService {
private async findAndBurnEmailVerificationToken(userId: string, token: string) { private async findAndBurnEmailVerificationToken(userId: string, token: string) {
return this.db.transaction(async (trx) => { return this.db.transaction(async (trx) => {
// find a valid record // 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; if (!emailVerificationRecord) return null;
// check if the token is valid // check if the token is valid