Make backend and frontend work together well, and add datasets/ page to frontend
This commit is contained in:
parent
6f551a83bf
commit
50b670916b
8 changed files with 107 additions and 5 deletions
|
|
@ -37,7 +37,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
get(Html(GraphiQLSource::build().endpoint("/graphql").finish())),
|
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);
|
println!("listening on http://{}", addr);
|
||||||
|
|
||||||
axum::serve(
|
axum::serve(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:Z
|
- ./db/initdb:/docker-entrypoint-initdb.d
|
||||||
- postgres_data:/var/lib/postgresql/data:Z
|
- postgres_data:/var/lib/postgresql/data:Z
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|
|
||||||
20
db/initdb/init.sql
Normal file
20
db/initdb/init.sql
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/kit": "^2.49.1",
|
"@sveltejs/kit": "^2.49.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
"@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-adapter-bun": "^1.0.1",
|
||||||
"svelte-check": "^4.3.4",
|
"svelte-check": "^4.3.4",
|
||||||
"typescript": "^5.9.3",
|
"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=="],
|
"@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/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@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=="],
|
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
|
|
||||||
"ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="],
|
"ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="],
|
||||||
|
|
@ -186,6 +191,8 @@
|
||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
"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=="],
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/kit": "^2.49.2",
|
"@sveltejs/kit": "^2.49.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||||
|
"@types/bun": "^1.3.5",
|
||||||
"svelte": "^5.46.0",
|
"svelte": "^5.46.0",
|
||||||
"svelte-adapter-bun": "^1.0.1",
|
"svelte-adapter-bun": "^1.0.1",
|
||||||
"svelte-check": "^4.3.4",
|
"svelte-check": "^4.3.4",
|
||||||
|
|
|
||||||
49
frontend/src/lib/server/backend.ts
Normal file
49
frontend/src/lib/server/backend.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
type GraphQLResponse<T> = {
|
||||||
|
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<Dataset[]> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
9
frontend/src/routes/datasets/+page.server.ts
Normal file
9
frontend/src/routes/datasets/+page.server.ts
Normal file
|
|
@ -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
|
||||||
|
};
|
||||||
|
};
|
||||||
14
frontend/src/routes/datasets/+page.svelte
Normal file
14
frontend/src/routes/datasets/+page.svelte
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageProps } from "./$types";
|
||||||
|
|
||||||
|
let { data }: PageProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{#each data.datasets as dataset}
|
||||||
|
<li>
|
||||||
|
<strong>{dataset.name}</strong><br />
|
||||||
|
{dataset.description}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue