Init backend

This commit is contained in:
Sphereso 2024-10-08 10:53:00 +02:00
parent a8fd8107b3
commit 00b6b09330
15 changed files with 2729 additions and 456 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[alias]
xtask = "run -p xtask --"

0
.env Normal file
View file

2827
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = ["crates/backend", "crates/ble-server"]
members = ["crates/backend", "crates/xtask"]

View file

@ -2,3 +2,18 @@
name = "backend"
version = "0.1.0"
edition = "2021"
[dependencies]
argon2 = { version = "0.5" }
actix-web = "4"
actix-cors = "0.7"
uuid = { version = "1" }
serde = { version = "*", features = ["derive"] }
sea-orm = { version = "1", features = [
"sqlx-postgres",
"runtime-tokio-rustls",
"with-uuid",
] }
dotenvy = "*"
jsonwebtoken = "*"
futures = "*"

View file

@ -1 +1,50 @@
fn main() {}
use actix_web::{web, App, HttpServer};
use sea_orm::{Database, DatabaseConnection};
use std::env;
mod routes;
use routes::config;
#[derive(Clone)]
struct AppState {
db: DatabaseConnection,
secret: String,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
#[cfg(debug_assertions)]
println!("Running debug build -> enabling permissive CORS");
dotenvy::dotenv().ok();
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let jwt_secret = env::var("TOKEN_SECRET").expect("TOKEN_SECRET must be set");
let conn = Database::connect(&db_url)
.await
.expect("Connecting to Database failed");
println!("Finished running migrations");
let state = AppState {
db: conn,
secret: jwt_secret,
};
println!("Listening for connections...");
HttpServer::new(move || {
let cors = if cfg!(debug_assertions) {
actix_cors::Cors::permissive()
} else {
actix_cors::Cors::default()
};
App::new()
.wrap(cors)
.app_data(web::Data::new(state.clone()))
.configure(config)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

View file

@ -0,0 +1,31 @@
use actix_web::web;
pub fn config(cfg: &mut web::ServiceConfig) {
/*cfg.service(
web::scope("/api/v1")
.service(
web::resource("/users")
.get(UserController::list_users)
.post(UserController::create_user),
)
.route("/users/me", web::get().to(UserController::get_current_user))
.service(
web::resource("/users/{user_id}")
.delete(UserController::delete_user)
.put(UserController::update_user),
)
.service(
web::resource("/licenses")
.get(LicenseController::list_groups)
.post(LicenseController::create_license),
)
.service(
web::resource("/licenses/{license_id}")
.delete(LicenseController::delete_license)
.put(LicenseController::update_license),
)
.route("/groups", web::post().to(LicenseController::create_group))
.service(web::resource("/groups/{group_id}").delete(LicenseController::delete_group))
.service(web::scope("/auth").route("/login", web::post().to(AuthController::login))),
);*/
}

9
crates/entity/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "entity"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "*", features = ["derive"] }
sea-orm = { version = "1" }

View file

@ -0,0 +1,22 @@
[package]
name = "migration"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
name = "migration"
path = "src/lib.rs"
[dependencies]
async-std = { version = "1", features = ["attributes", "tokio1"] }
[dependencies.sea-orm-migration]
version = "1.0.0"
features = [
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
# e.g.
# "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature
# "sqlx-postgres", # `DATABASE_DRIVER` feature
]

View file

@ -0,0 +1,41 @@
# Running Migrator CLI
- Generate a new migration file
```sh
cargo run -- generate MIGRATION_NAME
```
- Apply all pending migrations
```sh
cargo run
```
```sh
cargo run -- up
```
- Apply first 10 pending migrations
```sh
cargo run -- up -n 10
```
- Rollback last applied migrations
```sh
cargo run -- down
```
- Rollback last 10 applied migrations
```sh
cargo run -- down -n 10
```
- Drop all tables from the database, then reapply all migrations
```sh
cargo run -- fresh
```
- Rollback all applied migrations, then reapply all migrations
```sh
cargo run -- refresh
```
- Rollback all applied migrations
```sh
cargo run -- reset
```
- Check the status of all migrations
```sh
cargo run -- status
```

View file

@ -0,0 +1,12 @@
pub use sea_orm_migration::prelude::*;
mod m20220101_000001_create_table;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(m20220101_000001_create_table::Migration)]
}
}

View file

@ -0,0 +1,44 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(User::Table)
.if_not_exists()
.col(
uuid(Users::Id)
.extra("DEFAULT gen_random_uuid()")
.primary_key(),
)
.col(string(User::Name))
.col(string(User::Email))
.col(string(User::Hash))
.col(bool(User::Admin))
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(User::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
Name,
Email,
Hash,
Admin,
}

View file

@ -0,0 +1,6 @@
use sea_orm_migration::prelude::*;
#[async_std::main]
async fn main() {
cli::run_cli(migration::Migrator).await;
}

9
crates/xtask/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "*" }
ctrlc = { version = "*" }

114
crates/xtask/src/main.rs Normal file
View file

@ -0,0 +1,114 @@
use std::{
env::{current_dir, var_os},
path::PathBuf,
process,
sync::mpsc::channel,
};
use clap::Command;
fn main() {
let workspace_dir = var_os("CARGO_WORKSPACE_DIR")
.map(PathBuf::from)
.unwrap_or_else(|| current_dir().unwrap());
let matches = cli().get_matches();
match matches.subcommand() {
Some(("backend", _)) => {
process::Command::new("cargo")
.arg("run")
.arg("-p")
.arg("backend")
.current_dir(&workspace_dir)
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
.status()
.expect("running backend");
}
Some(("entity", submatches)) => match submatches.subcommand() {
Some(("generate", _)) => {
process::Command::new("sea-orm-cli")
.arg("generate")
.arg("entity")
.arg("-o")
.arg("crates/entity/src/")
.arg("--lib")
.arg("--with-serde")
.arg("both")
.current_dir(&workspace_dir)
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
.status()
.expect("running entity generate");
}
Some(("clean", _)) => {
let dir = workspace_dir.join("crates/entity/src");
let files = dir.read_dir().expect("Failed to read entity directory");
for file in files {
let file = file.expect("failed to get file path");
if file.file_name() == "lib.rs" {
continue;
}
let file_path = file.path();
match std::fs::remove_file(&file_path) {
Ok(_) => println!("Removed file {}", file_path.display()),
Err(_) => println!("Failed to remove file {}", file_path.display()),
}
}
}
_ => {
panic!(
"Unknown command: entity {:?}",
submatches.subcommand().map(|c| c.0)
)
}
},
Some(("frontend", submatches)) => match submatches.subcommand() {
Some(("start", _)) => {
let dir = workspace_dir.join("web");
let mut command = process::Command::new("pnpm")
.arg("run")
.arg("dev")
.current_dir(dir)
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
.spawn()
.expect("running entity generate");
let (tx, rx) = channel();
ctrlc::set_handler(move || tx.send(()).expect("could not set signal"))
.expect("failed setting ctrlc handler");
rx.recv().expect("could not recieve from channel");
command.kill().unwrap();
}
_ => {
panic!(
"Unknown command: entity {:?}",
submatches.subcommand().map(|c| c.0)
)
}
},
_ => {
panic!("Unknown command: {:?}", matches.subcommand().map(|c| c.0))
}
}
}
fn cli() -> Command {
Command::new("xtask")
.about("docusphere useful commands")
.subcommand_required(true)
.subcommand(Command::new("backend"))
.subcommand(
Command::new("entity")
.subcommand_required(true)
.subcommand(Command::new("generate"))
.subcommand(Command::new("clean")),
)
.subcommand(
Command::new("frontend")
.subcommand_required(true)
.subcommand(Command::new("start")),
)
}