super basic application with communication between front and backend
This commit is contained in:
parent
8255663005
commit
afc03bb722
1
back/.gitignore
vendored
1
back/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
db.sqlite
|
||||
|
|
109
back/Cargo.lock
generated
109
back/Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<Sqlite>
|
||||
}
|
||||
|
||||
#[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<ST>) -> (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<ST>, Json(body) : Json<String>) -> (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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
180
front/package-lock.json
generated
180
front/package-lock.json
generated
|
@ -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": {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
21
front/src/Api.ts
Normal file
21
front/src/Api.ts
Normal file
|
@ -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<PlantAPI>('http://localhost:3013')
|
||||
export default api
|
|
@ -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 (keyof T & string), method extends "GET" | "POST">
|
||||
= T[U] extends { kind: method } ? U : never
|
||||
|
||||
export type QR = { query: Object, request: Object }
|
||||
|
||||
export class API<T extends APIDef> {
|
||||
base: string;
|
||||
|
||||
constructor(base: string) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
private dispatch(method : "POST" | "GET",
|
||||
endpoint : keyof T & string,
|
||||
req : T[typeof endpoint] & QR):
|
||||
Promise<T[typeof endpoint]["response"]> {
|
||||
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<T, string, "GET">,
|
||||
req: T[typeof endpoint] & QR):
|
||||
Promise<T[typeof endpoint]["response"]> {
|
||||
return this.dispatch("GET", endpoint, req)
|
||||
}
|
||||
|
||||
post(endpoint : ValidEndPointForMethod<T, string, "POST">,
|
||||
req: T[typeof endpoint] & QR):
|
||||
Promise<T[typeof endpoint]["response"]> {
|
||||
return this.dispatch("POST", endpoint, req)
|
||||
}
|
||||
}
|
||||
|
||||
const test = new API<A>("mjau")
|
|
@ -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')
|
||||
|
|
|
@ -1,3 +1,61 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { api } from '../Api.js'
|
||||
|
||||
let content = ref<string>("")
|
||||
let counter = ref<number>()
|
||||
|
||||
onMounted(async () => {
|
||||
const response : string | Response = await api.post("/api/echo", {
|
||||
request: "hii world !! ^-^"
|
||||
})
|
||||
|
||||
if (typeof(response) == "string") {
|
||||
content.value = response
|
||||
} else {
|
||||
content.value = response.statusText
|
||||
}
|
||||
|
||||
await get_counter()
|
||||
})
|
||||
|
||||
async function get_counter() {
|
||||
const response : number | Response = await api.post("/api/get_counter", {})
|
||||
|
||||
if (typeof(response) == "number") {
|
||||
counter.value = response
|
||||
} else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
async function set_counter(action : "add" | "sub") {
|
||||
const response : number | Response = await api.post("/api/set_counter", {
|
||||
request: action
|
||||
})
|
||||
|
||||
if (typeof(response) == "number") {
|
||||
counter.value = response
|
||||
} else {
|
||||
console.log(response)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function add() {
|
||||
await set_counter("add");
|
||||
}
|
||||
|
||||
async function sub() {
|
||||
await set_counter("sub");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>hii world !! ^-^</h1>
|
||||
<h1>index :3</h1>
|
||||
<p>{{ content }}</p>
|
||||
|
||||
<pre><code>{{ counter }}</code></pre>
|
||||
<button @click="add">add</button>
|
||||
<button @click="sub">sub</button>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue
Block a user