New API + API Token Updates

This commit is contained in:
2026-01-16 13:20:11 -07:00
parent 756ab2a38f
commit 4412229990
26 changed files with 1661 additions and 1012 deletions

View File

@@ -1,135 +1,222 @@
import { sqliteTable, text, integer, primaryKey, foreignKey } from 'drizzle-orm/sqlite-core';
import { nanoid } from 'nanoid';
import {
sqliteTable,
text,
integer,
primaryKey,
foreignKey,
} from "drizzle-orm/sqlite-core";
import { nanoid } from "nanoid";
export const users = sqliteTable('users', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
email: text('email').notNull().unique(),
passwordHash: text('password_hash').notNull(),
name: text('name').notNull(),
isSiteAdmin: integer('is_site_admin', { mode: 'boolean' }).default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
export const users = sqliteTable("users", {
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
email: text("email").notNull().unique(),
passwordHash: text("password_hash").notNull(),
name: text("name").notNull(),
isSiteAdmin: integer("is_site_admin", { mode: "boolean" }).default(false),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
});
export const organizations = sqliteTable('organizations', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
name: text('name').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
export const organizations = sqliteTable("organizations", {
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
});
export const members = sqliteTable('members', {
userId: text('user_id').notNull(),
organizationId: text('organization_id').notNull(),
role: text('role').notNull().default('member'), // 'owner', 'admin', 'member'
joinedAt: integer('joined_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
}, (table: any) => ({
pk: primaryKey({ columns: [table.userId, table.organizationId] }),
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id]
export const members = sqliteTable(
"members",
{
userId: text("user_id").notNull(),
organizationId: text("organization_id").notNull(),
role: text("role").notNull().default("member"), // 'owner', 'admin', 'member'
joinedAt: integer("joined_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
pk: primaryKey({ columns: [table.userId, table.organizationId] }),
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id],
}),
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id],
}),
}),
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id]
})
}));
);
export const clients = sqliteTable('clients', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
organizationId: text('organization_id').notNull(),
name: text('name').notNull(),
email: text('email'),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
}, (table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id]
})
}));
export const categories = sqliteTable('categories', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
organizationId: text('organization_id').notNull(),
name: text('name').notNull(),
color: text('color'),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
}, (table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id]
})
}));
export const timeEntries = sqliteTable('time_entries', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
userId: text('user_id').notNull(),
organizationId: text('organization_id').notNull(),
clientId: text('client_id').notNull(),
categoryId: text('category_id').notNull(),
startTime: integer('start_time', { mode: 'timestamp' }).notNull(),
endTime: integer('end_time', { mode: 'timestamp' }),
description: text('description'),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
}, (table: any) => ({
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id]
export const clients = sqliteTable(
"clients",
{
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
organizationId: text("organization_id").notNull(),
name: text("name").notNull(),
email: text("email"),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id],
}),
}),
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id]
);
export const categories = sqliteTable(
"categories",
{
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
organizationId: text("organization_id").notNull(),
name: text("name").notNull(),
color: text("color"),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id],
}),
}),
clientFk: foreignKey({
columns: [table.clientId],
foreignColumns: [clients.id]
);
export const timeEntries = sqliteTable(
"time_entries",
{
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
userId: text("user_id").notNull(),
organizationId: text("organization_id").notNull(),
clientId: text("client_id").notNull(),
categoryId: text("category_id").notNull(),
startTime: integer("start_time", { mode: "timestamp" }).notNull(),
endTime: integer("end_time", { mode: "timestamp" }),
description: text("description"),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id],
}),
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id],
}),
clientFk: foreignKey({
columns: [table.clientId],
foreignColumns: [clients.id],
}),
categoryFk: foreignKey({
columns: [table.categoryId],
foreignColumns: [categories.id],
}),
}),
categoryFk: foreignKey({
columns: [table.categoryId],
foreignColumns: [categories.id]
})
}));
);
export const tags = sqliteTable('tags', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
organizationId: text('organization_id').notNull(),
name: text('name').notNull(),
color: text('color'),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
}, (table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id]
})
}));
export const timeEntryTags = sqliteTable('time_entry_tags', {
timeEntryId: text('time_entry_id').notNull(),
tagId: text('tag_id').notNull(),
}, (table: any) => ({
pk: primaryKey({ columns: [table.timeEntryId, table.tagId] }),
timeEntryFk: foreignKey({
columns: [table.timeEntryId],
foreignColumns: [timeEntries.id]
export const tags = sqliteTable(
"tags",
{
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
organizationId: text("organization_id").notNull(),
name: text("name").notNull(),
color: text("color"),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
orgFk: foreignKey({
columns: [table.organizationId],
foreignColumns: [organizations.id],
}),
}),
tagFk: foreignKey({
columns: [table.tagId],
foreignColumns: [tags.id]
})
}));
);
export const sessions = sqliteTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id').notNull(),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
}, (table: any) => ({
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id]
})
}));
export const timeEntryTags = sqliteTable(
"time_entry_tags",
{
timeEntryId: text("time_entry_id").notNull(),
tagId: text("tag_id").notNull(),
},
(table: any) => ({
pk: primaryKey({ columns: [table.timeEntryId, table.tagId] }),
timeEntryFk: foreignKey({
columns: [table.timeEntryId],
foreignColumns: [timeEntries.id],
}),
tagFk: foreignKey({
columns: [table.tagId],
foreignColumns: [tags.id],
}),
}),
);
export const siteSettings = sqliteTable('site_settings', {
id: text('id').primaryKey().$defaultFn(() => nanoid()),
key: text('key').notNull().unique(),
value: text('value').notNull(),
updatedAt: integer('updated_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
export const sessions = sqliteTable(
"sessions",
{
id: text("id").primaryKey(),
userId: text("user_id").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
},
(table: any) => ({
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id],
}),
}),
);
export const siteSettings = sqliteTable("site_settings", {
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
key: text("key").notNull().unique(),
value: text("value").notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
});
export const apiTokens = sqliteTable(
"api_tokens",
{
id: text("id")
.primaryKey()
.$defaultFn(() => nanoid()),
userId: text("user_id").notNull(),
name: text("name").notNull(),
token: text("token").notNull().unique(),
scopes: text("scopes").notNull().default("*"),
lastUsedAt: integer("last_used_at", { mode: "timestamp" }),
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
() => new Date(),
),
},
(table: any) => ({
userFk: foreignKey({
columns: [table.userId],
foreignColumns: [users.id],
}),
}),
);