init backend

This commit is contained in:
2026-01-10 22:26:16 +01:00
commit 02211eb0be
11 changed files with 329 additions and 0 deletions

21
src/embeddings.ts Normal file
View File

@@ -0,0 +1,21 @@
import type { Card } from "./types";
// Placeholder cards array
let cards: Card[] = [
{ id: "swsh1-1", name: "Celebi V", set: "Swsh1", number: "1", imageUrl: "https://assets.tcgdex.net/de/swsh/swsh1/1/high.png" },
{ id: "swsh12-001", name: "Bluzuk", set: "Swsh12", number: "001", imageUrl: "https://assets.tcgdex.net/de/swsh/swsh12/001/high.png" },
];
export function loadCards() {
// Placeholder: you can later load embeddings.npy + FAISS
console.log("Cards module loaded (currently empty)");
}
export function queryCardById(id: string): Card | null {
return cards.find(c => c.id === id) || null;
}
// Example placeholder: return top N matches
export function queryCardByEmbedding(/* embedding */): Card[] {
return cards.slice(0, 5); // dummy top 5
}

64
src/index.ts Normal file
View File

@@ -0,0 +1,64 @@
import { serve, spawn } from 'bun';
import { queryCardByEmbedding, queryCardById } from './embeddings';
import type { Card } from './types';
const PYTHON_SERVICE = "http://localhost:5001/query";
const server = serve({
routes: {
"/api/cards/query-image": {
async POST(req) {
try {
const formData = await req.formData();
const file = formData.get("file") as File;
if (!file) return new Response(JSON.stringify({ error: "No file uploaded" }), { status: 400 });
// Forward directly to Python microservice
const body = new FormData();
body.append("file", file);
const resp = await fetch(PYTHON_SERVICE, { method: "POST", body });
const json = await resp.json();
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
} catch (err) {
console.error(err);
return new Response(JSON.stringify({ error: "Failed to query image" }), { status: 500 });
}
},
},
"/api/cards/:id": async (req) => {
const { id } = req.params;
const card = queryCardById(id);
if (!card) return new Response(JSON.stringify({ error: "Card not found" }), { status: 404, headers: { "Content-Type": "application/json" } });
return new Response(JSON.stringify(card), { headers: { "Content-Type": "application/json" } });
},
"/*": async () => {
return new Response("<h1>Pokemon Card Backend</h1>", { headers: { "Content-Type": "text/html" } });
},
/* "/api/cards/query": {
async POST(req) {
try {
const { embedding } = await req.json() as { embedding: number[] };
if (!embedding) return new Response(JSON.stringify({ error: "Missing embedding" }), { status: 400, headers: { "Content-Type": "application/json" } });
const results: Card[] = queryCardByEmbedding(embedding);
return new Response(JSON.stringify(results), { headers: { "Content-Type": "application/json" } });
} catch (err) {
console.error("Error querying card:", err);
return new Response(JSON.stringify({ error: "Failed to query card" }), { status: 500, headers: { "Content-Type": "application/json" } });
}
},
}, */
},
development: process.env.NODE_ENV !== 'production' && {
// Enable browser hot reloading in development
hmr: true,
// Echo console logs from the browser to the server
console: true,
},
});
console.log(`🚀 Server running at ${server.url}`);

10
src/types.ts Normal file
View File

@@ -0,0 +1,10 @@
export interface Card {
id: string;
name: string;
set: string;
number: string;
rarity?: string;
variant?: string;
foil?: boolean;
imageUrl: string;
}