memory: log task worker session - due dates, subtasks, task page

This commit is contained in:
2026-01-29 07:07:45 +00:00
parent ad145c9ec3
commit 4663a45b45
39 changed files with 1044 additions and 3 deletions

View File

@@ -0,0 +1,7 @@
FROM oven/bun:1-alpine
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile 2>/dev/null || bun install
COPY . .
EXPOSE 3001
CMD ["bun", "run", "src/index.ts"]

View File

@@ -0,0 +1,10 @@
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});

View File

@@ -0,0 +1,23 @@
{
"name": "APP_TEMPLATE-api",
"version": "0.0.1",
"scripts": {
"dev": "bun run --watch src/index.ts",
"start": "bun run src/index.ts",
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio",
"test": "bun test"
},
"dependencies": {
"better-auth": "^1.0.0",
"drizzle-orm": "^0.38.0",
"elysia": "^1.2.0",
"postgres": "^3.4.0"
},
"devDependencies": {
"@types/bun": "latest",
"drizzle-kit": "^0.30.0"
}
}

View File

@@ -0,0 +1,6 @@
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });

View File

@@ -0,0 +1,54 @@
import { pgTable, text, timestamp, boolean, uuid } from 'drizzle-orm/pg-core';
// BetterAuth tables (managed by BetterAuth, do not modify)
export const users = pgTable('users', {
id: text('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: boolean('email_verified').default(false),
image: text('image'),
role: text('role').default('user'),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
export const sessions = pgTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id').notNull().references(() => users.id),
token: text('token').notNull().unique(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
export const accounts = pgTable('accounts', {
id: text('id').primaryKey(),
userId: text('user_id').notNull().references(() => users.id),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
password: text('password'),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
export const verifications = pgTable('verifications', {
id: text('id').primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
// ============================================
// APP-SPECIFIC TABLES — Customize below
// ============================================
// Example:
// export const items = pgTable('items', {
// id: uuid('id').primaryKey().defaultRandom(),
// title: text('title').notNull(),
// userId: text('user_id').references(() => users.id),
// createdAt: timestamp('created_at').defaultNow(),
// updatedAt: timestamp('updated_at').defaultNow(),
// });

View File

@@ -0,0 +1,14 @@
import { Elysia } from 'elysia';
import { cors } from '@elysiajs/cors';
import { hammerRoutes } from './routes/hammer';
const app = new Elysia()
.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:5173'],
credentials: true,
}))
.get('/api/health', () => ({ status: 'ok', timestamp: new Date().toISOString() }))
.use(hammerRoutes)
.listen(Number(process.env.PORT) || 3001);
console.log(`🚀 Server running on port ${app.server?.port}`);

View File

@@ -0,0 +1,17 @@
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { db } from '../db';
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: 'pg' }),
emailAndPassword: {
enabled: true,
disableSignUp: true, // Invite-only
},
trustedOrigins: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:5173'],
advanced: {
crossSubDomainCookies: process.env.NODE_ENV === 'production'
? { domain: process.env.COOKIE_DOMAIN || '' }
: undefined,
},
});

View File

@@ -0,0 +1,38 @@
import { Elysia, t } from 'elysia';
import { db } from '../db';
import { users } from '../db/schema';
import { eq } from 'drizzle-orm';
const validateHammerAuth = (authHeader: string | undefined): boolean => {
if (!authHeader) return false;
const token = authHeader.replace('Bearer ', '');
return token === process.env.HAMMER_API_KEY;
};
export const hammerRoutes = new Elysia({ prefix: '/api/hammer' })
.derive(({ request, set }) => {
const authHeader = request.headers.get('authorization');
if (!validateHammerAuth(authHeader)) {
set.status = 401;
throw new Error('Invalid API key');
}
return {};
})
.get('/me', async ({ set }) => {
const hammerUser = await db.query.users.findFirst({
where: eq(users.role, 'service'),
});
if (!hammerUser) {
set.status = 404;
throw new Error('Hammer service account not found');
}
return {
id: hammerUser.id,
name: hammerUser.name,
email: hammerUser.email,
role: hammerUser.role,
};
});
// Add app-specific Hammer routes below