Created cli tool to create inital admin member
This commit is contained in:
parent
972f744e73
commit
10297f9114
server
110
server/Cargo.lock
generated
110
server/Cargo.lock
generated
@ -47,6 +47,56 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
@ -266,10 +316,57 @@ dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@ -994,6 +1091,12 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
@ -2195,6 +2298,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.0"
|
||||
@ -2540,6 +2649,7 @@ dependencies = [
|
||||
"axum-extra",
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"clap",
|
||||
"csv",
|
||||
"dotenvy",
|
||||
"itertools",
|
||||
|
@ -17,12 +17,13 @@ dotenvy = "0.15.7"
|
||||
validator = { version = "0.19.0", features = [ "derive" ] }
|
||||
argon2 = "0.5"
|
||||
bitflags = { version = "2.8", features = [ "serde" ] }
|
||||
clap = { version = "4.5.31", features = ["derive"] }
|
||||
|
||||
|
||||
# Tertiary crates
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
chrono = "0.4"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
uuid = { version = "1.12", features = ["v4", "fast-rng", "serde"] }
|
||||
serde_json = "1.0.137"
|
||||
rand = "0.9"
|
||||
|
@ -1,4 +1,4 @@
|
||||
CREATE TABLE "members" (
|
||||
CREATE TABLE IF NOT EXISTS "members" (
|
||||
member_id varchar(7) NOT NULL PRIMARY KEY,
|
||||
first_name text NOT NULL,
|
||||
full_name text NOT NULL,
|
||||
|
@ -1,4 +1,4 @@
|
||||
CREATE TABLE "users" (
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
user_id uuid NOT NULL PRIMARY KEY,
|
||||
email text NOT NULL UNIQUE,
|
||||
password text NOT NULL,
|
||||
|
@ -1,4 +1,4 @@
|
||||
CREATE TABLE "sessions" (
|
||||
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
session_id uuid NOT NULL PRIMARY KEY,
|
||||
user_id uuid NOT NULL REFERENCES users (user_id) ON UPDATE cascade ON DELETE cascade,
|
||||
token text NOT NULL UNIQUE,
|
||||
|
@ -1,6 +1,6 @@
|
||||
CREATE TYPE message_status AS ENUM ('pending', 'sent', 'canceled');
|
||||
|
||||
CREATE TABLE messages (
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id uuid NOT NULL PRIMARY KEY,
|
||||
created_at timestamptz NOT NULL,
|
||||
scheduled_at timestamptz,
|
||||
@ -11,7 +11,7 @@ CREATE TABLE messages (
|
||||
thumbnail_url text
|
||||
);
|
||||
|
||||
CREATE TABLE messages_users (
|
||||
CREATE TABLE IF NOT EXISTS messages_users (
|
||||
message_id uuid NOT NULL REFERENCES users (user_id) ON UPDATE cascade ON DELETE cascade,
|
||||
user_id uuid NOT NULL REFERENCES users (user_id) ON UPDATE cascade ON DELETE cascade,
|
||||
is_read boolean NOT NULL,
|
||||
|
@ -1,3 +1,3 @@
|
||||
ALTER TABLE messages
|
||||
ADD COLUMN member_groups bigint,
|
||||
ADD COLUMN member_roles bigint;
|
||||
ADD COLUMN member_groups bigint NOT NULL,
|
||||
ADD COLUMN member_roles bigint NOT NULL;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::Postgres;
|
||||
use sqlx::{PgPool, Postgres};
|
||||
|
||||
use crate::model::{
|
||||
member::{Groups, Roles},
|
||||
@ -61,4 +61,20 @@ impl Message {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get(pool: &PgPool, channel: Channel) -> Result<Vec<Self>, sqlx::Error> {
|
||||
let messages = sqlx::query_as!(
|
||||
Self,
|
||||
"
|
||||
SELECT message_id, created_at, scheduled_at, status as \"status:MessageStatus\", title, content, channel, member_groups, member_roles, thumbnail_url FROM messages
|
||||
WHERE status = 'sent'
|
||||
AND channel & $1 > 0;
|
||||
",
|
||||
channel.bits() as i64
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(messages)
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::Router;
|
||||
use tokio::{net::TcpListener, sync::Mutex};
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
use wrbapp_server::routes::member::migrate::MigrationStore;
|
||||
use wrbapp_server::routes::routes;
|
||||
use wrbapp_server::{database, AppState};
|
||||
use wrbapp_server::database;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@ -30,23 +24,5 @@ async fn main() {
|
||||
.await
|
||||
.expect("Database connection failed");
|
||||
|
||||
let migration_store = Arc::new(Mutex::new(MigrationStore::default()));
|
||||
|
||||
let app_state = AppState {
|
||||
pool,
|
||||
migration_store,
|
||||
};
|
||||
|
||||
// Serve app
|
||||
let app = Router::new().nest("/v1", routes()).with_state(app_state);
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:3000")
|
||||
.await
|
||||
.expect("Error while initializing listener");
|
||||
|
||||
tracing::info!("Listening on {}", listener.local_addr().unwrap());
|
||||
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.expect("Error while serving axum application");
|
||||
wrbapp_server::util::cli::parse(pool).await;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use bitflags::bitflags;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Message {
|
||||
pub message_id: uuid::Uuid,
|
||||
pub created_at: DateTime<Utc>,
|
||||
@ -15,7 +16,7 @@ pub struct Message {
|
||||
pub thumbnail_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type)]
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Serialize)]
|
||||
#[sqlx(type_name = "message_status", rename_all = "lowercase")]
|
||||
pub enum MessageStatus {
|
||||
Pending,
|
||||
@ -24,7 +25,7 @@ pub enum MessageStatus {
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
pub struct Channel: u16 {
|
||||
const ALGEMEEN = 1 << 0;
|
||||
const BELANGRIJK = 1 << 1;
|
||||
|
@ -7,8 +7,8 @@ use axum::{
|
||||
|
||||
use crate::{
|
||||
auth::get_user_from_header,
|
||||
database::model::{Member as DbMember, UserMember as DbUserMember},
|
||||
model::{member::Roles, Member, User},
|
||||
database::model::{Member as DbMember, Message as DbMessage, UserMember as DbUserMember},
|
||||
model::{member::Roles, message::Channel, Member, Message, User},
|
||||
util::convert_vec,
|
||||
AppState,
|
||||
};
|
||||
@ -18,6 +18,7 @@ pub fn routes() -> Router<AppState> {
|
||||
.route("/user", get(get_current_user))
|
||||
.route("/user/{user_id}/members", post(members_insert))
|
||||
.route("/user/{user_id}/members", delete(members_remove))
|
||||
.route("/user/{user_id}/messages", get(get_messages))
|
||||
}
|
||||
|
||||
pub async fn get_current_user(
|
||||
@ -73,3 +74,16 @@ pub async fn members_remove(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_messages(
|
||||
State(state): State<AppState>,
|
||||
Path(user_id): Path<String>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Json<Vec<Message>>, crate::Error> {
|
||||
let user = get_user_from_header(&state.pool, &headers).await?;
|
||||
user.authorize(&state.pool, None, Some(user_id)).await?;
|
||||
|
||||
let messages = DbMessage::get(&state.pool, Channel::ALGEMEEN).await?;
|
||||
|
||||
Ok(Json(convert_vec(messages)))
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
mod bitflags;
|
||||
pub mod cli;
|
||||
pub mod error;
|
||||
mod helpers;
|
||||
pub mod serve;
|
||||
|
||||
pub use helpers::convert_vec;
|
||||
|
56
server/src/util/cli.rs
Normal file
56
server/src/util/cli.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use sqlx::{Acquire, PgPool};
|
||||
|
||||
use crate::model::{
|
||||
member::{Groups, Roles},
|
||||
Member,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Serve,
|
||||
CreateAdminMember,
|
||||
}
|
||||
|
||||
pub async fn parse(pool: PgPool) {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match &cli.command {
|
||||
Some(Commands::Serve) => {
|
||||
crate::util::serve::serve(pool).await;
|
||||
}
|
||||
Some(Commands::CreateAdminMember) => {
|
||||
create_admin_member(&pool).await.unwrap();
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_admin_member(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
use crate::database::model::Member as DbMember;
|
||||
|
||||
let member = DbMember {
|
||||
member_id: "D000000".to_string(),
|
||||
first_name: "Admin".to_string(),
|
||||
full_name: "Admin Admin".to_string(),
|
||||
registration_token: None,
|
||||
diploma: None,
|
||||
groups: Groups::empty(),
|
||||
roles: Roles::ADMIN,
|
||||
};
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
DbMember::insert_many(&mut transaction, vec![member]).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
31
server/src/util/serve.rs
Normal file
31
server/src/util/serve.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::Router;
|
||||
use sqlx::PgPool;
|
||||
use tokio::{net::TcpListener, sync::Mutex};
|
||||
|
||||
use crate::routes::member::migrate::MigrationStore;
|
||||
use crate::routes::routes;
|
||||
use crate::AppState;
|
||||
|
||||
pub async fn serve(pool: PgPool) {
|
||||
let migration_store = Arc::new(Mutex::new(MigrationStore::default()));
|
||||
|
||||
let app_state = AppState {
|
||||
pool,
|
||||
migration_store,
|
||||
};
|
||||
|
||||
// Serve app
|
||||
let app = Router::new().nest("/v1", routes()).with_state(app_state);
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:3000")
|
||||
.await
|
||||
.expect("Error while initializing listener");
|
||||
|
||||
tracing::info!("Listening on {}", listener.local_addr().unwrap());
|
||||
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.expect("Error while serving axum application");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user