feat(backend): manage arcadia settings in the database

This commit is contained in:
FrenchGithubUser
2025-12-14 19:20:47 +01:00
parent 25226f387f
commit eeb822cc45
44 changed files with 578 additions and 276 deletions

View File

@@ -124,6 +124,8 @@ use crate::handlers::user_applications::get_user_applications::GetUserApplicatio
crate::handlers::css_sheets::get_css_sheet_content::exec,
crate::handlers::css_sheets::get_css_sheets::exec,
crate::handlers::css_sheets::get_css_sheet::exec,
crate::handlers::arcadia_settings::get_arcadia_settings::exec,
crate::handlers::arcadia_settings::update_arcadia_settings::exec,
crate::handlers::external_db::get_isbn_data::exec,
crate::handlers::external_db::get_musicbrainz_data::exec,
crate::handlers::external_db::get_tmdb_data::exec,

View File

@@ -0,0 +1,35 @@
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
use actix_web::{web::Data, HttpResponse};
use arcadia_common::error::{Error, Result};
use arcadia_storage::{
models::{arcadia_settings::ArcadiaSettings, user::UserPermission},
redis::RedisPoolInterface,
};
#[utoipa::path(
get,
operation_id = "Get Arcadia settings",
tag = "Arcadia Settings",
path = "/api/arcadia-settings",
security(
("http" = ["Bearer"])
),
responses(
(status = 200, description = "Successfully retrieved Arcadia settings", body=ArcadiaSettings),
)
)]
pub async fn exec<R: RedisPoolInterface + 'static>(
arc: Data<Arcadia<R>>,
user: Authdata,
) -> Result<HttpResponse> {
if !arc
.pool
.user_has_permission(user.sub, &UserPermission::EditArcadiaSettings)
.await?
{
return Err(Error::InsufficientPrivileges);
}
let settings = arc.settings.lock().unwrap().clone();
Ok(HttpResponse::Ok().json(settings))
}

View File

@@ -0,0 +1,13 @@
pub mod get_arcadia_settings;
pub mod update_arcadia_settings;
use actix_web::web::{get, put, resource, ServiceConfig};
use arcadia_storage::redis::RedisPoolInterface;
pub fn config<R: RedisPoolInterface + 'static>(cfg: &mut ServiceConfig) {
cfg.service(
resource("")
.route(get().to(self::get_arcadia_settings::exec::<R>))
.route(put().to(self::update_arcadia_settings::exec::<R>)),
);
}

View File

@@ -0,0 +1,44 @@
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
use actix_web::{
web::{Data, Json},
HttpResponse,
};
use arcadia_common::error::{Error, Result};
use arcadia_storage::{
models::{arcadia_settings::ArcadiaSettings, user::UserPermission},
redis::RedisPoolInterface,
};
#[utoipa::path(
put,
operation_id = "Update Arcadia settings",
tag = "Arcadia Settings",
path = "/api/arcadia-settings",
security(
("http" = ["Bearer"])
),
request_body = ArcadiaSettings,
responses(
(status = 200, description = "Successfully updated Arcadia settings", body=ArcadiaSettings),
)
)]
pub async fn exec<R: RedisPoolInterface + 'static>(
settings: Json<ArcadiaSettings>,
arc: Data<Arcadia<R>>,
user: Authdata,
) -> Result<HttpResponse> {
if !arc
.pool
.user_has_permission(user.sub, &UserPermission::EditArcadiaSettings)
.await?
{
return Err(Error::InsufficientPrivileges);
}
let updated_settings = arc.pool.update_arcadia_settings(&settings).await?;
// Update the in-memory settings
*arc.settings.lock().unwrap() = updated_settings.clone();
Ok(HttpResponse::Ok().json(updated_settings))
}

View File

@@ -97,6 +97,7 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
&invitation,
&arc.is_open_signups(),
&arc.env.user_class_name_on_signup,
&arc.settings.lock().unwrap().default_css_sheet_name,
)
.await?;

View File

@@ -37,6 +37,17 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
return Err(Error::InsufficientPrivileges);
}
let updated = arc.pool.update_css_sheet(&css_sheet).await?;
Ok(HttpResponse::Ok().json(updated))
let old_name_was_default =
css_sheet.old_name == arc.settings.lock().unwrap().default_css_sheet_name;
let updated_css_sheet = arc.pool.update_css_sheet(&css_sheet).await?;
// If the old name was the default, the CASCADE has already updated the database.
// We just need to reload the settings from the database to update the in-memory cache.
if old_name_was_default {
let updated_settings = arc.pool.get_arcadia_settings().await?;
*arc.settings.lock().unwrap() = updated_settings;
}
Ok(HttpResponse::Ok().json(updated_css_sheet))
}

View File

@@ -17,5 +17,8 @@ use arcadia_storage::{models::css_sheet::CssSheetsEnriched, redis::RedisPoolInte
)]
pub async fn exec<R: RedisPoolInterface + 'static>(arc: Data<Arcadia<R>>) -> Result<HttpResponse> {
let sheets = arc.pool.find_css_sheets().await?;
Ok(HttpResponse::Ok().json(sheets))
Ok(HttpResponse::Ok().json(CssSheetsEnriched {
css_sheets: sheets,
default_sheet_name: arc.settings.lock().unwrap().default_css_sheet_name.clone(),
}))
}

View File

@@ -3,7 +3,6 @@ pub mod edit_css_sheet;
pub mod get_css_sheet;
pub mod get_css_sheet_content;
pub mod get_css_sheets;
pub mod set_default_css_sheet;
use actix_web::web::{get, post, put, resource, ServiceConfig};
use arcadia_storage::redis::RedisPoolInterface;
@@ -16,9 +15,6 @@ pub fn config<R: RedisPoolInterface + 'static>(cfg: &mut ServiceConfig) {
.route(put().to(self::edit_css_sheet::exec::<R>)),
);
cfg.service(resource("/{name}").route(get().to(self::get_css_sheet::exec::<R>)));
cfg.service(
resource("/{name}/default").route(put().to(self::set_default_css_sheet::exec::<R>)),
);
}
pub fn config_public<R: RedisPoolInterface + 'static>(cfg: &mut ServiceConfig) {

View File

@@ -1,36 +0,0 @@
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
use actix_web::{
web::{Data, Path},
HttpResponse,
};
use arcadia_common::error::{Error, Result};
use arcadia_storage::{models::user::UserPermission, redis::RedisPoolInterface};
#[utoipa::path(
put,
operation_id = "Set default CSS sheet",
tag = "Css Sheet",
path = "/api/css-sheets/{name}/default",
security(
("http" = ["Bearer"])
),
responses(
(status = 200, description = "Successfully changed the default CSS sheet"),
)
)]
pub async fn exec<R: RedisPoolInterface + 'static>(
name: Path<String>,
arc: Data<Arcadia<R>>,
user: Authdata,
) -> Result<HttpResponse> {
if !arc
.pool
.user_has_permission(user.sub, &UserPermission::SetDefaultCssSheet)
.await?
{
return Err(Error::InsufficientPrivileges);
}
arc.pool.set_default_css_sheet(&name).await?;
Ok(HttpResponse::Ok().json(serde_json::json!({ "result": "success" })))
}

View File

@@ -1,4 +1,5 @@
pub mod affiliated_artists;
pub mod arcadia_settings;
pub mod artists;
pub mod auth;
pub mod collages;

View File

@@ -1,5 +1,12 @@
use arcadia_storage::{connection_pool::ConnectionPool, redis::RedisPoolInterface};
use std::{ops::Deref, str::FromStr, sync::Arc};
use arcadia_storage::{
connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings,
redis::RedisPoolInterface,
};
use std::{
ops::Deref,
str::FromStr,
sync::{Arc, Mutex},
};
use crate::{env::Env, services::auth::Auth};
@@ -34,6 +41,7 @@ pub struct Arcadia<R: RedisPoolInterface> {
pub pool: Arc<ConnectionPool>,
pub redis_pool: Arc<R>,
pub auth: Auth<R>,
pub settings: Arc<Mutex<ArcadiaSettings>>,
env: Env,
}
@@ -46,11 +54,17 @@ impl<R: RedisPoolInterface> Deref for Arcadia<R> {
}
impl<R: RedisPoolInterface> Arcadia<R> {
pub fn new(pool: Arc<ConnectionPool>, redis_pool: Arc<R>, env: Env) -> Self {
pub fn new(
pool: Arc<ConnectionPool>,
redis_pool: Arc<R>,
env: Env,
settings: ArcadiaSettings,
) -> Self {
Self {
pool,
redis_pool: Arc::clone(&redis_pool),
auth: Auth::new(Arc::clone(&redis_pool)),
settings: Arc::new(Mutex::new(settings)),
env,
}
}

View File

@@ -49,10 +49,18 @@ async fn main() -> std::io::Result<()> {
&env.redis.password,
env.redis.port,
));
// Load settings from database on startup
let settings = pool
.get_arcadia_settings()
.await
.expect("failed to load arcadia settings from database");
let arc = Data::new(Arcadia::new(
Arc::clone(&pool),
Arc::clone(&redis_pool),
env,
settings,
));
let server = HttpServer::new(move || {
let cors = Cors::permissive();

View File

@@ -3,6 +3,7 @@ use actix_web_httpauth::middleware::HttpAuthentication;
use arcadia_storage::redis::RedisPoolInterface;
use crate::handlers::affiliated_artists::config as AffiliatedArtistsConfig;
use crate::handlers::arcadia_settings::config as ArcadiaSettingsConfig;
use crate::handlers::artists::config as ArtistsConfig;
use crate::handlers::auth::config as AuthConfig;
use crate::handlers::collages::config as CollagesConfig;
@@ -70,6 +71,7 @@ pub fn init<R: RedisPoolInterface + 'static>(cfg: &mut web::ServiceConfig) {
.service(scope("/gifts").configure(GiftsConfig::<R>))
.service(scope("/collages").configure(CollagesConfig::<R>))
.service(scope("/css-sheets").configure(CssSheetsConfig::<R>))
.service(scope("/arcadia-settings").configure(ArcadiaSettingsConfig::<R>))
.service(scope("/tracker").configure(TrackerConfig::<R>)),
);
}

View File

@@ -36,7 +36,13 @@ pub async fn create_test_app<R: RedisPoolInterface + 'static>(
env.global_upload_factor = global_upload_factor;
env.global_download_factor = global_download_factor;
let arc = Arcadia::<R>::new(pool, Arc::new(redis_pool), env);
// Load settings from database for tests
let settings = pool
.get_arcadia_settings()
.await
.expect("failed to load arcadia settings from database");
let arc = Arcadia::<R>::new(pool, Arc::new(redis_pool), env, settings);
// TODO: CORS?
test::init_service(
@@ -70,6 +76,7 @@ pub enum TestUser {
EditUserPermissions,
LockUserClass,
ChangeUserClass,
EditArcadiaSettings,
}
impl TestUser {
@@ -96,6 +103,7 @@ impl TestUser {
TestUser::EditUserPermissions => "user_perm_edit",
TestUser::LockUserClass => "user_lock_cls",
TestUser::ChangeUserClass => "user_cls_chg",
TestUser::EditArcadiaSettings => "user_arc_set",
};
Login {

View File

@@ -1,3 +1,3 @@
-- User with locked class
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, class_locked, permissions)
VALUES (999, 'locked_user', 'locked@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c9999', 'newbie', TRUE, '{download_torrent}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, class_locked, permissions)
VALUES (999, 'locked_user', 'locked@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c9999', 'newbie', 'arcadia', TRUE, '{download_torrent}');

View File

@@ -1,5 +1,5 @@
INSERT INTO
users (banned, username, email, password_hash, registered_from_ip, passkey, class_name)
users (banned, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)
VALUES
-- passkey d2037c66dd3e13044e0d2f9b891c3837
(true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdfkjqmsdkf', 'newbie')
(true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdfkjqmsdkf', 'newbie', 'arcadia')

View File

@@ -1,83 +1,87 @@
-- Basic user for read operations and permission denial tests
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (100, 'user_basic', 'test_user@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', '{download_torrent}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (100, 'user_basic', 'test_user@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', 'arcadia', '{download_torrent}');
-- User with edit_artist permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (101, 'user_edit_art', 'test_user_edit_artist@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3838', 'newbie', '{edit_artist}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (101, 'user_edit_art', 'test_user_edit_artist@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3838', 'newbie', 'arcadia', '{edit_artist}');
-- User with edit_series permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (102, 'user_edit_ser', 'test_user_edit_series@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3839', 'newbie', '{edit_series}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (102, 'user_edit_ser', 'test_user_edit_series@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3839', 'newbie', 'arcadia', '{edit_series}');
-- User with edit_title_group_comment permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (103, 'user_edit_tgc', 'test_user_edit_title_group_comment@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383a', 'newbie', '{edit_title_group_comment}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (103, 'user_edit_tgc', 'test_user_edit_title_group_comment@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383a', 'newbie', 'arcadia', '{edit_title_group_comment}');
-- User with create_css_sheet permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (104, 'user_css_crt', 'test_user_create_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383b', 'newbie', '{create_css_sheet}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (104, 'user_css_crt', 'test_user_create_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383b', 'newbie', 'arcadia', '{create_css_sheet}');
-- User with edit_css_sheet permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (105, 'user_css_edit', 'test_user_edit_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383c', 'newbie', '{edit_css_sheet}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (105, 'user_css_edit', 'test_user_edit_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383c', 'newbie', 'arcadia', '{edit_css_sheet}');
-- User with set_default_css_sheet permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (106, 'user_css_def', 'test_user_set_default_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383d', 'newbie', '{set_default_css_sheet}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (106, 'user_css_def', 'test_user_set_default_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383d', 'newbie', 'arcadia', '{set_default_css_sheet}');
-- User with create_forum_category permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (107, 'user_cat_crt', 'test_user_create_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383e', 'newbie', '{create_forum_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (107, 'user_cat_crt', 'test_user_create_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383e', 'newbie', 'arcadia', '{create_forum_category}');
-- User with edit_forum_category permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (108, 'user_cat_edit', 'test_user_edit_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383f', 'newbie', '{edit_forum_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (108, 'user_cat_edit', 'test_user_edit_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383f', 'newbie', 'arcadia', '{edit_forum_category}');
-- User with create_forum_sub_category permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (109, 'user_sub_crt', 'test_user_create_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3840', 'newbie', '{create_forum_sub_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (109, 'user_sub_crt', 'test_user_create_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3840', 'newbie', 'arcadia', '{create_forum_sub_category}');
-- User with edit_forum_sub_category permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (110, 'user_sub_edit', 'test_user_edit_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3841', 'newbie', '{edit_forum_sub_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (110, 'user_sub_edit', 'test_user_edit_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3841', 'newbie', 'arcadia', '{edit_forum_sub_category}');
-- User with edit_forum_thread permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (111, 'user_thr_edit', 'test_user_edit_forum_thread@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3842', 'newbie', '{edit_forum_thread}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (111, 'user_thr_edit', 'test_user_edit_forum_thread@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3842', 'newbie', 'arcadia', '{edit_forum_thread}');
-- User with edit_forum_post permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (112, 'user_post_edit', 'test_user_edit_forum_post@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3843', 'newbie', '{edit_forum_post}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (112, 'user_post_edit', 'test_user_edit_forum_post@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3843', 'newbie', 'arcadia', '{edit_forum_post}');
-- User with both create and edit forum category permissions (for flow tests)
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (113, 'user_cat_flow', 'test_user_cat_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3844', 'newbie', '{create_forum_category,edit_forum_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (113, 'user_cat_flow', 'test_user_cat_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3844', 'newbie', 'arcadia', '{create_forum_category,edit_forum_category}');
-- User with both create and edit forum sub category permissions (for flow tests)
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (114, 'user_sub_flow', 'test_user_sub_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3845', 'newbie', '{create_forum_sub_category,edit_forum_sub_category}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (114, 'user_sub_flow', 'test_user_sub_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3845', 'newbie', 'arcadia', '{create_forum_sub_category,edit_forum_sub_category}');
-- User with create_user_class permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (115, 'user_cls_crt', 'test_user_create_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3846', 'newbie', '{create_user_class}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (115, 'user_cls_crt', 'test_user_create_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3846', 'newbie', 'arcadia', '{create_user_class}');
-- User with edit_user_class permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (116, 'user_cls_edit', 'test_user_edit_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3847', 'newbie', '{edit_user_class}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (116, 'user_cls_edit', 'test_user_edit_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3847', 'newbie', 'arcadia', '{edit_user_class}');
-- User with delete_user_class permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (117, 'user_cls_del', 'test_user_delete_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3848', 'newbie', '{delete_user_class}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (117, 'user_cls_del', 'test_user_delete_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3848', 'newbie', 'arcadia', '{delete_user_class}');
-- User with edit_user_permissions permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (118, 'user_perm_edit', 'test_user_edit_permissions@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3849', 'newbie', '{edit_user_permissions}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (118, 'user_perm_edit', 'test_user_edit_permissions@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3849', 'newbie', 'arcadia', '{edit_user_permissions}');
-- User with lock_user_class permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (119, 'user_lock_cls', 'test_user_lock_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384a', 'newbie', '{lock_user_class}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (119, 'user_lock_cls', 'test_user_lock_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384a', 'newbie', 'arcadia', '{lock_user_class}');
-- User with change_user_class permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions)
VALUES (120, 'user_cls_chg', 'test_user_change_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384b', 'newbie', '{change_user_class}');
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (120, 'user_cls_chg', 'test_user_change_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384b', 'newbie', 'arcadia', '{change_user_class}');
-- User with edit_arcadia_settings permission
INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions)
VALUES (121, 'user_arc_set', 'test_user_arcadia_settings@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384c', 'newbie', 'arcadia', '{edit_arcadia_settings}');

View File

@@ -0,0 +1,210 @@
pub mod common;
pub mod mocks;
use actix_web::{http::StatusCode, test};
use arcadia_api::OpenSignups;
use arcadia_storage::{connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings};
use common::{
auth_header, call_and_read_body_json, create_test_app, create_test_app_and_login, TestUser,
};
use mocks::mock_redis::MockRedisPool;
use sqlx::PgPool;
use std::sync::Arc;
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_staff_can_get_arcadia_settings(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) = create_test_app_and_login(
pool,
MockRedisPool::default(),
100,
100,
TestUser::EditArcadiaSettings,
)
.await;
let req = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.to_request();
let settings = call_and_read_body_json::<ArcadiaSettings, _>(&service, req).await;
assert_eq!(settings.user_class_name_on_signup, "newbie");
assert_eq!(settings.default_css_sheet_name, "arcadia");
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_regular_user_cannot_get_arcadia_settings(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) =
create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard)
.await;
let req = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_get_arcadia_settings_requires_auth(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let service = create_test_app(
pool,
MockRedisPool::default(),
OpenSignups::Disabled,
100,
100,
)
.await;
let req = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.uri("/api/arcadia-settings")
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_staff_can_update_arcadia_settings(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) = create_test_app_and_login(
pool.clone(),
MockRedisPool::default(),
100,
100,
TestUser::EditArcadiaSettings,
)
.await;
let updated_settings = ArcadiaSettings {
user_class_name_on_signup: "newbie".to_string(),
default_css_sheet_name: "arcadia".to_string(),
};
let req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.set_json(&updated_settings)
.to_request();
let settings = call_and_read_body_json::<ArcadiaSettings, _>(&service, req).await;
assert_eq!(settings.user_class_name_on_signup, "newbie");
assert_eq!(settings.default_css_sheet_name, "arcadia");
// Verify the settings were actually updated in the database
let db_settings = pool.get_arcadia_settings().await.unwrap();
assert_eq!(db_settings.user_class_name_on_signup, "newbie");
assert_eq!(db_settings.default_css_sheet_name, "arcadia");
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_regular_user_cannot_update_arcadia_settings(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) =
create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard)
.await;
let updated_settings = ArcadiaSettings {
user_class_name_on_signup: "newbie".to_string(),
default_css_sheet_name: "arcadia".to_string(),
};
let req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.set_json(&updated_settings)
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_update_arcadia_settings_requires_auth(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let service = create_test_app(
pool,
MockRedisPool::default(),
OpenSignups::Disabled,
100,
100,
)
.await;
let updated_settings = ArcadiaSettings {
user_class_name_on_signup: "newbie".to_string(),
default_css_sheet_name: "arcadia".to_string(),
};
let req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.uri("/api/arcadia-settings")
.set_json(&updated_settings)
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
async fn test_update_arcadia_settings_updates_in_memory_cache(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) = create_test_app_and_login(
pool,
MockRedisPool::default(),
100,
100,
TestUser::EditArcadiaSettings,
)
.await;
// First, get the current settings
let get_req = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.to_request();
let initial_settings = call_and_read_body_json::<ArcadiaSettings, _>(&service, get_req).await;
assert_eq!(initial_settings.user_class_name_on_signup, "newbie");
// Update the settings
let updated_settings = ArcadiaSettings {
user_class_name_on_signup: "newbie".to_string(),
default_css_sheet_name: "arcadia".to_string(),
};
let update_req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.set_json(&updated_settings)
.to_request();
let updated = call_and_read_body_json::<ArcadiaSettings, _>(&service, update_req).await;
assert_eq!(updated.user_class_name_on_signup, "newbie");
// Get the settings again to verify the in-memory cache was updated
let get_req_after = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/arcadia-settings")
.to_request();
let final_settings =
call_and_read_body_json::<ArcadiaSettings, _>(&service, get_req_after).await;
assert_eq!(final_settings.user_class_name_on_signup, "newbie");
assert_eq!(final_settings.default_css_sheet_name, "arcadia");
}

View File

@@ -203,63 +203,6 @@ async fn test_regular_user_cannot_edit_css_sheet(pool: PgPool) {
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[sqlx::test(
fixtures("with_test_users", "with_test_css_sheets"),
migrations = "../storage/migrations"
)]
async fn test_staff_can_set_default_css_sheet(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) = create_test_app_and_login(
pool,
MockRedisPool::default(),
100,
100,
TestUser::SetDefaultCssSheet,
)
.await;
// Set a fixture sheet as default
let req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/css-sheets/test_sheet_1/default")
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::OK);
// Get the CSS sheets list and verify the default name is updated
let req = test::TestRequest::get()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/css-sheets")
.to_request();
let sheets = call_and_read_body_json::<CssSheetsEnriched, _>(&service, req).await;
assert_eq!(sheets.default_sheet_name, "test_sheet_1");
}
#[sqlx::test(
fixtures("with_test_users", "with_test_css_sheets"),
migrations = "../storage/migrations"
)]
async fn test_regular_user_cannot_set_default_css_sheet(pool: PgPool) {
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
let (service, user) =
create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard)
.await;
let req = test::TestRequest::put()
.insert_header(("X-Forwarded-For", "10.10.4.88"))
.insert_header(auth_header(&user.token))
.uri("/api/css-sheets/test_sheet_1/default")
.to_request();
let resp = test::call_service(&service, req).await;
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[sqlx::test(
fixtures("with_test_users", "with_test_css_sheets"),
migrations = "../storage/migrations"

View File

@@ -341,6 +341,12 @@ pub enum Error {
#[error("could not find css sheets")]
CouldNotFindCssSheets(#[source] sqlx::Error),
#[error("could not find arcadia settings")]
CouldNotFindArcadiaSettings(#[source] sqlx::Error),
#[error("could not update arcadia settings")]
CouldNotUpdateArcadiaSettings(#[source] sqlx::Error),
#[error("error getting musicbrainz data")]
ErrorGettingMusicbrainzData(#[source] musicbrainz_rs::Error),

View File

@@ -62,7 +62,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -0,0 +1,26 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT user_class_name_on_signup, default_css_sheet_name\n FROM arcadia_settings\n LIMIT 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "user_class_name_on_signup",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "default_css_sheet_name",
"type_info": "Varchar"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false
]
},
"hash": "0ce494c056dbc6166b9a68fd0ddf6e0319adf7db044ea27e1c391c1d5a29f230"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING *\n ",
"query": "\n INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n RETURNING *\n ",
"describe": {
"columns": [
{
@@ -142,7 +142,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}
@@ -269,6 +270,7 @@
"Varchar",
"Inet",
"Varchar",
"Varchar",
"Varchar"
]
},
@@ -315,5 +317,5 @@
false
]
},
"hash": "79ed9d5eee7f6d19a6ad85df3666c83828e0e162b8496533552b92efd807cd29"
"hash": "1a38ee4ed6ef71e6720ca19ab50bb2e53a2dec75c9fc53bb39135c07226e7563"
}

View File

@@ -142,7 +142,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT column_default\n FROM information_schema.columns\n WHERE table_name='users' AND column_name='css_sheet_name';\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "column_default",
"type_info": "Varchar"
}
],
"parameters": {
"Left": []
},
"nullable": [
true
]
},
"hash": "68e3b62b9a7961d2145823f7980576bf90ffc9f09fcb8ae7e25d63627b8a9648"
}

View File

@@ -62,7 +62,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}
@@ -127,7 +128,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -57,7 +57,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE arcadia_settings\n SET user_class_name_on_signup = $1,\n default_css_sheet_name = $2\n RETURNING *\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "user_class_name_on_signup",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "default_css_sheet_name",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": [
false,
false
]
},
"hash": "8a8c1264be51c4286ad8a85a2abb158ee499582e3212920d741cbd8b4494ee2b"
}

View File

@@ -59,7 +59,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -142,7 +142,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -62,7 +62,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}
@@ -126,7 +127,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -62,7 +62,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT quote_literal($1) AS quoted",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "quoted",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
null
]
},
"hash": "bc7a8046601ae0830b7702bc03b83ddd0f15cc6fc05a317246a06aa293e0d93f"
}

View File

@@ -142,7 +142,8 @@
"warn_user",
"edit_user",
"create_wiki_article",
"edit_wiki_article"
"edit_wiki_article",
"edit_arcadia_settings"
]
}
}

View File

@@ -41,7 +41,8 @@ CREATE TYPE user_permissions_enum AS ENUM (
'warn_user',
'edit_user',
'create_wiki_article',
'edit_wiki_article'
'edit_wiki_article',
'edit_arcadia_settings'
);
CREATE TABLE user_classes (
name VARCHAR(30) UNIQUE NOT NULL,
@@ -90,13 +91,12 @@ CREATE TABLE users (
warned BOOLEAN NOT NULL DEFAULT FALSE,
banned BOOLEAN NOT NULL DEFAULT FALSE,
staff_note TEXT NOT NULL DEFAULT '',
-- the default sheet for a new user is updated in the rust repository as it's too tricky/impossible with triggers
css_sheet_name VARCHAR(30) NOT NULL DEFAULT 'arcadia',
css_sheet_name VARCHAR(30) NOT NULL,
UNIQUE(passkey)
);
INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name)
VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'newbie');
INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)
VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'newbie', 'arcadia');
CREATE TABLE css_sheets (
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
created_by_id INT NOT NULL REFERENCES users(id),
@@ -113,6 +113,12 @@ ADD CONSTRAINT fk_users_css_sheet
FOREIGN KEY (css_sheet_name)
REFERENCES css_sheets(name)
ON UPDATE CASCADE;
CREATE TABLE arcadia_settings (
user_class_name_on_signup VARCHAR(30) NOT NULL REFERENCES user_classes(name) ON UPDATE CASCADE,
default_css_sheet_name VARCHAR(30) NOT NULL REFERENCES css_sheets(name) ON UPDATE CASCADE
);
INSERT INTO arcadia_settings (user_class_name_on_signup, default_css_sheet_name)
VALUES ('newbie', 'arcadia');
CREATE TABLE api_keys (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

View File

@@ -29,11 +29,11 @@ VALUES ('staff', '{create_user_class,edit_user_class,delete_user_class,edit_user
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: arcadia
--
INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '111111111111111111111111111111111', false, false, '');
INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '22222222222222222222222222222222', false, false, '''''');
INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '33333333333333333333333333333333', false, false, '''''');
INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '55555555555555555555555555555555', false, false, '''''');
INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-11-28 17:29:24.968105+00', 'newbie', false, '{create_user_class,edit_user_class,delete_user_class,edit_user_permissions,change_user_class,lock_user_class,upload_torrent,download_torrent,create_torrent_request,immune_activity_pruning,edit_title_group,edit_title_group_comment,edit_edition_group,edit_torrent,edit_artist,edit_collage,edit_series,edit_torrent_request,edit_forum_post,edit_forum_thread,edit_forum_sub_category,edit_forum_category,create_forum_category,create_forum_sub_category,create_forum_thread,create_forum_post,send_pm,create_css_sheet,edit_css_sheet,set_default_css_sheet,read_staff_pm,reply_staff_pm,resolve_staff_pm,unresolve_staff_pm,delete_title_group_tag,edit_title_group_tag,delete_torrent,get_user_application,update_user_application,warn_user,edit_user,create_wiki_article,edit_wiki_article}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '44444444444444444444444444444444', false, false, '''''');
INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '111111111111111111111111111111111', false, false, '', 'arcadia');
INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '22222222222222222222222222222222', false, false, '''''', 'arcadia');
INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '33333333333333333333333333333333', false, false, '''''', 'arcadia');
INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '55555555555555555555555555555555', false, false, '''''', 'arcadia');
INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-11-28 17:29:24.968105+00', 'newbie', false, '{create_user_class,edit_user_class,delete_user_class,edit_user_permissions,change_user_class,lock_user_class,upload_torrent,download_torrent,create_torrent_request,immune_activity_pruning,edit_title_group,edit_title_group_comment,edit_edition_group,edit_torrent,edit_artist,edit_collage,edit_series,edit_torrent_request,edit_forum_post,edit_forum_thread,edit_forum_sub_category,edit_forum_category,create_forum_category,create_forum_sub_category,create_forum_thread,create_forum_post,send_pm,create_css_sheet,edit_css_sheet,set_default_css_sheet,read_staff_pm,reply_staff_pm,resolve_staff_pm,unresolve_staff_pm,delete_title_group_tag,edit_title_group_tag,delete_torrent,get_user_application,update_user_application,warn_user,edit_user,create_wiki_article,edit_wiki_article}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '44444444444444444444444444444444', false, false, '''''', 'arcadia');
--

View File

@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
use sqlx::prelude::FromRow;
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, FromRow, ToSchema)]
pub struct ArcadiaSettings {
pub user_class_name_on_signup: String,
pub default_css_sheet_name: String,
}

View File

@@ -1,3 +1,4 @@
pub mod arcadia_settings;
pub mod artist;
pub mod collage;
pub mod common;

View File

@@ -102,6 +102,7 @@ pub enum UserPermission {
EditUserPermissions,
LockUserClass,
ChangeUserClass,
EditArcadiaSettings,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]

View File

@@ -0,0 +1,43 @@
use crate::{connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings};
use arcadia_common::error::{Error, Result};
use std::borrow::Borrow;
impl ConnectionPool {
pub async fn get_arcadia_settings(&self) -> Result<ArcadiaSettings> {
let settings = sqlx::query_as!(
ArcadiaSettings,
r#"
SELECT user_class_name_on_signup, default_css_sheet_name
FROM arcadia_settings
LIMIT 1
"#,
)
.fetch_one(self.borrow())
.await
.map_err(Error::CouldNotFindArcadiaSettings)?;
Ok(settings)
}
pub async fn update_arcadia_settings(
&self,
settings: &ArcadiaSettings,
) -> Result<ArcadiaSettings> {
let updated_settings = sqlx::query_as!(
ArcadiaSettings,
r#"
UPDATE arcadia_settings
SET user_class_name_on_signup = $1,
default_css_sheet_name = $2
RETURNING *
"#,
settings.user_class_name_on_signup,
settings.default_css_sheet_name
)
.fetch_one(self.borrow())
.await
.map_err(Error::CouldNotUpdateArcadiaSettings)?;
Ok(updated_settings)
}
}

View File

@@ -37,6 +37,7 @@ impl ConnectionPool {
invitation: &Invitation,
open_signups: &bool,
user_class_name: &str,
css_sheet_name: &str,
) -> Result<User> {
let rng = rand::rng();
@@ -55,8 +56,8 @@ impl ConnectionPool {
let registered_user = sqlx::query_as_unchecked!(
User,
r#"
INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name)
VALUES ($1, $2, $3, $4, $5, $6)
INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
"#,
&user.username,
@@ -64,7 +65,8 @@ impl ConnectionPool {
password_hash,
from_ip,
passkey,
user_class_name
user_class_name,
css_sheet_name
)
.fetch_one(self.borrow())
.await

View File

@@ -1,6 +1,6 @@
use crate::{
connection_pool::ConnectionPool,
models::css_sheet::{CssSheet, CssSheetsEnriched, EditedCssSheet, UserCreatedCssSheet},
models::css_sheet::{CssSheet, EditedCssSheet, UserCreatedCssSheet},
};
use arcadia_common::error::{Error, Result};
use std::borrow::Borrow;
@@ -30,7 +30,7 @@ impl ConnectionPool {
Ok(css_sheet)
}
pub async fn find_css_sheets(&self) -> Result<CssSheetsEnriched> {
pub async fn find_css_sheets(&self) -> Result<Vec<CssSheet>> {
let sheets = sqlx::query_as!(
CssSheet,
r#"
@@ -41,35 +41,7 @@ impl ConnectionPool {
.await
.map_err(Error::CouldNotFindCssSheets)?;
let default_sheet_name = Self::find_default_css_sheet_name(self).await?;
Ok(CssSheetsEnriched {
css_sheets: sheets,
default_sheet_name,
})
}
pub async fn find_default_css_sheet_name(&self) -> Result<String> {
let mut default_sheet_name = sqlx::query_scalar!(
r#"
SELECT column_default
FROM information_schema.columns
WHERE table_name='users' AND column_name='css_sheet_name';
"#,
)
.fetch_one(self.borrow())
.await
.map_err(Error::CouldNotFindCssSheets)?
.unwrap();
default_sheet_name = regex::Regex::new(r#"^'(.*)'::\s*character varying$"#)
.unwrap()
.captures(&default_sheet_name)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str().to_string())
.unwrap();
Ok(default_sheet_name)
Ok(sheets)
}
pub async fn find_css_sheet(&self, name: &str) -> Result<CssSheet> {
@@ -88,8 +60,6 @@ impl ConnectionPool {
}
pub async fn update_css_sheet(&self, form: &EditedCssSheet) -> Result<CssSheet> {
let default_sheet_name = Self::find_default_css_sheet_name(self).await?;
let css_sheet = sqlx::query_as!(
CssSheet,
r#"
@@ -107,31 +77,9 @@ impl ConnectionPool {
.await
.map_err(Error::CssSheetNotFound)?;
if form.old_name == default_sheet_name {
Self::set_default_css_sheet(self, &form.name).await?;
}
Ok(css_sheet)
}
pub async fn set_default_css_sheet(&self, sheet_name: &str) -> Result<()> {
let sql = format!(
"ALTER TABLE users ALTER COLUMN css_sheet_name SET DEFAULT {}",
sqlx::query_scalar!("SELECT quote_literal($1) AS quoted", sheet_name)
.fetch_one(self.borrow())
.await
.map_err(Error::CouldNotUpdateDefaultCssSheet)?
.unwrap()
);
sqlx::query(&sql)
.execute(self.borrow())
.await
.map_err(Error::CouldNotUpdateDefaultCssSheet)?;
Ok(())
}
pub async fn set_css_sheet_for_user(
&self,
user_id: i32,

View File

@@ -1,3 +1,4 @@
pub mod arcadia_settings_repository;
pub mod artist_repository;
pub mod auth_repository;
pub mod collage_repository;

View File

@@ -1,6 +1,6 @@
INSERT INTO
users (username, email, password_hash, registered_from_ip, passkey, class_name)
users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)
VALUES
('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie');
('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', 'arcadia');