claude drizzle integration

This commit is contained in:
2025-08-19 01:48:23 -04:00
parent 9942a11c0d
commit 92535f7e54
10 changed files with 135 additions and 91 deletions

View File

@@ -9,7 +9,7 @@ import { Card } from '@/components/ui/card'
import { RecurrencePicker } from '@/components/recurrence-picker'
import { IcsFilePicker } from '@/components/ics-file-picker'
import { addEvent, deleteEvent, getAllEvents, clearEvents, getDB } from '@/lib/db'
import { saveEvent as addEvent, deleteEvent, getEvents as getAllEvents, clearEvents, updateEvent } from '@/lib/events-db'
import { parseICS, generateICS } from '@/lib/ical'
import type { CalendarEvent } from '@/lib/types'
import { Textarea } from '@/components/ui/textarea'
@@ -74,11 +74,8 @@ export default function HomePage() {
lastModified: new Date().toISOString(),
}
if (editingId) {
const db = await getDB()
if (db) {
await db.put('events', eventData)
setEvents(prev => prev.map(e => (e.id === editingId ? eventData : e)))
}
await updateEvent(eventData)
setEvents(prev => prev.map(e => (e.id === editingId ? eventData : e)))
} else {
await addEvent(eventData)
setEvents(prev => [...prev, eventData])

View File

@@ -1,6 +1,8 @@
import NextAuth, { NextAuthConfig, NextAuthResult } from "next-auth";
import Authentik from "next-auth/providers/authentik";
import type { Provider } from "next-auth/providers";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/db/index";
const providers: Provider[] = [
Authentik({
@@ -20,6 +22,7 @@ export const providerMap = providers.map((provider) => {
});
const config = {
adapter: DrizzleAdapter(db),
providers,
pages: {
signIn: "/signin",

View File

@@ -1,10 +0,0 @@
import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres";
// You can specify any property from the node-postgres connection options
const db = drizzle({
connection: {
connectionString: process.env.DATABASE_URL!,
ssl: true,
},
});

12
src/db/index.ts Normal file
View File

@@ -0,0 +1,12 @@
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const connectionString = process.env.DATABASE_URL!;
const client = postgres(connectionString, {
prepare: false,
connect_timeout: 30,
idle_timeout: 30,
});
export const db = drizzle(client, { schema });

View File

@@ -1,7 +1,36 @@
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
export const usersTable = pgTable("users", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull().unique(),
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
name: text('name'),
image: text('image'),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
export const accounts = pgTable('accounts', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
type: text('type').notNull(),
provider: text('provider').notNull(),
providerAccountId: text('provider_account_id').notNull(),
refreshToken: text('refresh_token'),
accessToken: text('access_token'),
expiresAt: timestamp('expires_at'),
tokenType: text('token_type'),
scope: text('scope'),
idToken: text('id_token'),
sessionState: text('session_state'),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
export const sessions = pgTable('sessions', {
id: uuid('id').primaryKey().defaultRandom(),
sessionToken: text('session_token').notNull().unique(),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
expires: timestamp('expires').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

View File

@@ -1,55 +0,0 @@
import { openDB, DBSchema, IDBPDatabase } from "idb";
import { type CalendarEvent } from "./types";
interface ICalDB extends DBSchema {
events: {
key: string;
value: CalendarEvent;
};
}
let dbPromise: Promise<IDBPDatabase<ICalDB>> | null = null;
async function initDB() {
return openDB<ICalDB>("icalPWA", 1, {
upgrade(db) {
if (!db.objectStoreNames.contains("events")) {
db.createObjectStore("events", { keyPath: "id" });
}
},
});
}
// Get the database in a browser-safe way
export async function getDB() {
if (typeof window === "undefined") return null;
if (!dbPromise) {
dbPromise = initDB();
}
return dbPromise;
}
// CRUD operations — all SSR-safe
export async function getAllEvents() {
const db = await getDB();
if (!db) return [];
return db.getAll("events");
}
export async function addEvent(event: ICalDB["events"]["value"]) {
const db = await getDB();
if (!db) return;
return db.put("events", event);
}
export async function deleteEvent(id: string) {
const db = await getDB();
if (!db) return;
return db.delete("events", id);
}
export async function clearEvents() {
const db = await getDB();
if (!db) return;
return db.clear("events");
}

62
src/lib/events-db.ts Normal file
View File

@@ -0,0 +1,62 @@
import { openDB, type IDBPDatabase } from 'idb';
import type { CalendarEvent } from '@/lib/types';
const DB_NAME = 'LocalCalEvents';
const DB_VERSION = 1;
const EVENTS_STORE = 'events';
let dbPromise: Promise<IDBPDatabase> | null = null;
function getDB() {
if (!dbPromise) {
dbPromise = openDB(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(EVENTS_STORE)) {
const store = db.createObjectStore(EVENTS_STORE, { keyPath: 'id' });
store.createIndex('start', 'start');
store.createIndex('title', 'title');
}
},
});
}
return dbPromise;
}
export async function saveEvent(event: CalendarEvent): Promise<void> {
const db = await getDB();
await db.put(EVENTS_STORE, event);
}
export async function getEvents(): Promise<CalendarEvent[]> {
const db = await getDB();
return db.getAll(EVENTS_STORE);
}
export async function getEvent(id: string): Promise<CalendarEvent | undefined> {
const db = await getDB();
return db.get(EVENTS_STORE, id);
}
export async function deleteEvent(id: string): Promise<void> {
const db = await getDB();
await db.delete(EVENTS_STORE, id);
}
export async function updateEvent(event: CalendarEvent): Promise<void> {
const db = await getDB();
await db.put(EVENTS_STORE, event);
}
export async function getEventsByDateRange(startDate: string, endDate: string): Promise<CalendarEvent[]> {
const db = await getDB();
const tx = db.transaction(EVENTS_STORE, 'readonly');
const index = tx.store.index('start');
const events = await index.getAll(IDBKeyRange.bound(startDate, endDate));
await tx.done;
return events;
}
export async function clearEvents(): Promise<void> {
const db = await getDB();
await db.clear(EVENTS_STORE);
}