diff --git a/back/.gitignore b/back/.gitignore index ea8c4bf..2955c66 100644 --- a/back/.gitignore +++ b/back/.gitignore @@ -1 +1,2 @@ /target +db.sqlite diff --git a/back/Cargo.lock b/back/Cargo.lock index 5170871..3f07dc3 100644 --- a/back/Cargo.lock +++ b/back/Cargo.lock @@ -477,6 +477,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "hex" version = "0.4.3" @@ -734,6 +740,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -781,6 +797,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.1" @@ -796,6 +822,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.3" @@ -904,7 +936,10 @@ name = "plantback" version = "0.1.0" dependencies = [ "axum", + "serde", "sqlx", + "tokio", + "tracing-subscriber", ] [[package]] @@ -1111,6 +1146,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signature" version = "2.2.0" @@ -1451,6 +1495,16 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -1476,6 +1530,7 @@ dependencies = [ "bytes", "libc", "mio", + "num_cpus", "pin-project-lite", "socket2", "tokio-macros", @@ -1562,6 +1617,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -1626,6 +1707,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1660,6 +1747,28 @@ dependencies = [ "wasite", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/back/Cargo.toml b/back/Cargo.toml index 08a2659..ced5ab8 100644 --- a/back/Cargo.toml +++ b/back/Cargo.toml @@ -5,4 +5,7 @@ edition = "2021" [dependencies] axum = "0.7.5" +serde = "1.0.204" sqlx = { version = "0.7.4", features = ["runtime-tokio", "sqlite"] } +tokio = { version = "1.38.1", features = ["rt-multi-thread", "macros", "net", "time", "sync"] } +tracing-subscriber = "0.3.18" diff --git a/back/src/main.rs b/back/src/main.rs index e7a11a9..817d936 100644 --- a/back/src/main.rs +++ b/back/src/main.rs @@ -1,3 +1,76 @@ -fn main() { - println!("Hello, world!"); +use std::str::FromStr; + +use axum::{ + extract::State, http::StatusCode, routing::post, Json, Router +}; + +use sqlx::{Row, sqlite::{SqliteConnectOptions, SqlitePoolOptions}, Pool, Sqlite}; + +#[derive(Clone)] +struct ST { + db: Pool +} + +#[tokio::main] +async fn main() { + const ADDR : &str = "localhost:3013"; + const DB_ADDR : &str = "sqlite://./db.sqlite"; + + tracing_subscriber::fmt::init(); + + let listener = tokio::net::TcpListener::bind(ADDR) + .await.expect(&format!("Could not bind on {ADDR}")); + + println!("Listening on http://{ADDR}"); + + let opts = SqliteConnectOptions::from_str(DB_ADDR) + .expect("Could not parse database address") + .create_if_missing(true); + + let db = SqlitePoolOptions::new() + .max_connections(5) + .connect_with(opts) + .await.expect("Could not open database"); + + println!("Opened sqlite database on {DB_ADDR}"); + + sqlx::query(r#" + CREATE TABLE IF NOT EXISTS Counter ( + id INTEGER PRIMARY KEY check (id = 0), + value INTEGER NOT NULL + ); + INSERT OR IGNORE INTO Counter (id, value) VALUES (0, 0); + "#).execute(&db).await.expect("Could not initialize database"); + + let state : ST = ST { db }; + + let app = Router::new() + .route("/api/echo", post(echo)) + .route("/api/get_counter", post(get_counter)) + .route("/api/set_counter", post(set_counter)) + .with_state(state); + + axum::serve(listener, app) + .await.expect("Unable to start app"); +} + +async fn get_counter(State(ST { db }): State) -> (StatusCode, String) { + let row = sqlx::query("SELECT * FROM Counter") + .fetch_one(&db) + .await.expect("no counter in db"); + let response : u32 = row.get("value"); + (StatusCode::OK, response.to_string()) +} + +async fn set_counter(State(ST { db }): State, Json(body) : Json) -> (StatusCode, String) { + sqlx::query(match &*body { + "add" => "UPDATE Counter SET value = value + 1", + "sub" => "UPDATE Counter SET value = value - 1", + _ => return (StatusCode::BAD_REQUEST, "invalid action".to_string()), + }).execute(&db).await.expect("database write error"); + get_counter(State(ST { db })).await +} + +async fn echo(body : String) -> (StatusCode, String) { + (StatusCode::OK, body) } diff --git a/flake.nix b/flake.nix index 338b485..e0d5b9b 100644 --- a/flake.nix +++ b/flake.nix @@ -49,7 +49,7 @@ ''; devShells.default = pkgs.mkShell { shellHook = '' - PATH="${builtins.toString ./front/node_modules/.bin}:$PATH" + PATH="$PWD/front/node_modules/.bin:$PATH" ''; packages = deps; }; diff --git a/front/package-lock.json b/front/package-lock.json index 91b87a8..982de15 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -11,6 +11,7 @@ "vue": "^3.4.29" }, "devDependencies": { + "@rachelambda/well-rested": "^0.1.3", "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", "@vitejs/plugin-vue": "^5.0.5", @@ -23,6 +24,7 @@ "typescript-language-server": "^4.3.3", "vite": "^5.3.1", "vite-plugin-pages": "^0.32.3", + "vue-router": "^4.4.1", "vue-tsc": "^2.0.21" } }, @@ -506,6 +508,13 @@ "node": ">= 8" } }, + "node_modules/@rachelambda/well-rested": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@rachelambda/well-rested/-/well-rested-0.1.3.tgz", + "integrity": "sha512-r8v+GI9zLZztk69uqUiAd2Ubz3bp6QDCXh0CAdGic7jm5ikMkiolv//C1Rtlphe4CYB7/8k6lJmDaTBNULKCOA==", + "dev": true, + "engines": ">= 18.0.0" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.19.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz", @@ -764,24 +773,24 @@ } }, "node_modules/@volar/language-core": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0-alpha.17.tgz", - "integrity": "sha512-FF9g89QZUVJpgZvrNpA+v5Sgo7MdUjeA1celxCe4nFTpfp4P/FUdZ1lgeYy7ZS5r13oC4Ei6HqWpfLN7PFM60w==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz", + "integrity": "sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==", "dev": true, "dependencies": { - "@volar/source-map": "2.4.0-alpha.17" + "@volar/source-map": "2.4.0-alpha.18" } }, "node_modules/@volar/language-server": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.0-alpha.17.tgz", - "integrity": "sha512-K+ffVR484Zzq9tTeoRxwtvGzvhR8qCpKhcgsPkCPeCh904yr1zxkMX728fhTePB9nZtKpI0jDuqdQA+338Gl1Q==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.0-alpha.18.tgz", + "integrity": "sha512-dciHEE/R5kzI0bY71QfkoCVQ3cQI6g9MHfA4oIP6UhnJy0CdleUalWSygOXoD3Nq7Yk6wn2BRrb1PP5MsadY/Q==", "dev": true, "dependencies": { - "@volar/language-core": "2.4.0-alpha.17", - "@volar/language-service": "2.4.0-alpha.17", - "@volar/snapshot-document": "2.4.0-alpha.17", - "@volar/typescript": "2.4.0-alpha.17", + "@volar/language-core": "2.4.0-alpha.18", + "@volar/language-service": "2.4.0-alpha.18", + "@volar/snapshot-document": "2.4.0-alpha.18", + "@volar/typescript": "2.4.0-alpha.18", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", @@ -791,21 +800,21 @@ } }, "node_modules/@volar/language-service": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.0-alpha.17.tgz", - "integrity": "sha512-rq+O/Nf7krrq611khGOH6+f9c5i7vQiDPXOLuGks2bBWjPUqaN7dR8agMm+9BTlAj0IItArKqUncYr5mYU78kQ==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.0-alpha.18.tgz", + "integrity": "sha512-EuetrtbEtudi9buinWAG5U3Jam5dY27zXd/7GYnx542kBwanWOBM8i4DAQd0z7M11fOxXgybxPA933uaSyaOog==", "dev": true, "dependencies": { - "@volar/language-core": "2.4.0-alpha.17", + "@volar/language-core": "2.4.0-alpha.18", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "node_modules/@volar/snapshot-document": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/snapshot-document/-/snapshot-document-2.4.0-alpha.17.tgz", - "integrity": "sha512-7h8cf8r+gKU0EEn68pulM1yER1iFshQR/fVT0Bw7T7cbRLe7afnaXbU+jg9yKoEUuJ/B8GU3a/5IBLofY6ZqVg==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/snapshot-document/-/snapshot-document-2.4.0-alpha.18.tgz", + "integrity": "sha512-JAeclEly/wnILhR4Pu9MpgBLInZJH49O1zoy8fU+pk5I+zpv7JIEby5z2UFAS60+sIDnxBdAGd7rZ5VibE70vg==", "dev": true, "dependencies": { "vscode-languageserver-protocol": "^3.17.5", @@ -813,18 +822,18 @@ } }, "node_modules/@volar/source-map": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0-alpha.17.tgz", - "integrity": "sha512-6LOuR2nIloQCSNMNcUPRPLjL5CInIE1pYZ8lifOCSxQRiz8GcWaOm34kAvdm7pzPQqMRHBBnV/Ihkdt/w7oWAQ==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz", + "integrity": "sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==", "dev": true }, "node_modules/@volar/typescript": { - "version": "2.4.0-alpha.17", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0-alpha.17.tgz", - "integrity": "sha512-oJlz5xJd0O1Xe/I7AV3kPpV6gXlcyxfpMcj/w4/wGY5AxFHxyy5i7VhaE/BVk99zsT6M2KxcZyUSsL55RlNXlQ==", + "version": "2.4.0-alpha.18", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz", + "integrity": "sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==", "dev": true, "dependencies": { - "@volar/language-core": "2.4.0-alpha.17", + "@volar/language-core": "2.4.0-alpha.18", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } @@ -900,20 +909,36 @@ "@vue/shared": "3.4.33" } }, - "node_modules/@vue/language-core": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.26.tgz", - "integrity": "sha512-/lt6SfQ3O1yDAhPsnLv9iSUgXd1dMHqUm/t3RctfqjuwQf1LnftZ414X3UBn6aXT4MiwXWtbNJ4Z0NZWwDWgJQ==", + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", "dev": true, "dependencies": { - "@volar/language-core": "~2.4.0-alpha.15", + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz", + "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==", + "dev": true + }, + "node_modules/@vue/language-core": { + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.29.tgz", + "integrity": "sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "~2.4.0-alpha.18", "@vue/compiler-dom": "^3.4.0", + "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.4.0", "computeds": "^0.0.1", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", - "path-browserify": "^1.0.1", - "vue-template-compiler": "^2.7.14" + "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" @@ -925,16 +950,16 @@ } }, "node_modules/@vue/language-server": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@vue/language-server/-/language-server-2.0.26.tgz", - "integrity": "sha512-t+kwaHMefRdq55Q/tkGRncOOVkzcfAghR6rKgIyTh1oTzQippEhx42bLVqYvxNjyNx/yvwd7QXNDjyPRLx23kA==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/@vue/language-server/-/language-server-2.0.29.tgz", + "integrity": "sha512-Nni7KwxQBzFVKJj9tLIDe1MVmFBFHtup8yC5LIrWq+8/LFNcznf9QHBjgEWEmwfz6PKtv46vH1hqHlmPrClf/w==", "dev": true, "dependencies": { - "@volar/language-core": "~2.4.0-alpha.15", - "@volar/language-server": "~2.4.0-alpha.15", - "@vue/language-core": "2.0.26", - "@vue/language-service": "2.0.26", - "@vue/typescript-plugin": "2.0.26", + "@volar/language-core": "~2.4.0-alpha.18", + "@volar/language-server": "~2.4.0-alpha.18", + "@vue/language-core": "2.0.29", + "@vue/language-service": "2.0.29", + "@vue/typescript-plugin": "2.0.29", "vscode-languageserver-protocol": "^3.17.5", "vscode-uri": "^3.0.8" }, @@ -943,28 +968,28 @@ } }, "node_modules/@vue/language-service": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@vue/language-service/-/language-service-2.0.26.tgz", - "integrity": "sha512-Lo4RJ+fcKrF09iIygcLFm3wdTEbmMb+l+/bpA3TXrgZk8+SbOkh6LSexJBvRQfStZSKYIV6FMgJ3ME6qpXpYqA==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/@vue/language-service/-/language-service-2.0.29.tgz", + "integrity": "sha512-lY54t7KNp1WKXfYccTj9PizwE8zrswTZbYzYdLyoeLyLwcO/JlkMssTrt1G+64TLBwBptvV9PwvNw5Bp2YxJHg==", "dev": true, "dependencies": { - "@volar/language-core": "~2.4.0-alpha.15", - "@volar/language-service": "~2.4.0-alpha.15", - "@volar/typescript": "~2.4.0-alpha.15", + "@volar/language-core": "~2.4.0-alpha.18", + "@volar/language-service": "~2.4.0-alpha.18", + "@volar/typescript": "~2.4.0-alpha.18", "@vue/compiler-dom": "^3.4.0", - "@vue/language-core": "2.0.26", + "@vue/language-core": "2.0.29", "@vue/shared": "^3.4.0", - "@vue/typescript-plugin": "2.0.26", + "@vue/typescript-plugin": "2.0.29", "computeds": "^0.0.1", "path-browserify": "^1.0.1", - "volar-service-css": "volar-2.4", - "volar-service-emmet": "volar-2.4", - "volar-service-html": "volar-2.4", - "volar-service-json": "volar-2.4", - "volar-service-pug": "volar-2.4", - "volar-service-pug-beautify": "volar-2.4", - "volar-service-typescript": "volar-2.4", - "volar-service-typescript-twoslash-queries": "volar-2.4", + "volar-service-css": "0.0.59", + "volar-service-emmet": "0.0.59", + "volar-service-html": "0.0.59", + "volar-service-json": "0.0.59", + "volar-service-pug": "0.0.59", + "volar-service-pug-beautify": "0.0.59", + "volar-service-typescript": "0.0.59", + "volar-service-typescript-twoslash-queries": "0.0.59", "vscode-html-languageservice": "^5.2.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" @@ -1022,13 +1047,13 @@ "dev": true }, "node_modules/@vue/typescript-plugin": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-2.0.26.tgz", - "integrity": "sha512-C0F2lpv1m9LO1sEIJmZEN7tSzRwPObbYHtxftDlrvUKNWuEu4OqilnRuUCNyAQRq7UrkNR3fv1Dc+OcKOj0dEg==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-2.0.29.tgz", + "integrity": "sha512-cO/cP467bGONkm/imEVvcRg77/VmoWpLyO94jSwLAt8QV0X9l414SwsRdsae+wGMPV+6k7rweer0SP16A0HYdw==", "dev": true, "dependencies": { - "@volar/typescript": "~2.4.0-alpha.15", - "@vue/language-core": "2.0.26", + "@volar/typescript": "~2.4.0-alpha.18", + "@vue/language-core": "2.0.29", "@vue/shared": "^3.4.0" } }, @@ -2529,9 +2554,9 @@ } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", "dev": true }, "node_modules/vscode-languageserver-types": { @@ -2572,24 +2597,29 @@ } } }, - "node_modules/vue-template-compiler": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "node_modules/vue-router": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.1.tgz", + "integrity": "sha512-njTLt/6gYGgIhv+U8nc5J6JpJpntFgy4fptRJ9Dp2qWQRo/PekB5DbKRYRPt0kM6feXysPKl7A5BjOmOJL5Ttw==", "dev": true, "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" + "@vue/devtools-api": "^6.6.3" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" } }, "node_modules/vue-tsc": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.26.tgz", - "integrity": "sha512-tOhuwy2bIXbMhz82ef37qeiaQHMXKQkD6mOF6CCPl3/uYtST3l6fdNyfMxipudrQTxTfXVPlgJdMENBFfC1CfQ==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.29.tgz", + "integrity": "sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==", "dev": true, "dependencies": { - "@volar/typescript": "~2.4.0-alpha.15", - "@vue/language-core": "2.0.26", + "@volar/typescript": "~2.4.0-alpha.18", + "@vue/language-core": "2.0.29", "semver": "^7.5.4" }, "bin": { diff --git a/front/package.json b/front/package.json index 6563c0d..2f98eb0 100644 --- a/front/package.json +++ b/front/package.json @@ -14,6 +14,7 @@ "vue": "^3.4.29" }, "devDependencies": { + "@rachelambda/well-rested": "^0.1.3", "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", "@vitejs/plugin-vue": "^5.0.5", @@ -26,6 +27,7 @@ "typescript-language-server": "^4.3.3", "vite": "^5.3.1", "vite-plugin-pages": "^0.32.3", + "vue-router": "^4.4.1", "vue-tsc": "^2.0.21" } } diff --git a/front/src/Api.ts b/front/src/Api.ts new file mode 100644 index 0000000..cc84f2b --- /dev/null +++ b/front/src/Api.ts @@ -0,0 +1,21 @@ +import { API } from '@rachelambda/well-rested' + +type PlantAPI = { + "/api/echo": { + kind: "POST" + request: string + response: string + } + "/api/get_counter": { + kind: "POST" + response: number + } + "/api/set_counter": { + kind: "POST" + request: "add" | "sub" + response: number + } +} + +export const api = new API('http://localhost:3013') +export default api diff --git a/front/src/api/index.ts b/front/src/api/index.ts deleted file mode 100644 index c953d32..0000000 --- a/front/src/api/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -export interface QueryParams { - [key: string]: (string | number) -} - -export interface APIDef { - [key: string]: ({ - kind: "POST", - query?: QueryParams, - request?: Object, - response: Object, - } | { - kind: "GET", - query?: QueryParams, - response: Object, - }) -} - -type A = { - "/api/blah": { - kind: "GET", - request: { - mjau: number - cat: string - } - response: { - status: number - } - } -} - -export type ValidEndPointForMethod - = T[U] extends { kind: method } ? U : never - -export type QR = { query: Object, request: Object } - -export class API { - base: string; - - constructor(base: string) { - this.base = base; - } - - private dispatch(method : "POST" | "GET", - endpoint : keyof T & string, - req : T[typeof endpoint] & QR): - Promise { - let url : URL = new URL(endpoint, this.base) - let opts : RequestInit = { method } - if (req.query != null) { - const params = new URLSearchParams() - for (const [key, value] of Object.entries(req.query)) { - params.set(key, typeof(value) == "string" ? value : value.toString()) - } - url = new URL(params.toString(), url) - } - if (method == "POST" && req.request != null) { - opts.body = JSON.stringify(req.request) - opts.headers = { "Content-Type": "application/json" } - } - return fetch(url, opts) - } - - get(endpoint : ValidEndPointForMethod, - req: T[typeof endpoint] & QR): - Promise { - return this.dispatch("GET", endpoint, req) - } - - post(endpoint : ValidEndPointForMethod, - req: T[typeof endpoint] & QR): - Promise { - return this.dispatch("POST", endpoint, req) - } -} - -const test = new API("mjau") diff --git a/front/src/main.ts b/front/src/main.ts index 7de2f88..11889c4 100644 --- a/front/src/main.ts +++ b/front/src/main.ts @@ -1,13 +1,18 @@ import './css/main.scss' +import { createMemoryHistory, createRouter } from 'vue-router' import { createApp } from 'vue' import routes from '~pages' +import { api } from './Api.js' import App from './App.vue' -const app = createApp( - App, - { routes }, -) +const router = createRouter({ + history: createMemoryHistory(), + routes +}) +const app = createApp(App) + +app.use(router) app.mount('#app') diff --git a/front/src/pages/index.vue b/front/src/pages/index.vue index b250648..023b69d 100644 --- a/front/src/pages/index.vue +++ b/front/src/pages/index.vue @@ -1,3 +1,61 @@ + +