1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/*
This file is part of jellything (https://codeberg.org/metamuffin/jellything)
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
#![feature(int_roundings, str_as_str, duration_constructors)]
#![allow(clippy::needless_borrows_for_generic_args)]
#![recursion_limit = "4096"]
use crate::{
auth::{hash_password, token::SessionKey},
logger::setup_logger,
};
use anyhow::{Result, anyhow};
use jellycache::Cache;
use jellycommon::{
USER_ADMIN, USER_LOGIN, USER_PASSWORD,
jellyobject::{ObjectBuffer, Path},
};
use jellydb::{Database, Filter, Query, Sort};
use log::{error, info};
use routes::build_rocket;
use serde::Deserialize;
use std::{env::args, fs::read_to_string, path::PathBuf, process::exit, sync::Arc};
pub mod api;
pub mod auth;
pub mod compat;
pub mod logger;
pub mod logic;
pub mod request_info;
pub mod responders;
pub mod routes;
pub mod ui;
pub mod ui_responder;
#[rocket::main]
async fn main() {
setup_logger();
let state = match create_state() {
Ok(s) => s,
Err(e) => {
error!("unable to start: {e:#}");
exit(1);
}
};
let r = build_rocket(state).launch().await;
match r {
Ok(_) => info!("server shutdown"),
Err(e) => {
error!("server exited: {e}");
exit(1);
}
}
}
pub struct State {
pub config: Config,
pub cache: Arc<Cache>,
pub database: Arc<dyn Database>,
pub session_key: SessionKey,
}
#[derive(Debug, Deserialize)]
pub struct Config {
pub import: jellyimport::Config,
pub ui: jellyui::Config,
pub stream: Arc<jellystream::Config>,
pub session_key: String,
pub admin_password: String,
pub asset_path: PathBuf,
pub database_path: PathBuf,
pub cache_path: PathBuf,
pub max_memory_cache_size: usize,
pub tls: bool,
pub hostname: String,
}
pub fn create_state() -> Result<Arc<State>> {
let config_path = args()
.nth(1)
.ok_or(anyhow!("first argument (config path) missing"))?;
let config: Config = serde_yaml_ng::from_str(&read_to_string(config_path)?)?;
let cache_storage = jellykv::rocksdb::new(&config.cache_path)?;
let db_storage = jellykv::rocksdb::new(&config.database_path)?;
// let db_storage = jellykv::memory::new();
let state = Arc::new(State {
cache: Cache::new(Box::new(cache_storage), config.max_memory_cache_size).into(),
database: Arc::new(db_storage),
session_key: SessionKey::parse(&config.session_key)?,
config,
});
create_admin_user(&state)?;
Ok(state)
}
fn create_admin_user(state: &State) -> Result<()> {
state.database.transaction(&mut |txn| {
let admin_row = txn.query_single(Query {
filter: Filter::Match(Path(vec![USER_LOGIN.0]), "admin".into()),
sort: Sort::None,
})?;
if admin_row.is_none() {
info!("Creating new admin user");
let pwhash = hash_password("admin", &state.config.admin_password);
txn.insert(ObjectBuffer::new(&mut [
(USER_LOGIN.0, &"admin"),
(USER_PASSWORD.0, &pwhash.as_slice()),
(USER_ADMIN.0, &()),
]))?;
}
Ok(())
})?;
Ok(())
}
|