Compare commits

..

1 Commits

Author SHA1 Message Date
60f87472a1 Add task related tables.
Pivoting from "records" to a combination of "tasks", "micro-tasks",
"assets", and a few others.
2025-09-26 17:26:57 -05:00
13 changed files with 149 additions and 186 deletions

1
.gitignore vendored
View File

@ -12,7 +12,6 @@ node_modules
*.log
# Databases
/drizzle
*.db
# OS

View File

@ -6,13 +6,11 @@
"dependencies": {
"better-sqlite3": "^12.4.1",
"drizzle-orm": "^0.44.5",
"drizzle-seed": "^0.3.1",
"pino": "^9.11.0",
},
"devDependencies": {
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.22.0",
"@faker-js/faker": "^10.0.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
@ -111,8 +109,6 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
"@faker-js/faker": ["@faker-js/faker@10.0.0", "", {}, "sha512-UollFEUkVXutsaP+Vndjxar40Gs5JL2HeLcl8xO1QAjJgOdhc3OmBFWyEylS+RddWaaBiAzH+5/17PLQJwDiLw=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
@ -307,8 +303,6 @@
"drizzle-orm": ["drizzle-orm@0.44.5", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ=="],
"drizzle-seed": ["drizzle-seed@0.3.1", "", { "dependencies": { "pure-rand": "^6.1.0" }, "peerDependencies": { "drizzle-orm": ">=0.36.4" }, "optionalPeers": ["drizzle-orm"] }, "sha512-F/0lgvfOAsqlYoHM/QAGut4xXIOXoE5VoAdv2FIl7DpGYVXlAzKuJO+IphkKUFK3Dz+rFlOsQLnMNrvoQ0cx7g=="],
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
@ -513,8 +507,6 @@
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],

View File

@ -0,0 +1,15 @@
CREATE TABLE `records` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`record_id` text NOT NULL,
`type_id` integer NOT NULL,
FOREIGN KEY (`type_id`) REFERENCES `record_types`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE UNIQUE INDEX `records_record_id_unique` ON `records` (`record_id`);--> statement-breakpoint
CREATE INDEX `chores_index` ON `records` (`record_id`) WHERE type_id = 1;--> statement-breakpoint
CREATE INDEX `project_index` ON `records` (`record_id`) WHERE type_id = 2;--> statement-breakpoint
CREATE INDEX `ticket_index` ON `records` (`record_id`) WHERE type_id = 3;--> statement-breakpoint
CREATE TABLE `record_types` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`type` text
);

View File

@ -0,0 +1,119 @@
{
"version": "6",
"dialect": "sqlite",
"id": "feb276b9-6e2b-4204-885b-167701a4a707",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"records": {
"name": "records",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"record_id": {
"name": "record_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type_id": {
"name": "type_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"records_record_id_unique": {
"name": "records_record_id_unique",
"columns": [
"record_id"
],
"isUnique": true
},
"chores_index": {
"name": "chores_index",
"columns": [
"record_id"
],
"isUnique": false,
"where": "type_id = 1"
},
"project_index": {
"name": "project_index",
"columns": [
"record_id"
],
"isUnique": false,
"where": "type_id = 2"
},
"ticket_index": {
"name": "ticket_index",
"columns": [
"record_id"
],
"isUnique": false,
"where": "type_id = 3"
}
},
"foreignKeys": {
"records_type_id_record_types_id_fk": {
"name": "records_type_id_record_types_id_fk",
"tableFrom": "records",
"tableTo": "record_types",
"columnsFrom": [
"type_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"record_types": {
"name": "record_types",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1758833816361,
"tag": "0000_wandering_riptide",
"breakpoints": true
}
]
}

View File

@ -15,7 +15,6 @@
"devDependencies": {
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.22.0",
"@faker-js/faker": "^10.0.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
@ -36,7 +35,6 @@
"dependencies": {
"better-sqlite3": "^12.4.1",
"drizzle-orm": "^0.44.5",
"drizzle-seed": "^0.3.1",
"pino": "^9.11.0"
}
}

View File

@ -6,4 +6,3 @@ import { drizzle } from "drizzle-orm/bun-sqlite";
const sqlite = new Database("sqlite.db");
export const db = drizzle({ client: sqlite, schema: { ...schema1, ...schema2, ...schema3 } });
export type DB = typeof db;

View File

@ -7,7 +7,7 @@ export type TaskIntegrationConfigs = {
commits: Array<{
hash: string;
message: string;
description?: string;
description: string;
}>;
}>;
};
@ -41,7 +41,7 @@ export const tasks = sqliteTable("tasks", {
{
enabled: boolean;
entries: Array<{
group?: string;
group: string;
sort_order: number;
checked: boolean;
text: string;

View File

@ -11,14 +11,12 @@ const loggerConfig: Record<string, pino.LoggerOptions> = {
transport: {
targets: [
{
level: "debug",
target: "pino-pretty",
options: {
colorize: true,
},
},
{
level: "debug",
target: "pino/file",
options: {
destination: "./logs/dev.log",

View File

@ -1,29 +0,0 @@
import { type DB, db } from "$lib/server/db/db";
import logger from "../logger";
class TasksService {
private db: DB;
private caller: "internal" | "api";
constructor(caller: "internal" | "api", dbClient: DB = db) {
this.db = dbClient;
this.caller = caller;
}
public async getAll() {
logger.info("Fetching all task records...");
try {
const result = await this.db.query.tasks.findMany({
with: {
type: true,
},
});
logger.debug(`Found ${result.length} records.`);
return result;
} catch (e) {
logger.error({ msg: "Error querying the database.", error: e });
return false as const;
}
}
}
export default TasksService;

View File

@ -1,10 +0,0 @@
import TasksService from "$lib/server/services/tasks";
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
const tasks = new TasksService("internal");
return {
tasks: await tasks.getAll(),
test: "string",
};
};

View File

@ -1,32 +0,0 @@
<script lang="ts">
import type { PageProps } from "./$types";
let { data }: PageProps = $props();
</script>
{#if data.tasks}
<p>{data.tasks.length} total records.</p>
<table>
<thead>
<tr>
<td><strong>Id</strong></td>
<td><strong>Description</strong></td>
<td><strong>Status</strong></td>
</tr>
</thead>
<tbody>
{#each data.tasks as task (task.id)}
<tr>
<td
>{task.type?.prefix +
task.taskId}</td
>
<td>{task.description}</td>
<td>{task.status}</td>
</tr>
{/each}
</tbody>
</table>
{:else}
<p>There was an error accessing the database.</p>
{/if}

View File

@ -1,99 +0,0 @@
import { db } from "../src/lib/server/db/db";
import { tasks, taskTypes } from "../src/lib/server/db/schema/tasks";
import { faker } from "@faker-js/faker";
import logger from "../src/lib/server/logger";
async function resetDatabase() {
logger.info("Resetting database...");
// Delete in reverse order of creation to respect foreign key constraints
await db.delete(tasks);
await db.delete(taskTypes);
logger.info("Database reset complete.");
}
async function seedDatabase(count: number) {
logger.info(`Seeding database with ${count} tasks...`);
// 1. Seed Task Types
const insertedTaskTypes = await db.insert(taskTypes).values([
{ name: "Product Change", prefix: "PC" },
{ name: "Task", prefix: "TA" },
]).returning();
logger.info(`Seeded ${insertedTaskTypes.length} task types.`);
if (insertedTaskTypes.length === 0) {
throw new Error("Task type seeding failed. Cannot seed tasks.");
}
// 2. Seed Tasks
const newTasks = [];
const usedTaskIds = new Set<number>();
for (let i = 0; i < count; i++) {
let randomId: number;
do {
randomId = Math.floor(100000 + Math.random() * 900000);
} while (usedTaskIds.has(randomId));
usedTaskIds.add(randomId);
newTasks.push({
// task_id with a guaranteed unique 6 digit number
taskId: randomId.toString(),
// a random phrase in each description
description: faker.lorem.sentence(),
// open_date with a valid date
openDate: faker.date.recent({ days: 30 }).toISOString(),
// a random paragraph in body
body: faker.lorem.paragraphs(3),
// other fields as needed
type: faker.helpers.arrayElement(insertedTaskTypes).id,
status: faker.helpers.arrayElement(["Todo", "In Progress", "Done", "Canceled"]),
priority: faker.helpers.arrayElement(["Low", "Medium", "High", "Urgent"]),
});
}
// 3. Insert the new tasks in chunks to avoid exceeding driver limits
logger.info(`Inserting ${newTasks.length} tasks in chunks...`);
const chunkSize = 500;
let totalInserted = 0;
for (let i = 0; i < newTasks.length; i += chunkSize) {
const chunk = newTasks.slice(i, i + chunkSize);
const inserted = await db.insert(tasks).values(chunk).returning();
totalInserted += inserted.length;
logger.info(` ... inserted chunk ${Math.floor(i / chunkSize) + 1}, ${totalInserted}/${newTasks.length} tasks`);
}
logger.info(`Seeding complete. Total tasks inserted: ${totalInserted}.`);
}
async function main() {
const mode = process.argv[2];
// Default count if no flag is provided
let count = 20;
// Find and parse the --count flag
const countArg = process.argv.find((arg) => arg.startsWith("--count="));
if (countArg) {
const countValue = parseInt(countArg.split("=")[1], 10);
if (!isNaN(countValue) && countValue > 0) {
count = countValue;
}
}
if (mode === "reset") {
await resetDatabase();
} else if (mode === "seed") {
// Seeding implies a reset first for a clean slate
await resetDatabase();
await seedDatabase(count);
} else {
logger.error('Invalid mode. Use "seed" or "reset".');
process.exit(1);
}
logger.info("Script finished.");
}
main().catch((e) => {
logger.error(e, "An error occurred during the script execution.");
process.exit(1);
});