From b0f8d2c6c82f9a991af315ff1c22f7b0c789a2b4 Mon Sep 17 00:00:00 2001 From: Zeph Levy <171337931+ZephLevy@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:23:43 +0100 Subject: [PATCH] Make backend and frontend work together well, and add datasets/ page to frontend --- backend/src/main.rs | 2 +- compose.yml | 2 +- db/initdb/init.sql | 20 ++++++++ frontend/bun.lock | 15 ++++-- frontend/package.json | 1 + frontend/src/lib/server/backend.ts | 49 ++++++++++++++++++++ frontend/src/routes/datasets/+page.server.ts | 9 ++++ frontend/src/routes/datasets/+page.svelte | 14 ++++++ 8 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 db/initdb/init.sql create mode 100644 frontend/src/lib/server/backend.ts create mode 100644 frontend/src/routes/datasets/+page.server.ts create mode 100644 frontend/src/routes/datasets/+page.svelte diff --git a/backend/src/main.rs b/backend/src/main.rs index a6747e5..8673104 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -37,7 +37,7 @@ async fn main() -> anyhow::Result<()> { get(Html(GraphiQLSource::build().endpoint("/graphql").finish())), ); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); println!("listening on http://{}", addr); axum::serve( diff --git a/compose.yml b/compose.yml index b550a34..b0ef60b 100644 --- a/compose.yml +++ b/compose.yml @@ -7,7 +7,7 @@ services: ports: - "5432:5432" volumes: - - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:Z + - ./db/initdb:/docker-entrypoint-initdb.d - postgres_data:/var/lib/postgresql/data:Z restart: unless-stopped diff --git a/db/initdb/init.sql b/db/initdb/init.sql new file mode 100644 index 0000000..de6a8ec --- /dev/null +++ b/db/initdb/init.sql @@ -0,0 +1,20 @@ +CREATE TABLE datasets ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + description TEXT +); + +CREATE TABLE locations ( + id SERIAL PRIMARY KEY, + name TEXT, + lat DOUBLE PRECISION NOT NULL, + lon DOUBLE PRECISION NOT NULL +); + +CREATE TABLE records ( + id SERIAL PRIMARY KEY, + dataset_id INTEGER NOT NULL REFERENCES datasets(id), + location_id INTEGER REFERENCES locations(id), + timestamp TIMESTAMPTZ NOT NULL DEFAULT now(), + data JSONB +); diff --git a/frontend/bun.lock b/frontend/bun.lock index 46b6ec9..c08aa4f 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -5,13 +5,14 @@ "": { "name": "frontend", "devDependencies": { - "@sveltejs/kit": "^2.49.1", + "@sveltejs/kit": "^2.49.2", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "svelte": "^5.45.6", + "@types/bun": "^1.3.5", + "svelte": "^5.46.0", "svelte-adapter-bun": "^1.0.1", "svelte-check": "^4.3.4", "typescript": "^5.9.3", - "vite": "^7.2.6", + "vite": "^7.3.0", }, }, }, @@ -174,10 +175,14 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/node": ["@types/node@25.0.8", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], @@ -186,6 +191,8 @@ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], @@ -258,6 +265,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], diff --git a/frontend/package.json b/frontend/package.json index 82e78f6..753af33 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@sveltejs/kit": "^2.49.2", "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@types/bun": "^1.3.5", "svelte": "^5.46.0", "svelte-adapter-bun": "^1.0.1", "svelte-check": "^4.3.4", diff --git a/frontend/src/lib/server/backend.ts b/frontend/src/lib/server/backend.ts new file mode 100644 index 0000000..8c61c2d --- /dev/null +++ b/frontend/src/lib/server/backend.ts @@ -0,0 +1,49 @@ +type GraphQLResponse = { + data?: T; + errors?: Array<{ + message: string; + locations?: { line: number; column: number }[]; + path?: string[]; + }>; +}; + +export type Dataset = { + id: number; + name: string; + description: string; +}; + +export async function list_datasets(): Promise { + const query = ` + query { + datasets { + id + name + description + } + } + `; + + const response = await fetch('http://host.containers.internal:3000/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ query }), + }); + + if (!response.ok) { + throw new Error(`Error - status ${response.status}`); + } + + const result = await response.json() as GraphQLResponse<{ + datasets: Dataset[]; + }>; + + if (result.errors) { + throw new Error(result.errors.map(e => e.message).join(', ')); + } + + return result.data!.datasets; +} + diff --git a/frontend/src/routes/datasets/+page.server.ts b/frontend/src/routes/datasets/+page.server.ts new file mode 100644 index 0000000..0bf9b4d --- /dev/null +++ b/frontend/src/routes/datasets/+page.server.ts @@ -0,0 +1,9 @@ +import * as backend from '$lib/server/backend'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ params }) => { + const datasets: backend.Dataset[] = await backend.list_datasets() + return { + datasets + }; +}; diff --git a/frontend/src/routes/datasets/+page.svelte b/frontend/src/routes/datasets/+page.svelte new file mode 100644 index 0000000..764460d --- /dev/null +++ b/frontend/src/routes/datasets/+page.svelte @@ -0,0 +1,14 @@ + + +