WIP Serverless with infra & drizzle start files.

This commit is contained in:
Patrick Fic
2026-01-13 17:35:25 -08:00
parent 21c132642b
commit 66fcaaf8f4
10 changed files with 2724 additions and 251 deletions

View File

@@ -0,0 +1,31 @@
import { AnyPgColumn, boolean, pgTable, text, timestamp, uuid, index } from 'drizzle-orm/pg-core';
export const shops = pgTable('shops', {
id: uuid('id').defaultRandom().primaryKey(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
esApiKey: text('es_api_key').notNull().unique(),
active: boolean('active').notNull().default(true),
});
export const jobs = pgTable(
'jobs',
{
id: uuid('id').defaultRandom().primaryKey(),
shopId: uuid('shopId')
.references((): AnyPgColumn => shops.id)
.notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
clm_no: text('clm_no'),
ciecaid: text('ciecaid'),
},
(table) => [index('clm_no_idx').on(table.clm_no)]
);
export const joblines = pgTable('joblines', {
id: uuid('id').defaultRandom().primaryKey(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
jobId: uuid('jobId')
.references((): AnyPgColumn => jobs.id)
.notNull(),
line_desc: text('line_desc'),
});

View File

@@ -0,0 +1,22 @@
import { APIGatewayProxyResult } from 'aws-lambda';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { getDb } from '../lib/db';
export const handler = async (): Promise<APIGatewayProxyResult> => {
try {
const db = await getDb();
await migrate(db, { migrationsFolder: 'drizzle' });
return {
statusCode: 200,
body: JSON.stringify({ success: true, message: 'Migrations applied.' }),
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
statusCode: 500,
body: JSON.stringify({ success: false, error: errorMessage }),
};
}
};

View File

@@ -0,0 +1,23 @@
import { APIGatewayProxyResult } from 'aws-lambda';
import { sql } from 'drizzle-orm';
import { getDb } from '../lib/db';
export const handler = async (): Promise<APIGatewayProxyResult> => {
try {
const db = await getDb();
const result = await db.execute(sql`select 1 as ok`);
return {
statusCode: 200,
body: JSON.stringify({ success: true, result }),
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
statusCode: 500,
body: JSON.stringify({ success: false, error: errorMessage }),
};
}
};

View File

@@ -1,2 +1,75 @@
// Placeholder for database utilities
// Export database connection and helper functions here
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
import { drizzle, NodePgDatabase } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from '../db/schema';
type DbSecret = {
username: string;
password: string;
};
let cachedSecret: DbSecret | undefined;
let cachedPool: Pool | undefined;
let cachedDb: NodePgDatabase<typeof schema> | undefined;
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required env var: ${name}`);
}
return value;
}
async function getDbSecret(): Promise<DbSecret> {
if (cachedSecret) return cachedSecret;
const secretArn = requireEnv('DB_SECRET_ARN');
const client = new SecretsManagerClient({});
const result = await client.send(
new GetSecretValueCommand({
SecretId: secretArn,
}),
);
if (!result.SecretString) {
throw new Error('SecretString was empty for DB_SECRET_ARN');
}
const parsed = JSON.parse(result.SecretString) as Partial<DbSecret>;
if (!parsed.username || !parsed.password) {
throw new Error('DB secret missing username/password');
}
cachedSecret = { username: parsed.username, password: parsed.password };
return cachedSecret;
}
export async function getPool(): Promise<Pool> {
if (cachedPool) return cachedPool;
const host = requireEnv('DB_HOST');
const port = Number.parseInt(requireEnv('DB_PORT'), 10);
const database = requireEnv('DB_NAME');
const { username: user, password } = await getDbSecret();
cachedPool = new Pool({
host,
port,
database,
user,
password,
max: 5,
idleTimeoutMillis: 30_000,
connectionTimeoutMillis: 10_000,
});
return cachedPool;
}
export async function getDb(): Promise<NodePgDatabase<typeof schema>> {
if (cachedDb) return cachedDb;
const pool = await getPool();
cachedDb = drizzle(pool, { schema });
return cachedDb;
}