add: endpoint /timeline & /frame/:id in backend server
improve: simplify the migration code
This commit is contained in:
parent
fb70acab00
commit
f53616f345
@ -12,26 +12,23 @@ function migrateTo(version: number, db: Database) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVersion(db: Database): number {
|
||||||
|
const stmt = db.prepare(`SELECT value FROM config WHERE key = 'version';`);
|
||||||
|
const data = stmt.get() as { value: string };
|
||||||
|
const version = data.value;
|
||||||
|
return parseInt(version);
|
||||||
|
}
|
||||||
|
|
||||||
export function migrate(db: Database) {
|
export function migrate(db: Database) {
|
||||||
const configTableExists =
|
const configTableExists =
|
||||||
db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='config';`).get()
|
db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='config';`).get() !==
|
||||||
!== undefined;
|
undefined;
|
||||||
if (!configTableExists) {
|
if (!configTableExists) {
|
||||||
migrateToV2(db);
|
migrateToV2(db);
|
||||||
}
|
}
|
||||||
let databaseVersion = parseInt(
|
let databaseVersion = getVersion(db);
|
||||||
(
|
|
||||||
db.prepare(`SELECT value FROM config WHERE key = 'version';`).get() as
|
|
||||||
{ value: any }
|
|
||||||
).value
|
|
||||||
);
|
|
||||||
while (databaseVersion < CURRENT_VERSION) {
|
while (databaseVersion < CURRENT_VERSION) {
|
||||||
migrateTo(databaseVersion, db);
|
migrateTo(databaseVersion, db);
|
||||||
databaseVersion = parseInt(
|
databaseVersion = getVersion(db);
|
||||||
(
|
|
||||||
db.prepare(`SELECT value FROM config WHERE key = 'version';`).get() as
|
|
||||||
{ value: any }
|
|
||||||
).value
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -96,6 +96,7 @@ app.on("ready", () => {
|
|||||||
setInterval(processEncodingTasks, 10000, db);
|
setInterval(processEncodingTasks, 10000, db);
|
||||||
setInterval(deleteEncodedScreenshots, 5000, db);
|
setInterval(deleteEncodedScreenshots, 5000, db);
|
||||||
dbConnection = db;
|
dbConnection = db;
|
||||||
|
cache.put("server:dbConnection", dbConnection);
|
||||||
});
|
});
|
||||||
mainWindow = createMainWindow(port, () => (mainWindow = null));
|
mainWindow = createMainWindow(port, () => (mainWindow = null));
|
||||||
settingsWindow = createSettingsWindow(port, () => (settingsWindow = null));
|
settingsWindow = createSettingsWindow(port, () => (settingsWindow = null));
|
||||||
@ -107,6 +108,8 @@ app.on("ready", () => {
|
|||||||
|
|
||||||
app.on("will-quit", () => {
|
app.on("will-quit", () => {
|
||||||
dbConnection?.close();
|
dbConnection?.close();
|
||||||
|
if (screenshotInterval)
|
||||||
|
clearInterval(screenshotInterval);
|
||||||
});
|
});
|
||||||
|
|
||||||
// app.on("window-all-closed", () => {
|
// app.on("window-all-closed", () => {
|
||||||
|
@ -1,17 +1,75 @@
|
|||||||
import { Hono } from 'hono'
|
import { Hono } from "hono";
|
||||||
import cache from "memory-cache";
|
import cache from "memory-cache";
|
||||||
|
import { join } from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import { Database } from "better-sqlite3";
|
||||||
|
import type { Frame } from "../backend/schema";
|
||||||
|
import { getScreenshotsDir } from "../utils/backend.js";
|
||||||
|
|
||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
|
|
||||||
app.use(async (c, next) => {
|
app.use(async (c, next) => {
|
||||||
const key = cache.get("server:APIKey");
|
const key = cache.get("server:APIKey");
|
||||||
if (key && c.req.header("x-api-key") !== key) {
|
if (key && c.req.header("x-api-key") !== key) {
|
||||||
c.res = undefined
|
c.res = undefined;
|
||||||
c.res = c.json({ error: "Invalid API key" }, 401);
|
c.res = c.json({ error: "Invalid API key" }, 401);
|
||||||
}
|
}
|
||||||
await next();
|
await next();
|
||||||
})
|
});
|
||||||
|
|
||||||
app.get('/ping', (c) => c.text('pong'))
|
app.get("/ping", (c) => c.text("pong"));
|
||||||
|
|
||||||
|
app.get("/timeline", async (c) => {
|
||||||
|
const { offset = 0, limit = 50 } = c.req.query();
|
||||||
|
const db = cache.get("server:dbConnection");
|
||||||
|
|
||||||
|
const frames = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT id, createdAt, imgFilename, videoPath, videoFrameIndex
|
||||||
|
FROM frame
|
||||||
|
ORDER BY createdAt DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.all(limit, offset);
|
||||||
|
|
||||||
|
return c.json(frames);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/frame/:id", async (c) => {
|
||||||
|
const { id } = c.req.param();
|
||||||
|
const db: Database = cache.get("server:dbConnection");
|
||||||
|
|
||||||
|
const frame = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT imgFilename, videoPath, videoFrameIndex
|
||||||
|
FROM frame
|
||||||
|
WHERE id = ?
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.get(id) as Frame;
|
||||||
|
|
||||||
|
if (!frame) {
|
||||||
|
return c.json({ error: "Frame not found" }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If frame is from video, decode and return frame
|
||||||
|
if (frame.videoPath) {
|
||||||
|
// TODO: Implement video frame extraction
|
||||||
|
return c.json({ error: "Video frame extraction not implemented" }, 501);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return image file
|
||||||
|
const imagePath = join(getScreenshotsDir(), frame.imgFilename);
|
||||||
|
const imageBuffer = fs.readFileSync(imagePath);
|
||||||
|
return new Response(imageBuffer, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "image/png"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default app;
|
export default app;
|
Loading…
Reference in New Issue
Block a user