commit 792346d43d4bf999f16f6712d94ed56dfc1cf3d7 Author: lsy Date: Sat Nov 9 00:05:18 2024 +0800 完成后端数据库的连接,后端配置文件的读,前端动态页面的选择 diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..0002f48 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "echoes" +version = "0.1.0" +edition = "2021" + +[dependencies] +rocket = { version = "0.5", features = ["json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +toml = "0.8.19" +tokio = { version = "1", features = ["full"] } +tokio-postgres = "0.7.12" +once_cell = "1.20.2" +async-trait = "0.1.83" \ No newline at end of file diff --git a/backend/src/config.rs b/backend/src/config.rs new file mode 100644 index 0000000..5b26a1a --- /dev/null +++ b/backend/src/config.rs @@ -0,0 +1,22 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Config { + pub info: Info, + pub database: Database, +} + +#[derive(Deserialize)] +pub struct Info { + pub install: bool, +} + +#[derive(Deserialize)] +pub struct Database { + pub ilk : String, + pub address : String, + pub prot : u32, + pub user : String, + pub password : String, + pub dbname : String, +} \ No newline at end of file diff --git a/backend/src/config.toml b/backend/src/config.toml new file mode 100644 index 0000000..8d73a7a --- /dev/null +++ b/backend/src/config.toml @@ -0,0 +1,10 @@ +[info] +install = false + +[database] +ilk = "postgresql" +address = "localhost" +prot = 5432 +user = "postgres" +password = "postgres" +dbname = "echoes" \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..fe27177 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,61 @@ +mod sql; +mod config; +use rocket::{get, launch, routes, Route}; +use rocket::http::{ContentType, Status}; +use rocket::serde::{ Serialize}; +use rocket::response::status; +use std::sync::{Arc, Mutex}; +use once_cell::sync::Lazy; +use rocket::serde::json::Json; +use tokio::sync::Mutex as TokioMutex; +use tokio_postgres::types::ToSql; + +// 获取数据库连接 +static GLOBAL_SQL: Lazy>>>> += Lazy::new(|| Arc::new(TokioMutex::new(None))); + +// 获取数据库连接 +async fn initialize_sql() { + let sql_instance = sql::loading().await; + let mut lock = GLOBAL_SQL.lock().await; + *lock = sql_instance; +} + +// 网站初始化 +#[get("/install")] +fn install() -> status::Custom<()> { + status::Custom(Status::Ok, ()) +} +// sql查询 +#[derive(Serialize)] +struct SSql{ + key:String, + value:String, +} +#[get("/sql")] +async fn ssql() -> status::Custom>> { + let sql_instance=GLOBAL_SQL.lock().await; + let sql =sql_instance.as_ref().unwrap(); + let query = "SELECT * FROM info"; + let params: &[&(dyn ToSql + Sync)] = &[]; + let data = sql.query(query, params).await.expect("查询数据失败"); + let mut vec = Vec::new(); + for row in data { + let key=row.get(0); + let value=row.get(1); + vec.push(SSql{ + key, + value, + }); + } + status::Custom(Status::Ok, Json(vec)) +} + + + +#[launch] +async fn rocket() -> _ { + initialize_sql().await; + rocket::build() + .mount("/api", routes![install,ssql]) +} diff --git a/backend/src/sql/mod.rs b/backend/src/sql/mod.rs new file mode 100644 index 0000000..8f3df0d --- /dev/null +++ b/backend/src/sql/mod.rs @@ -0,0 +1,53 @@ +mod postgresql; +use std::fs; +use tokio_postgres::{Error, Row}; +use toml; +use crate::config::Config; +use async_trait::async_trait; + +// 所有数据库类型 +#[async_trait] +pub trait Database: Send + Sync { + async fn query(&self, + query: &str, + params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) + -> Result, Error>; + async fn execute( + &self, + data: &str, + params: &[&(dyn tokio_postgres::types::ToSql + Sync)], + ) + -> Result; +} + + +// 加载对应数据库 +pub async fn loading() -> Option> { + let config_string = fs::read_to_string("./src/config.toml") + .expect("Could not load config file"); + let config: Config = toml::de::from_str(config_string.as_str()).expect("Could not parse config"); + let address = config.database.address; + let port = config.database.prot; + let user = config.database.user; + let password = config.database.password; + let dbname = config.database.dbname; + let sql_instance: Box; + + match config.database.ilk.as_str() { + "postgresql" => { + let sql = postgresql::connect(address, port, user, password, dbname).await; + match sql { + Ok(conn) => { + sql_instance = Box::new(conn); + } + Err(e) => { + println!("Database connection failed {}", e); + return None; + } + } + + } + _ => { return None } + }; + Some(sql_instance) +} \ No newline at end of file diff --git a/backend/src/sql/postgresql.rs b/backend/src/sql/postgresql.rs new file mode 100644 index 0000000..e0f67a9 --- /dev/null +++ b/backend/src/sql/postgresql.rs @@ -0,0 +1,65 @@ +use tokio_postgres::{NoTls, Error, Row, Client, Connection, Socket}; +use crate::sql; +use async_trait::async_trait; +use tokio_postgres::tls::NoTlsStream; + +pub struct Postgresql { + pub client: tokio_postgres::Client, +} + +pub async fn connect( + address: String, + port: u32, + user: String, + password: String, + dbname: String, +) -> Result { + let connection_str = format!( + "host={} port={} user={} password={} dbname={}", + address, port, user, password, dbname + ); + + let client:Client; + let connection:Connection; + let link = tokio_postgres::connect(&connection_str, NoTls).await; + match link { + Ok((clie,conne)) => { + client = clie; + connection = conne; + } + Err(err) => { + println!("Failed to connect to postgresql: {}", err); + return Err(err); + } + } + + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("postgresql connection error: {}", e); + } + }); + + Ok(Postgresql { client }) +} + +impl Postgresql { + +} +#[async_trait] +impl sql::Database for Postgresql { + async fn query(&self, query: & str, + params: &[&(dyn tokio_postgres::types::ToSql + Sync)] + ) -> Result, Error> { + let rows = self.client.query(query, params).await?; + Ok(rows) + } + + async fn execute( + &self, + data: &str, + params: &[&(dyn tokio_postgres::types::ToSql + Sync)], + ) -> Result { + let rows_affected = self.client.execute(data, params).await?; + Ok(rows_affected) + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..4a49cea --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,30 @@ +{ + "name": "echoes", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "eslint": "^9.13.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.11.0", + "vite": "^5.4.10" + } +} diff --git a/frontend/src/install/page.tsx b/frontend/src/install/page.tsx new file mode 100644 index 0000000..0cd47df --- /dev/null +++ b/frontend/src/install/page.tsx @@ -0,0 +1,8 @@ +import react from 'react'; +const page:react.FC=()=>{ + return ( +
+ 安装 +
) +} +export default page; \ No newline at end of file diff --git a/frontend/src/main.css b/frontend/src/main.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/frontend/src/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..c831684 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import "./main.css" +import DynamicPage from "./page/page.tsx" +import ReactDOM from 'react-dom/client' +import {createContext} from "react"; + +export const serverAddressContext=createContext("localhost:8080") +// 动态路由 +const RouterListener: React.FC = () => { + let pathname = location.pathname.split("/"); + console.log(pathname) + return ( + + + + ) +} + + +ReactDOM.createRoot(document.getElementById("root")).render( + + + +); \ No newline at end of file diff --git a/frontend/src/page/page.tsx b/frontend/src/page/page.tsx new file mode 100644 index 0000000..5f0d35d --- /dev/null +++ b/frontend/src/page/page.tsx @@ -0,0 +1,36 @@ +import React from "react"; +const THEMEPATH= "../../themes" +import {serverAddressContext} from "../main.tsx"; +// 动态获取当前主题 +const getCurrentTheme = async (): Promise => { + return new Promise((resolve) => { + resolve("default"); + }) +} + +// 获取页面 +const loadPage = (theme: string, pageName: string) => { + return import(/* @vite-ignore */`${THEMEPATH}/${theme}/${pageName}/page`).catch(() => import((/* @vite-ignore */`${THEMEPATH}/default/page/page`))) +} + +// 动态加载页面 +const DynamicPage: React.FC<{ pageName: string }> = ({pageName}) => { + const serverAddress = React.useContext(serverAddressContext) + console.log(serverAddress) + const [Page, setPage] = React.useState(null); + const [theme, setTheme] = React.useState(""); + React.useEffect(() => { + getCurrentTheme().then((theme) => { + setTheme(theme); + loadPage(theme, pageName).then((module) => { + setPage(() => module.default); + }); + }) + }) + if(!Page){ + return
loading...
; + } + + return ; +} +export default DynamicPage; \ No newline at end of file diff --git a/frontend/themes/default/404/page.tsx b/frontend/themes/default/404/page.tsx new file mode 100644 index 0000000..8d56103 --- /dev/null +++ b/frontend/themes/default/404/page.tsx @@ -0,0 +1,7 @@ +import React from "react"; +const Page: React.FC = () => { + return
404
; +}; + + +export default Page; \ No newline at end of file diff --git a/frontend/themes/default/page/page.tsx b/frontend/themes/default/page/page.tsx new file mode 100644 index 0000000..af39a9a --- /dev/null +++ b/frontend/themes/default/page/page.tsx @@ -0,0 +1,5 @@ +import React from "react"; +const Page: React.FC = () => { + return
主页
; +}; +export default Page; \ No newline at end of file