first
This commit is contained in:
34
src/db.ts
Normal file
34
src/db.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Database } from "bun:sqlite";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const DB_DIR = join(process.cwd(), "data");
|
||||
|
||||
// Ensure data directory exists
|
||||
try {
|
||||
mkdirSync(DB_DIR, { recursive: true });
|
||||
} catch (e) {
|
||||
// Ignore if exists
|
||||
}
|
||||
|
||||
const db = new Database(join(DB_DIR, "safe-marks.db"));
|
||||
|
||||
export function initDB() {
|
||||
console.log("Creating database and tables...");
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS extensions (
|
||||
ID TEXT PRIMARY KEY,
|
||||
NAME TEXT NOT NULL,
|
||||
SAFE INTEGER NOT NULL,
|
||||
UPDATE_DATE TEXT NOT NULL,
|
||||
NOTES TEXT
|
||||
)
|
||||
`);
|
||||
// Create index on ID if needed, though PRIMARY KEY implies unique index usually.
|
||||
// The Python code had index=True on ID.
|
||||
db.run(`CREATE INDEX IF NOT EXISTS idx_id ON extensions (ID)`);
|
||||
|
||||
console.log("Database and tables created successfully (if they didn't exist).");
|
||||
}
|
||||
|
||||
export default db;
|
||||
191
src/index.ts
Normal file
191
src/index.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { Hono } from "hono";
|
||||
import db, { initDB } from "./db";
|
||||
import {
|
||||
SafeStatus,
|
||||
type ExtensionCreate,
|
||||
type ExtensionBatchCreateItem,
|
||||
type ExtensionUpdatePayload,
|
||||
type ExtensionInDB,
|
||||
type ExtensionNecessary,
|
||||
} from "./models";
|
||||
|
||||
// Initialize Database
|
||||
initDB();
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Helper to get extension by ID
|
||||
function getExtensionById(id: string): ExtensionInDB | null {
|
||||
const query = db.query("SELECT * FROM extensions WHERE ID = $id");
|
||||
return query.get({ $id: id }) as ExtensionInDB | null;
|
||||
}
|
||||
|
||||
// --- API Routes ---
|
||||
|
||||
app.get("/api/v1/ext/query_all", (c) => {
|
||||
const query = db.query("SELECT * FROM extensions");
|
||||
const extensions = query.all() as ExtensionInDB[];
|
||||
return c.json(extensions);
|
||||
});
|
||||
|
||||
app.get("/api/v1/ext/query_necessary", (c) => {
|
||||
// Python code fetches all columns then filters via Pydantic model.
|
||||
// We can select just what we need or select all and map.
|
||||
// Let's select all to be consistent with the 'all()' behavior if logic changes,
|
||||
// but map to the response shape to respect the 'response_model'.
|
||||
const query = db.query("SELECT * FROM extensions");
|
||||
const extensions = query.all() as ExtensionInDB[];
|
||||
|
||||
const necessary: ExtensionNecessary[] = extensions.map((ext) => ({
|
||||
ID: ext.ID,
|
||||
SAFE: ext.SAFE,
|
||||
}));
|
||||
|
||||
return c.json(necessary);
|
||||
});
|
||||
|
||||
app.post("/api/v1/ext/add_one", async (c) => {
|
||||
const body = await c.req.json<ExtensionCreate>();
|
||||
|
||||
// Validation (basic, assuming valid types passed)
|
||||
if (!body.ID || !body.NAME || body.SAFE === undefined) {
|
||||
// Python throws 422 for validation errors usually, checking manual checks here
|
||||
}
|
||||
|
||||
if (getExtensionById(body.ID)) {
|
||||
return c.json({ detail: `Extension with ID ${body.ID} already exists.` }, 400);
|
||||
}
|
||||
|
||||
const currentDate = new Date().toISOString();
|
||||
const newExtension: ExtensionInDB = {
|
||||
ID: body.ID,
|
||||
NAME: body.NAME,
|
||||
NOTES: body.NOTES ?? null,
|
||||
SAFE: body.SAFE,
|
||||
UPDATE_DATE: currentDate,
|
||||
};
|
||||
|
||||
const insert = db.query(`
|
||||
INSERT INTO extensions (ID, NAME, SAFE, UPDATE_DATE, NOTES)
|
||||
VALUES ($ID, $NAME, $SAFE, $UPDATE_DATE, $NOTES)
|
||||
`);
|
||||
|
||||
insert.run({
|
||||
$ID: newExtension.ID,
|
||||
$NAME: newExtension.NAME,
|
||||
$SAFE: newExtension.SAFE,
|
||||
$UPDATE_DATE: newExtension.UPDATE_DATE,
|
||||
$NOTES: newExtension.NOTES,
|
||||
});
|
||||
|
||||
return c.json(newExtension);
|
||||
});
|
||||
|
||||
app.post("/api/v1/ext/add_batch", async (c) => {
|
||||
const items = await c.req.json<ExtensionBatchCreateItem[]>();
|
||||
const createdExtensions: ExtensionInDB[] = [];
|
||||
const currentDate = new Date().toISOString();
|
||||
|
||||
const insert = db.query(`
|
||||
INSERT INTO extensions (ID, NAME, SAFE, UPDATE_DATE, NOTES)
|
||||
VALUES ($ID, $NAME, $SAFE, $UPDATE_DATE, $NOTES)
|
||||
`);
|
||||
|
||||
// Use a transaction for batch insert
|
||||
const insertTransaction = db.transaction((itemsToInsert: ExtensionBatchCreateItem[]) => {
|
||||
for (const item of itemsToInsert) {
|
||||
if (getExtensionById(item.ID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newExtension: ExtensionInDB = {
|
||||
ID: item.ID,
|
||||
NAME: item.NAME,
|
||||
NOTES: item.NOTES ?? null,
|
||||
SAFE: SafeStatus.unknown,
|
||||
UPDATE_DATE: currentDate,
|
||||
};
|
||||
|
||||
insert.run({
|
||||
$ID: newExtension.ID,
|
||||
$NAME: newExtension.NAME,
|
||||
$SAFE: newExtension.SAFE,
|
||||
$UPDATE_DATE: newExtension.UPDATE_DATE,
|
||||
$NOTES: newExtension.NOTES,
|
||||
});
|
||||
|
||||
createdExtensions.push(newExtension);
|
||||
}
|
||||
});
|
||||
|
||||
insertTransaction(items);
|
||||
|
||||
return c.json(createdExtensions);
|
||||
});
|
||||
|
||||
app.put("/api/v1/ext/update_one/:item_id", async (c) => {
|
||||
const itemId = c.req.param("item_id");
|
||||
const updateData = await c.req.json<ExtensionUpdatePayload>();
|
||||
|
||||
const currentExt = getExtensionById(itemId);
|
||||
|
||||
if (!currentExt) {
|
||||
return c.json({ detail: `Extension with ID ${itemId} not found.` }, 404);
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length === 0) {
|
||||
return c.json({ detail: "No update data provided." }, 400);
|
||||
}
|
||||
|
||||
let updated = false;
|
||||
const nextExt = { ...currentExt };
|
||||
|
||||
if (updateData.NAME !== undefined && updateData.NAME !== null && updateData.NAME !== currentExt.NAME) {
|
||||
nextExt.NAME = updateData.NAME;
|
||||
updated = true;
|
||||
}
|
||||
if (updateData.SAFE !== undefined && updateData.SAFE !== null && updateData.SAFE !== currentExt.SAFE) {
|
||||
nextExt.SAFE = updateData.SAFE;
|
||||
updated = true;
|
||||
}
|
||||
if (updateData.NOTES !== undefined && updateData.NOTES !== null && updateData.NOTES !== currentExt.NOTES) {
|
||||
nextExt.NOTES = updateData.NOTES;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
nextExt.UPDATE_DATE = new Date().toISOString();
|
||||
|
||||
const updateQuery = db.query(`
|
||||
UPDATE extensions
|
||||
SET NAME = $NAME, SAFE = $SAFE, NOTES = $NOTES, UPDATE_DATE = $UPDATE_DATE
|
||||
WHERE ID = $ID
|
||||
`);
|
||||
|
||||
updateQuery.run({
|
||||
$NAME: nextExt.NAME,
|
||||
$SAFE: nextExt.SAFE,
|
||||
$NOTES: nextExt.NOTES,
|
||||
$UPDATE_DATE: nextExt.UPDATE_DATE,
|
||||
$ID: nextExt.ID,
|
||||
});
|
||||
}
|
||||
|
||||
return c.json(nextExt);
|
||||
});
|
||||
|
||||
app.delete("/api/v1/ext/delete_one/:item_id", (c) => {
|
||||
const itemId = c.req.param("item_id");
|
||||
const currentExt = getExtensionById(itemId);
|
||||
|
||||
if (!currentExt) {
|
||||
return c.json({ detail: `Extension with ID ${itemId} not found.` }, 404);
|
||||
}
|
||||
|
||||
const deleteQuery = db.query("DELETE FROM extensions WHERE ID = $id");
|
||||
deleteQuery.run({ $id: itemId });
|
||||
|
||||
return c.json({ message: `Extension with ID ${itemId} successfully deleted.` });
|
||||
});
|
||||
|
||||
export default app;
|
||||
34
src/models.ts
Normal file
34
src/models.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export enum SafeStatus {
|
||||
safe = 1,
|
||||
unsure = 0,
|
||||
unsafe = -1,
|
||||
unknown = -2,
|
||||
}
|
||||
|
||||
export interface ExtensionBase {
|
||||
ID: string;
|
||||
NAME: string;
|
||||
NOTES?: string | null;
|
||||
}
|
||||
|
||||
export interface ExtensionCreate extends ExtensionBase {
|
||||
SAFE: SafeStatus;
|
||||
}
|
||||
|
||||
export interface ExtensionBatchCreateItem extends ExtensionBase {}
|
||||
|
||||
export interface ExtensionUpdatePayload {
|
||||
NAME?: string | null;
|
||||
SAFE?: SafeStatus | null;
|
||||
NOTES?: string | null;
|
||||
}
|
||||
|
||||
export interface ExtensionInDB extends ExtensionBase {
|
||||
SAFE: SafeStatus;
|
||||
UPDATE_DATE: string;
|
||||
}
|
||||
|
||||
export interface ExtensionNecessary {
|
||||
ID: string;
|
||||
SAFE: SafeStatus;
|
||||
}
|
||||
Reference in New Issue
Block a user