diff --git a/.env b/.env
new file mode 100644
index 0000000000000000000000000000000000000000..6b77cf254053d6bbf2e113be23f20444e9706801
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+DATABASE_URL=sqlite://./database.sqlite
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..35410cacdc5e87f985c93a96520f5e11a5c822e4
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/apiserver.iml b/.idea/apiserver.iml
new file mode 100644
index 0000000000000000000000000000000000000000..cf84ae4a69877a117dad3f555c9d8ebf05a4fc20
--- /dev/null
+++ b/.idea/apiserver.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ad765240e80a7d422b5c5a8401142fe971290e11
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:$PROJECT_DIR$/database.sqlite
+ $ProjectFileDir$
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3eff73054ab6112beb4f341301aaced00bac7222
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index a312dd98ff5c654cb61fddc50bf1bf83701c94d2..4c2b76fbbca11cf4e3971c0cd7778f94a60e60b6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -238,6 +238,21 @@ dependencies = [
"alloc-no-stdlib",
]
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "anstream"
version = "0.6.15"
@@ -444,8 +459,13 @@ version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
"num-traits",
"serde",
+ "wasm-bindgen",
+ "windows-targets",
]
[[package]]
@@ -471,6 +491,12 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
[[package]]
name = "cpufeatures"
version = "0.2.14"
@@ -499,6 +525,41 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "deranged"
version = "0.3.11"
@@ -521,6 +582,55 @@ dependencies = [
"syn",
]
+[[package]]
+name = "diesel"
+version = "2.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "diesel_derives",
+ "itoa",
+ "libsqlite3-sys",
+ "pq-sys",
+ "r2d2",
+ "time",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4"
+dependencies = [
+ "diesel_table_macro_syntax",
+ "dsl_auto_type",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "diesel_migrations"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6"
+dependencies = [
+ "diesel",
+ "migrations_internals",
+ "migrations_macros",
+]
+
+[[package]]
+name = "diesel_table_macro_syntax"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
+dependencies = [
+ "syn",
+]
+
[[package]]
name = "digest"
version = "0.10.7"
@@ -542,12 +652,38 @@ dependencies = [
"syn",
]
+[[package]]
+name = "dotenv"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+
+[[package]]
+name = "dsl_auto_type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607"
+dependencies = [
+ "darling",
+ "either",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
[[package]]
name = "email_address"
version = "0.2.9"
@@ -754,6 +890,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "hermit-abi"
version = "0.3.9"
@@ -861,6 +1003,29 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "icu_collections"
version = "1.5.0"
@@ -979,6 +1144,12 @@ dependencies = [
"syn",
]
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
[[package]]
name = "idna"
version = "0.5.0"
@@ -1101,9 +1272,14 @@ dependencies = [
"actix-service",
"actix-web",
"async-trait",
+ "chrono",
+ "diesel",
+ "diesel_migrations",
+ "dotenv",
"env_logger",
"jsonschema",
"k8s-openapi",
+ "r2d2",
"schemars",
"serde",
"serde_json",
@@ -1128,6 +1304,16 @@ version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
+[[package]]
+name = "libsqlite3-sys"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "litemap"
version = "0.7.3"
@@ -1173,6 +1359,27 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+[[package]]
+name = "migrations_internals"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff"
+dependencies = [
+ "serde",
+ "toml",
+]
+
+[[package]]
+name = "migrations_macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd"
+dependencies = [
+ "migrations_internals",
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "mime"
version = "0.3.17"
@@ -1384,6 +1591,15 @@ dependencies = [
"zerocopy",
]
+[[package]]
+name = "pq-sys"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793"
+dependencies = [
+ "vcpkg",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.87"
@@ -1402,6 +1618,17 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "r2d2"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
+dependencies = [
+ "log",
+ "parking_lot",
+ "scheduled-thread-pool",
+]
+
[[package]]
name = "rand"
version = "0.8.5"
@@ -1566,6 +1793,15 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
+dependencies = [
+ "parking_lot",
+]
+
[[package]]
name = "schemars"
version = "0.8.21"
@@ -1655,6 +1891,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -1724,6 +1969,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
[[package]]
name = "syn"
version = "2.0.79"
@@ -1841,6 +2092,40 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
[[package]]
name = "tower-service"
version = "0.3.3"
@@ -1946,6 +2231,12 @@ dependencies = [
"vsimd",
]
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
[[package]]
name = "version_check"
version = "0.9.5"
@@ -2050,6 +2341,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
[[package]]
name = "windows-registry"
version = "0.2.0"
@@ -2153,6 +2453,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "winnow"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "write16"
version = "1.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 9903945662bcb65f04c8aea7674c90af73550450..cf870f4e77724e5bae811f4e5099ec1cddb0af03 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,10 @@ repository = "https://gitee.com/iscas-system/apiserver"
readme = "README.md"
[dependencies]
+r2d2 = "0.8.10"
+dotenv = "0.15.0"
+diesel = { version = "2.2.0", features = ["sqlite", "postgres", "r2d2"] }
+diesel_migrations = "2.2.0"
actix-http = "3.9.0"
actix-service = "2.0.2"
actix-web = "4.9.0"
@@ -23,5 +27,6 @@ tokio = "1.40.0"
k8s-openapi = { version = "0.23.0", features = ["latest"] }
jsonschema = "0.23.0"
schemars = "0.8.21"
+chrono = "0.4.38"
#log = "0.4"
#env_logger = { version = "0.11" }
diff --git a/diesel.toml b/diesel.toml
new file mode 100644
index 0000000000000000000000000000000000000000..40400e18dab14832d5cbe000c1d7459d56763996
--- /dev/null
+++ b/diesel.toml
@@ -0,0 +1,9 @@
+# For documentation on how to configure this file,
+# see https://diesel.rs/guides/configuring-diesel-cli
+
+[print_schema]
+file = "src/schema.rs"
+custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
+
+[migrations_directory]
+dir = "/home/yuichi/apiserver/migrations"
diff --git a/migrations/.keep b/migrations/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/migrations/2024-11-03-082237_create_metadata_table/down.sql b/migrations/2024-11-03-082237_create_metadata_table/down.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d3c389a350ff17bf1dea0735313f861f93e0ad99
--- /dev/null
+++ b/migrations/2024-11-03-082237_create_metadata_table/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+DROP TABLE IF EXISTS metadata;
\ No newline at end of file
diff --git a/migrations/2024-11-03-082237_create_metadata_table/up.sql b/migrations/2024-11-03-082237_create_metadata_table/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..e89b705e21d74c8b8a3e5c19095667a0f1cb5645
--- /dev/null
+++ b/migrations/2024-11-03-082237_create_metadata_table/up.sql
@@ -0,0 +1,10 @@
+-- Your SQL goes here
+CREATE TABLE IF NOT EXISTS metadata (
+ name VARCHAR(256),
+ namespace BOOLEAN,
+ apigroup VARCHAR(256),
+ data TEXT,
+ created_time VARCHAR(256),
+ updated_time VARCHAR(256),
+ PRIMARY KEY (name, namespace, apigroup)
+ );
\ No newline at end of file
diff --git a/src/cores/apiserver.rs b/src/cores/apiserver.rs
index d648ee1d0cf8f463b7879230c0ddecc22a33c7c2..ea18e79474bce00b58af0e6d01602b2b0088ac19 100644
--- a/src/cores/apiserver.rs
+++ b/src/cores/apiserver.rs
@@ -10,7 +10,8 @@ use actix_web::{HttpServer, App, web, HttpResponse};
use crate::cores::config::{Config, APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE};
use crate::cores::handlers::{Handler};
-use std::sync::Arc;
+use std::sync::{Arc};
+use crate::cores::db::{DbPool};
pub struct ApiServer
{
@@ -29,7 +30,7 @@ impl ApiServer
// TODO 未来加上else
// TODO 优化注册流程
pub async fn start
- (self: Arc, addr: &str, handler: T) -> Result<(), std::io::Error> {
+ (self: Arc, addr: &str, handler: T, db_pool: Arc) -> Result<(), std::io::Error> {
let handler = Arc::new(handler);
@@ -38,44 +39,93 @@ impl ApiServer
let mut app = App::new();
let handler = Arc::clone(&handler);
let config = Arc::clone(&self.config);
+ let db_pool = Arc::clone(&db_pool);
// URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE,
// API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况
for (key, route) in config.create_routes() {
if route == API_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::post().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().create_api_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .create_api_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == API_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::post().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().create_api_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .create_api_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::post().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().create_apis_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .create_apis_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::post().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().create_apis_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .create_apis_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
}
@@ -86,38 +136,86 @@ impl ApiServer
for (key, route) in config.delete_routes() {
if route == API_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::delete().to(
- move |path, data| {
+ move |path| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().delete_api_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .delete_api_without_namespace(path, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == API_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::delete().to(
- move |path, data| {
+ move |path| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().delete_api_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .delete_api_with_namespace(path, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::delete().to(
- move |path, data| {
+ move |path| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().delete_apis_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .delete_apis_without_namespace(path, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::delete().to(
- move |path, data| {
+ move |path| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().delete_apis_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .delete_apis_with_namespace(path, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
}
@@ -128,38 +226,86 @@ impl ApiServer
for (key, route) in config.update_routes() {
if route == API_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::put().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().update_api_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .update_api_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == API_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::put().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().update_api_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .update_api_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::put().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().update_apis_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .update_apis_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::put().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().update_apis_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .update_apis_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
}
@@ -170,38 +316,86 @@ impl ApiServer
for (key, route) in config.getone_routes() {
if route == API_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().getone_api_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .getone_api_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == API_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().getone_api_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .getone_api_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().getone_apis_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .getone_apis_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().getone_apis_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .getone_apis_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
}
@@ -213,38 +407,86 @@ impl ApiServer
for (key, route) in config.listall_routes() {
if route == API_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().listall_api_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .listall_api_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == API_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().listall_api_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .listall_api_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITHOUT_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().listall_apis_without_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .listall_apis_without_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
} else if route == APIS_WITH_NAMESPACE {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
app = app.route(key, web::get().to(
move |path, data| {
let handler = Arc::clone(&handler);
+ let db_pool = Arc::clone(&db_pool);
async move {
- handler.default().listall_apis_with_namespace(path, data).await
+ match db_pool.get_connection() {
+ Ok(mut conn) => {
+ handler
+ .default()
+ .listall_apis_with_namespace(path, data, &mut conn)
+ .await
+ },
+ Err(_) => Err(actix_web::error::ErrorInternalServerError(
+ "Failed to get database connection",
+ )),
+ }
}
}));
}
diff --git a/src/cores/db.rs b/src/cores/db.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1d706464e3ad1ee9076982943dba0edd839061f6
--- /dev/null
+++ b/src/cores/db.rs
@@ -0,0 +1,176 @@
+use std::collections::HashMap;
+use chrono::Utc;
+use diesel::pg::PgConnection;
+use diesel::{sql_query, QueryResult, RunQueryDsl};
+use diesel::sqlite::SqliteConnection;
+use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
+use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
+use serde_json::{json, Value};
+
+pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
+
+pub enum DbPool {
+ Pg(Pool>),
+ Sqlite(Pool>),
+}
+
+pub enum DbConnection {
+ Pg(PooledConnection>),
+ Sqlite(PooledConnection>),
+}
+
+// 定义所有资源模板的静态映射
+fn resource_templates() -> HashMap<&'static str, (bool, Value)> {
+ let mut templates = HashMap::new();
+ templates.insert(
+ "cargos",
+ (
+ true,
+ json!({
+ "apiVersion": "v1",
+ "kind": "Cargo",
+ "metadata": {
+ "name": "string",
+ "annotations": "object"
+ },
+ "spec": {
+ "containers": [
+ {
+ "name": "string",
+ "image": "string",
+ "command": "array"
+ }
+ ]
+ }
+ }),
+ ),
+ );
+ templates.insert(
+ "nodes",
+ (
+ false,
+ json!({
+ "apiVersion": "v1",
+ "kind": "Node",
+ "metadata": {
+ "name": "string",
+ "labels": "object"
+ },
+ "spec": {
+ "podCIDR": "string",
+ "providerID": "string"
+ }
+ }),
+ ),
+ );
+ templates.insert(
+ "jobs",
+ (
+ true,
+ json!({
+ "apiVersion": "batch/v1",
+ "kind": "Job",
+ "metadata": {
+ "name": "string",
+ "labels": "object"
+ },
+ "spec": {
+ "template": {
+ "metadata": {
+ "labels": "object"
+ },
+ "spec": {
+ "containers": [
+ {
+ "name": "string",
+ "image": "string",
+ "args": "array"
+ }
+ ]
+ }
+ }
+ }
+ }),
+ ),
+ );
+ // 其他资源可以继续添加
+ templates
+}
+
+
+impl DbPool {
+ // 初始化所有资源的 metadata 和表结构
+ fn initialize_metadata(conn: &mut DbConnection) -> QueryResult<()> {
+ let templates = resource_templates();
+
+ for (table_name, (supports_namespace, template)) in templates.iter() {
+
+ let create_table_query = format!(
+ "CREATE TABLE IF NOT EXISTS {} (
+ name VARCHAR(256),
+ namespace VARCHAR(256),
+ apigroup VARCHAR(256),
+ data TEXT,
+ created_time VARCHAR(256),
+ updated_time VARCHAR(256),
+ PRIMARY KEY (name, namespace, apigroup)
+ );",
+ table_name
+ );
+
+ // 从模板中提取 `apiVersion` 字段作为 `apigroup`
+ let apigroup = template.get("apiVersion")
+ .and_then(Value::as_str)
+ .unwrap_or("v1");
+
+ let insert_metadata_query = format!(
+ "INSERT OR IGNORE INTO metadata (name, namespace, apigroup, data, created_time, updated_time)
+ VALUES ('{}', {}, '{}', '{}', '{}', '{}');",
+ table_name,
+ *supports_namespace,
+ apigroup,
+ template.to_string(),
+ Utc::now().naive_utc().to_string(),
+ Utc::now().naive_utc().to_string()
+ );
+
+ match conn {
+ DbConnection::Pg(pg_conn) => {
+ sql_query(create_table_query).execute(pg_conn)?;
+ sql_query(insert_metadata_query).execute(pg_conn)?;
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ sql_query(create_table_query).execute(sqlite_conn)?;
+ sql_query(insert_metadata_query).execute(sqlite_conn)?;
+ }
+ }
+ }
+ Ok(())
+ }
+ pub fn get_connection(&self) -> Result {
+ match self {
+ DbPool::Pg(pool) => pool.get().map(DbConnection::Pg),
+ DbPool::Sqlite(pool) => pool.get().map(DbConnection::Sqlite),
+ }
+ }
+ pub fn new(database_url: &str) -> Result {
+ if database_url.starts_with("postgres://") {
+ let manager = ConnectionManager::::new(database_url);
+ let pool = Pool::builder().build(manager)?;
+ // 运行迁移
+ let mut conn = pool.get()?;
+ conn.run_pending_migrations(MIGRATIONS).unwrap();
+ Self::initialize_metadata(&mut DbPool::Pg(pool.clone()).get_connection()?).unwrap();
+ Ok(DbPool::Pg(pool))
+ } else {
+ let manager = ConnectionManager::::new(database_url);
+ let pool = Pool::builder().build(manager)?;
+ // 运行迁移
+ let mut conn = pool.get()?;
+ conn.run_pending_migrations(MIGRATIONS).unwrap();
+ Self::initialize_metadata(&mut DbPool::Sqlite(pool.clone()).get_connection()?).unwrap();
+ Ok(DbPool::Sqlite(pool))
+ }
+ }
+}
+
diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs
index 77d964cb6e8aa920c92d4e53ec374f3fbae37ae2..8eee2da4f080af141d2b4128e932ced3505770bc 100644
--- a/src/cores/handlers.rs
+++ b/src/cores/handlers.rs
@@ -7,70 +7,75 @@
use async_trait::async_trait;
use actix_web::{HttpResponse, Result, web};
+use chrono::Utc;
+use diesel::{QueryResult, RunQueryDsl, QueryDsl, ExpressionMethods, OptionalExtension, QueryableByName};
use serde_json::json;
use serde_json::Value;
+use crate::cores::db::DbConnection;
+use diesel::sql_types::{Text};
+use actix_web::error::ErrorInternalServerError;
#[async_trait]
pub trait Handler {
// /api/{version}/{plural}
- async fn create_api_without_namespace(&self, info: web::Path<(String, String)>, data: web::Json) -> Result;
+ async fn create_api_without_namespace(&self, info: web::Path<(String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /api/{version}/namespaces/{namespace}/{plural}
- async fn create_api_with_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json) -> Result;
+ async fn create_api_with_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/{plural}
- async fn create_apis_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json) -> Result;
+ async fn create_apis_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/namespaces/{namespace}/{plural}
- async fn create_apis_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json) -> Result;
+ async fn create_apis_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /api/{version}/{plural}/{name}
- async fn delete_api_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json) -> Result;
+ async fn delete_api_without_namespace(&self, info: web::Path<(String, String, String)>, db_connection: &mut DbConnection) -> Result;
// /api/{version}/namespaces/{namespace}/{plural}/{name}
- async fn delete_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json) -> Result;
+ async fn delete_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/{plural}/{name}
- async fn delete_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json) -> Result;
+ async fn delete_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}
- async fn delete_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, data: web::Json) -> Result;
+ async fn delete_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, db_connection: &mut DbConnection) -> Result;
// /api/{version}/{plural}/{name}
- async fn update_api_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json) -> Result;
+ async fn update_api_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /api/{version}/namespaces/{namespace}/{plural}/{name}
- async fn update_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json) -> Result;
+ async fn update_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/{plural}/{name}
- async fn update_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json) -> Result;
+ async fn update_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}
- async fn update_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, data: web::Json) -> Result;
+ async fn update_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, data: web::Json, db_connection: &mut DbConnection) -> Result;
// /api/{version}/{plural}/{name}
- async fn getone_api_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query) -> Result;
+ async fn getone_api_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /api/{version}/namespaces/{namespace}/{plural}/{name}
- async fn getone_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query) -> Result;
+ async fn getone_api_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/{plural}/{name}
- async fn getone_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query) -> Result;
+ async fn getone_apis_without_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}
- async fn getone_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, data: web::Query) -> Result;
+ async fn getone_apis_with_namespace(&self, info: web::Path<(String, String, String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /api/{version}/{plural}
- async fn listall_api_without_namespace(&self, info: web::Path<(String, String)>, data: web::Query) -> Result;
+ async fn listall_api_without_namespace(&self, info: web::Path<(String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /api/{version}/namespaces/{namespace}/{plural}
- async fn listall_api_with_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query) -> Result;
+ async fn listall_api_with_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/{plural}
- async fn listall_apis_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query) -> Result;
+ async fn listall_apis_without_namespace(&self, info: web::Path<(String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// /apis/{group}/{version}/namespaces/{namespace}/{plural}
- async fn listall_apis_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query) -> Result;
+ async fn listall_apis_with_namespace(&self, info: web::Path<(String, String, String, String)>, data: web::Query, db_connection: &mut DbConnection) -> Result;
// 不满足以上请求路径的处理
fn default(&self) -> DefaultHandler;
@@ -87,147 +92,977 @@ impl DefaultHandler {
}
}
+
+
+// 查询 metadata 表中的 plural 是否存在,并检查 namespace 要求是否满足
+async fn check_metadata(
+ conn: &mut DbConnection,
+ plural: &str,
+ version: &str,
+ requires_namespace: bool,
+) -> QueryResult {
+ use diesel::dsl::count_star;
+ use crate::schema::metadata::dsl::*;
+
+ let count;
+ match conn {
+ DbConnection::Pg(pg_conn) => {
+ count = metadata
+ .filter(name.eq(plural))
+ .filter(apigroup.eq(version))
+ .filter(namespace.eq(requires_namespace))
+ .select(count_star())
+ .first::(pg_conn)?;
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ count = metadata
+ .filter(name.eq(plural))
+ .filter(apigroup.eq(version))
+ .filter(namespace.eq(requires_namespace))
+ .select(count_star())
+ .first::(sqlite_conn)?;
+ }
+ }
+ Ok(count > 0)
+}
+
+
+// 创建新的数据表
+async fn create_table_if_not_exists(conn: &mut DbConnection, table_name: &str) -> QueryResult<()> {
+
+ let create_query = format!(
+ "CREATE TABLE IF NOT EXISTS {} (
+ name VARCHAR(256),
+ namespace VARCHAR(256),
+ apigroup VARCHAR(256),
+ data TEXT,
+ created_time VARCHAR(256),
+ updated_time VARCHAR(256),
+ PRIMARY KEY (name, namespace, apigroup)
+ )",
+ table_name
+ );
+
+ match conn {
+ DbConnection::Pg(pg_conn) => {
+ diesel::sql_query(create_query).execute(pg_conn)?;
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ diesel::sql_query(create_query).execute(sqlite_conn)?;
+ }
+ }
+
+ Ok(())
+}
+
+// 在 metadata 表中插入新记录
+async fn insert_metadata(
+ conn: &mut DbConnection,
+ plural: &str,
+ version: &str,
+ namespace_required: bool,
+ json_data: &Value
+) -> QueryResult<()> {
+ use crate::schema::metadata::dsl::*;
+ match conn {
+ DbConnection::Pg(pg_conn) => {
+ diesel::insert_into(metadata)
+ .values((
+ name.eq(plural),
+ apigroup.eq(version),
+ namespace.eq(namespace_required),
+ data.eq(json_data.to_string()),
+ created_time.eq(Utc::now().naive_utc().to_string()),
+ updated_time.eq(Utc::now().naive_utc().to_string()),
+ ))
+ .execute(pg_conn)?;
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ diesel::insert_into(metadata)
+ .values((
+ name.eq(plural),
+ apigroup.eq(version),
+ namespace.eq(namespace_required),
+ data.eq(json_data.to_string()),
+ created_time.eq(Utc::now().naive_utc().to_string()),
+ updated_time.eq(Utc::now().naive_utc().to_string()),
+ ))
+ .execute(sqlite_conn)?;
+ }
+ }
+ Ok(())
+}
+
+
+
+// 插入数据到指定的表
+async fn insert_into_table(
+ conn: &mut DbConnection,
+ table_name: &str,
+ json_data: &Value,
+ version: &str,
+ namespace: Option<&str>,
+) -> QueryResult<()> {
+ match conn {
+ DbConnection::Pg(pg_conn) => {
+ // 从 json_data 中提取 metadata.name
+ let name = json_data
+ .get("metadata")
+ .and_then(|metadata| metadata.get("name"))
+ .and_then(|name| name.as_str())
+ .unwrap_or("error");
+
+ let insert_query = format!(
+ "INSERT INTO {} (name, namespace, apigroup, data, created_time, updated_time) VALUES ($1, $2, $3, $4, $5, $6)",
+ table_name
+ );
+ diesel::sql_query(insert_query)
+ .bind::(name)
+ .bind::(namespace.unwrap_or(""))
+ .bind::(version)
+ .bind::(json_data.to_string()) // 将 JSON 数据存储为 TEXT
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .execute(pg_conn)?;
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ // 从 json_data 中提取 metadata.name
+ let name = json_data
+ .get("metadata")
+ .and_then(|metadata| metadata.get("name"))
+ .and_then(|name| name.as_str())
+ .unwrap_or("error");
+
+ let insert_query = format!(
+ "INSERT INTO {} (name, namespace, apigroup, data, created_time, updated_time) VALUES (?, ?, ?, ?, ?, ?)",
+ table_name
+ );
+ diesel::sql_query(insert_query)
+ .bind::(name)
+ .bind::(namespace.unwrap_or(""))
+ .bind::(version)
+ .bind::(json_data.to_string()) // 将 JSON 数据存储为 TEXT
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .execute(sqlite_conn)?;
+ }
+ }
+ Ok(())
+}
+
+// 从 plural 表中删除特定 name 的记录
+async fn delete_from_table(
+ conn: &mut DbConnection,
+ table_name: &str,
+ item_name: &str,
+ item_version: &str,
+ item_namespace: Option<&str>,
+) -> QueryResult {
+ // 根据是否存在 namespace 动态构建删除查询
+ let delete_query = if let Some(_) = item_namespace {
+ match conn {
+ DbConnection::Pg(_) => format!("DELETE FROM {} WHERE name = $1 AND namespace = $2 AND apigroup = $3", table_name),
+ DbConnection::Sqlite(_) => format!("DELETE FROM {} WHERE name = ? AND namespace = ? AND apigroup = ?", table_name),
+ }
+ } else {
+ match conn {
+ DbConnection::Pg(_) => format!("DELETE FROM {} WHERE name = $1 AND apigroup = $2", table_name),
+ DbConnection::Sqlite(_) => format!("DELETE FROM {} WHERE name = ? AND apigroup = ?", table_name),
+ }
+ };
+
+ // 执行删除操作
+ let rows_affected = match conn {
+ DbConnection::Pg(pg_conn) => {
+ if let Some(namespace) = item_namespace {
+ diesel::sql_query(delete_query)
+ .bind::(item_name)
+ .bind::(namespace)
+ .bind::(item_version)
+ .execute(pg_conn)?
+ } else {
+ diesel::sql_query(delete_query)
+ .bind::(item_name)
+ .bind::(item_version)
+ .execute(pg_conn)?
+ }
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ if let Some(namespace) = item_namespace {
+ diesel::sql_query(delete_query)
+ .bind::(item_name)
+ .bind::(namespace)
+ .bind::(item_version)
+ .execute(sqlite_conn)?
+ } else {
+ diesel::sql_query(delete_query)
+ .bind::(item_name)
+ .bind::(item_version)
+ .execute(sqlite_conn)?
+ }
+ }
+ };
+ Ok(rows_affected > 0)
+}
+
+
+async fn update_data_in_table(
+ conn: &mut DbConnection,
+ table_name: &str,
+ item_name: &str,
+ item_version: &str,
+ item_namespace: Option<&str>,
+ json_data: &Value,
+) -> QueryResult {
+
+ let update_query = if let Some(_) = item_namespace {
+ match conn {
+ DbConnection::Pg(_) => format!("UPDATE {} SET data = $1, updated_time = $2 WHERE name = $3 AND namespace = $4 AND apigroup = $5", table_name),
+ DbConnection::Sqlite(_) => format!("UPDATE {} SET data = ?, updated_time = ? WHERE name = ? AND namespace = ? AND apigroup = ?", table_name),
+ }
+ } else {
+ match conn {
+ DbConnection::Pg(_) => format!("UPDATE {} SET data = $1, updated_time = $2 WHERE name = $3 AND apigroup = $4", table_name),
+ DbConnection::Sqlite(_) => format!("UPDATE {} SET data = ?, updated_time = ? WHERE name = ? AND apigroup = ?", table_name),
+ }
+ };
+
+ let rows_affected = match conn {
+ DbConnection::Pg(pg_conn) => {
+ if let Some(namespace) = item_namespace {
+ diesel::sql_query(update_query)
+ .bind::(json_data.to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(item_name)
+ .bind::(namespace)
+ .bind::(item_version)
+ .execute(pg_conn)?
+ } else {
+ diesel::sql_query(update_query)
+ .bind::(json_data.to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(item_name)
+ .bind::(item_version)
+ .execute(pg_conn)?
+ }
+ },
+ DbConnection::Sqlite(sqlite_conn) => {
+ if let Some(namespace) = item_namespace {
+ diesel::sql_query(update_query)
+ .bind::(json_data.to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(item_name)
+ .bind::(namespace)
+ .bind::(item_version)
+ .execute(sqlite_conn)?
+ } else {
+ diesel::sql_query(update_query)
+ .bind::(json_data.to_string())
+ .bind::(Utc::now().naive_utc().to_string())
+ .bind::(item_name)
+ .bind::(item_version)
+ .execute(sqlite_conn)?
+ }
+ }
+ };
+
+ Ok(rows_affected > 0)
+}
+
+// 辅助查询函数,用于获取数据的 `data` 字段
+#[derive(QueryableByName)]
+struct DataResult {
+ #[sql_type = "Text"]
+ data: String,
+}
+
+async fn get_data_from_table(
+ conn: &mut DbConnection,
+ table_name: &str,
+ item_name: &str,
+ item_version: &str,
+ item_namespace: Option<&str>,
+) -> QueryResult