refactor
This commit is contained in:
@@ -3,16 +3,10 @@ name = "packager"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
default-run = "packager"
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "packager"
|
name = "packager"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "packager-adm"
|
|
||||||
path = "src/bin/adm.rs"
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
lto = "off"
|
lto = "off"
|
||||||
|
|||||||
@@ -6,4 +6,10 @@ COPY target/x86_64-unknown-linux-musl/release/packager /usr/local/bin/packager
|
|||||||
|
|
||||||
ENTRYPOINT ["tini", "--"]
|
ENTRYPOINT ["tini", "--"]
|
||||||
|
|
||||||
CMD ["/usr/local/bin/packager", "--bind", "0.0.0.0", "--port", "3000", "--database-url", "/var/lib/packager/db/db.sqlite"]
|
CMD [ \
|
||||||
|
"/usr/local/bin/packager", \
|
||||||
|
"--database-url", "/var/lib/packager/db/db.sqlite", \
|
||||||
|
"serve", \
|
||||||
|
"--bind", "0.0.0.0", \
|
||||||
|
"--port", "3000" \
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
|
|
||||||
use packager::{models, sqlite, StartError};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(long)]
|
|
||||||
database_url: String,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Command,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
|
||||||
enum Command {
|
|
||||||
#[command(subcommand)]
|
|
||||||
User(UserCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
|
||||||
enum UserCommand {
|
|
||||||
Create(UserCreate),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
struct UserCreate {
|
|
||||||
#[arg(long)]
|
|
||||||
username: String,
|
|
||||||
#[arg(long)]
|
|
||||||
fullname: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Error {
|
|
||||||
Generic { message: String },
|
|
||||||
UserExists { username: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Generic { message } => write!(f, "{}", message),
|
|
||||||
Self::UserExists { username } => write!(f, "user \"{username}\" already exists"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StartError> for Error {
|
|
||||||
fn from(starterror: StartError) -> Self {
|
|
||||||
Self::Generic {
|
|
||||||
message: starterror.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
let database_pool = sqlite::init_database_pool(&args.database_url).await?;
|
|
||||||
|
|
||||||
match args.command {
|
|
||||||
Command::User(cmd) => match cmd {
|
|
||||||
UserCommand::Create(user) => {
|
|
||||||
let id = match models::user::create(
|
|
||||||
&database_pool,
|
|
||||||
models::user::NewUser {
|
|
||||||
username: &user.username,
|
|
||||||
fullname: &user.fullname,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(id) => id,
|
|
||||||
Err(error) => {
|
|
||||||
if let models::Error::Query(models::QueryError::Duplicate {
|
|
||||||
description: _,
|
|
||||||
}) = error
|
|
||||||
{
|
|
||||||
println!(
|
|
||||||
"Error: {}",
|
|
||||||
Error::UserExists {
|
|
||||||
username: user.username,
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return Err(error.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"User \"{}\" created successfully (id {})",
|
|
||||||
user.username, id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ pub enum RequestError {
|
|||||||
AuthenticationUserNotFound { username: String },
|
AuthenticationUserNotFound { username: String },
|
||||||
AuthenticationHeaderMissing,
|
AuthenticationHeaderMissing,
|
||||||
AuthenticationHeaderInvalid { message: String },
|
AuthenticationHeaderInvalid { message: String },
|
||||||
|
Transport { inner: hyper::Error },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for RequestError {}
|
impl std::error::Error for RequestError {}
|
||||||
@@ -35,6 +36,9 @@ impl fmt::Display for RequestError {
|
|||||||
Self::AuthenticationHeaderInvalid { message } => {
|
Self::AuthenticationHeaderInvalid { message } => {
|
||||||
write!(f, "Authentication header invalid: {message}")
|
write!(f, "Authentication header invalid: {message}")
|
||||||
}
|
}
|
||||||
|
Self::Transport { inner } => {
|
||||||
|
write!(f, "HTTP error: {inner}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,52 +47,19 @@ impl fmt::Display for RequestError {
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
Model(models::Error),
|
Model(models::Error),
|
||||||
Request(RequestError),
|
Request(RequestError),
|
||||||
|
Start(StartError),
|
||||||
|
Command(CommandError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum StartError {
|
|
||||||
DatabaseInitError { message: String },
|
|
||||||
DatabaseMigrationError { message: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for StartError {}
|
|
||||||
|
|
||||||
impl fmt::Display for StartError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::DatabaseInitError { message } => {
|
|
||||||
write!(f, "database initialization error: {message}")
|
|
||||||
}
|
|
||||||
Self::DatabaseMigrationError { message } => {
|
|
||||||
write!(f, "database migration error: {message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<sqlx::Error> for StartError {
|
|
||||||
fn from(value: sqlx::Error) -> Self {
|
|
||||||
Self::DatabaseInitError {
|
|
||||||
message: value.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<sqlx::migrate::MigrateError> for StartError {
|
|
||||||
fn from(value: sqlx::migrate::MigrateError) -> Self {
|
|
||||||
Self::DatabaseMigrationError {
|
|
||||||
message: value.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Model(model_error) => write!(f, "Model error: {model_error}"),
|
Self::Model(model_error) => write!(f, "Model error: {model_error}"),
|
||||||
Self::Request(request_error) => write!(f, "Request error: {request_error}"),
|
Self::Request(request_error) => write!(f, "Request error: {request_error}"),
|
||||||
|
Self::Start(start_error) => write!(f, "{start_error}"),
|
||||||
|
Self::Command(command_error) => write!(f, "{command_error}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +70,18 @@ impl From<models::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<StartError> for Error {
|
||||||
|
fn from(value: StartError) -> Self {
|
||||||
|
Self::Start(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<hyper::Error> for Error {
|
||||||
|
fn from(value: hyper::Error) -> Self {
|
||||||
|
Self::Request(RequestError::Transport { inner: value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IntoResponse for Error {
|
impl IntoResponse for Error {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
match self {
|
match self {
|
||||||
@@ -146,8 +129,74 @@ impl IntoResponse for Error {
|
|||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
view::ErrorPage::build(&request_error.to_string()),
|
view::ErrorPage::build(&request_error.to_string()),
|
||||||
),
|
),
|
||||||
|
RequestError::Transport { inner } => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
view::ErrorPage::build(&inner.to_string()),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
Self::Start(start_error) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
view::ErrorPage::build(&start_error.to_string()),
|
||||||
|
),
|
||||||
|
Self::Command(command_error) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
view::ErrorPage::build(&command_error.to_string()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum StartError {
|
||||||
|
DatabaseInitError { message: String },
|
||||||
|
DatabaseMigrationError { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for StartError {}
|
||||||
|
|
||||||
|
impl fmt::Display for StartError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::DatabaseInitError { message } => {
|
||||||
|
write!(f, "database initialization error: {message}")
|
||||||
|
}
|
||||||
|
Self::DatabaseMigrationError { message } => {
|
||||||
|
write!(f, "database migration error: {message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for StartError {
|
||||||
|
fn from(value: sqlx::Error) -> Self {
|
||||||
|
Self::DatabaseInitError {
|
||||||
|
message: value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::migrate::MigrateError> for StartError {
|
||||||
|
fn from(value: sqlx::migrate::MigrateError) -> Self {
|
||||||
|
Self::DatabaseMigrationError {
|
||||||
|
message: value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CommandError {
|
||||||
|
UserExists { username: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for CommandError {}
|
||||||
|
|
||||||
|
impl fmt::Display for CommandError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UserExists { username } => {
|
||||||
|
write!(f, "user \"{username}\" already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub mod sqlite;
|
|||||||
|
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
pub use error::{Error, RequestError, StartError};
|
pub use error::{CommandError, Error, RequestError, StartError};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
|||||||
199
rust/src/main.rs
199
rust/src/main.rs
@@ -1,15 +1,31 @@
|
|||||||
use packager::{auth, routing, sqlite, AppState, ClientState, StartError};
|
|
||||||
|
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::process::ExitCode;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
use packager::{auth, models, routing, sqlite, AppState, ClientState, Error};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
database_url: String,
|
database_url: String,
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum Command {
|
||||||
|
Serve(Serve),
|
||||||
|
#[command(subcommand)]
|
||||||
|
Admin(Admin),
|
||||||
|
Migrate,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
struct Serve {
|
||||||
#[arg(long, default_value_t = 3000)]
|
#[arg(long, default_value_t = 3000)]
|
||||||
port: u16,
|
port: u16,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
@@ -18,42 +34,143 @@ struct Args {
|
|||||||
disable_auth_and_assume_user: Option<String>,
|
disable_auth_and_assume_user: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[derive(Subcommand, Debug)]
|
||||||
async fn main() -> Result<(), StartError> {
|
enum Admin {
|
||||||
tracing_subscriber::fmt()
|
#[command(subcommand)]
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
User(UserCommand),
|
||||||
.init();
|
}
|
||||||
|
|
||||||
let args = Args::parse();
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum UserCommand {
|
||||||
let database_pool = sqlite::init_database_pool(&args.database_url).await?;
|
Create(UserCreate),
|
||||||
sqlite::migrate(&database_pool).await?;
|
}
|
||||||
|
|
||||||
let state = AppState {
|
#[derive(Parser, Debug)]
|
||||||
database_pool,
|
struct UserCreate {
|
||||||
client_state: ClientState::new(),
|
#[arg(long)]
|
||||||
auth_config: if let Some(assume_user) = args.disable_auth_and_assume_user {
|
username: String,
|
||||||
auth::AuthConfig::Disabled { assume_user }
|
#[arg(long)]
|
||||||
} else {
|
fullname: String,
|
||||||
auth::AuthConfig::Enabled
|
}
|
||||||
},
|
|
||||||
};
|
struct MainResult(Result<(), Error>);
|
||||||
|
|
||||||
// build our application with a route
|
impl std::process::Termination for MainResult {
|
||||||
let app = routing::router(state);
|
fn report(self) -> std::process::ExitCode {
|
||||||
let addr = SocketAddr::from((
|
match self.0 {
|
||||||
IpAddr::from_str(&args.bind)
|
Ok(_) => ExitCode::SUCCESS,
|
||||||
.map_err(|error| format!("error parsing bind address {}: {}", &args.bind, error))
|
Err(e) => {
|
||||||
.unwrap(),
|
eprintln!("Error: {e}");
|
||||||
args.port,
|
ExitCode::FAILURE
|
||||||
));
|
}
|
||||||
tracing::debug!("listening on {}", addr);
|
}
|
||||||
axum::Server::try_bind(&addr)
|
}
|
||||||
.map_err(|error| format!("error binding to {}: {}", addr, error))
|
}
|
||||||
.unwrap()
|
|
||||||
.serve(app.into_make_service())
|
impl From<Error> for MainResult {
|
||||||
.await
|
fn from(error: Error) -> Self {
|
||||||
.unwrap();
|
Self(Err(error))
|
||||||
|
}
|
||||||
Ok(())
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> MainResult {
|
||||||
|
let args = Args::parse();
|
||||||
|
match args.command {
|
||||||
|
Command::Serve(serve_args) => {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(tracing::Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let database_pool = match sqlite::init_database_pool(&args.database_url).await {
|
||||||
|
Ok(pool) => pool,
|
||||||
|
Err(e) => return <_ as Into<Error>>::into(e).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = sqlite::migrate(&database_pool).await {
|
||||||
|
return <_ as Into<Error>>::into(e).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = AppState {
|
||||||
|
database_pool,
|
||||||
|
client_state: ClientState::new(),
|
||||||
|
auth_config: if let Some(assume_user) = serve_args.disable_auth_and_assume_user {
|
||||||
|
auth::AuthConfig::Disabled { assume_user }
|
||||||
|
} else {
|
||||||
|
auth::AuthConfig::Enabled
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// build our application with a route
|
||||||
|
let app = routing::router(state);
|
||||||
|
let addr = SocketAddr::from((
|
||||||
|
IpAddr::from_str(&serve_args.bind)
|
||||||
|
.map_err(|error| {
|
||||||
|
format!("error parsing bind address {}: {}", &serve_args.bind, error)
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
serve_args.port,
|
||||||
|
));
|
||||||
|
tracing::debug!("listening on {}", addr);
|
||||||
|
if let Err(e) = axum::Server::try_bind(&addr)
|
||||||
|
.map_err(|error| format!("error binding to {}: {}", addr, error))
|
||||||
|
.unwrap()
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return <hyper::Error as Into<Error>>::into(e).into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Admin(admin_command) => match admin_command {
|
||||||
|
Admin::User(cmd) => match cmd {
|
||||||
|
UserCommand::Create(user) => {
|
||||||
|
let database_pool = match sqlite::init_database_pool(&args.database_url).await {
|
||||||
|
Ok(pool) => pool,
|
||||||
|
Err(e) => return <_ as Into<Error>>::into(e).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = match models::user::create(
|
||||||
|
&database_pool,
|
||||||
|
models::user::NewUser {
|
||||||
|
username: &user.username,
|
||||||
|
fullname: &user.fullname,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|error| match error {
|
||||||
|
models::Error::Query(models::QueryError::Duplicate { description: _ }) => {
|
||||||
|
Error::Command(packager::CommandError::UserExists {
|
||||||
|
username: user.username.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Error::Model(error),
|
||||||
|
}) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(e) => {
|
||||||
|
return e.into();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"User \"{}\" created successfully (id {})",
|
||||||
|
&user.username, id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Command::Migrate => {
|
||||||
|
let database_pool = match sqlite::init_database_pool(&args.database_url).await {
|
||||||
|
Ok(pool) => pool,
|
||||||
|
Err(e) => return <_ as Into<Error>>::into(e).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = sqlite::migrate(&database_pool).await {
|
||||||
|
return <_ as Into<Error>>::into(e).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Migrations successfully applied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MainResult(Ok(()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user