feat: add host id allocation

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
2026-07-19 13:05:26 +08:00
parent bfcc98b50e
commit c72134a89e
8 changed files with 124 additions and 9 deletions
+23 -3
View File
@@ -15,7 +15,7 @@ use semios_account::{
record::{GroupInfo, SecureTag, UserInfo},
secret::Secret,
};
use std::{fmt::Debug, path::PathBuf, pin::Pin, sync::Arc};
use std::{fmt::Debug, path::PathBuf, pin::Pin, range::RangeInclusive, sync::Arc};
use uuid::Uuid;
pub async fn launch_server(app_state: Arc<AppState>) -> anyhow::Result<()> {
@@ -215,11 +215,21 @@ async fn create_user(
args: CreateUserArgs,
) -> Result<(), Error> {
require_secure_tags(&state, caller, &[SecureTag::CreateUser]).await?;
let host_uid = match args.host_uid {
HostIdRule::Unallocated => None,
HostIdRule::Manual(x) => Some(x),
HostIdRule::Auto(a, b) => Some(
state
.local_db
.allocate_host_uid(RangeInclusive::from(a..=b))
.ok_or(Error::AllocHostId)?,
),
};
let user_info = UserInfo {
uuid: Uuid::new_v7(uuid::Timestamp::now(uuid::ContextV7::new())),
fullname: args.fullname.unwrap_or_else(|| args.username.clone()),
username: args.username,
host_uid: args.host_uid,
host_uid,
description: args.description,
secure_tags: args.secure_tags,
extra_records: args.extra_records,
@@ -241,11 +251,21 @@ async fn create_group(
args: CreateGroupArgs,
) -> Result<(), Error> {
require_secure_tags(&state, caller, &[SecureTag::CreateUser]).await?;
let host_gid = match args.host_gid {
HostIdRule::Unallocated => None,
HostIdRule::Manual(x) => Some(x),
HostIdRule::Auto(a, b) => Some(
state
.local_db
.allocate_host_gid(RangeInclusive::from(a..=b))
.ok_or(Error::AllocHostId)?,
),
};
let group_info = GroupInfo {
uuid: Uuid::new_v7(uuid::Timestamp::now(uuid::ContextV7::new())),
fullname: args.fullname.unwrap_or_else(|| args.groupname.clone()),
groupname: args.groupname,
host_gid: args.host_gid,
host_gid,
description: args.description,
creation_time: timestamp_s(),
};
+33 -2
View File
@@ -1,4 +1,4 @@
use rusqlite::params;
use rusqlite::{OptionalExtension, ToSql, params, types::FromSql};
use semios_account::{
auth::{UserAuthMethodFlags, UserAuthMethodRecord},
error::Error,
@@ -6,7 +6,7 @@ use semios_account::{
secret::{Secret, SecurityFlags},
};
use serde::de::DeserializeOwned;
use std::{path::Path, sync::Mutex};
use std::{path::Path, range::RangeInclusive, sync::Mutex};
use uuid::Uuid;
/// Schema of the database.
@@ -285,6 +285,18 @@ impl LocalDb {
self._find_uuid_by("group", "group_host_gid", &host_gid.to_string())
}
pub fn allocate_host_uid(&self, range: RangeInclusive<HostUid>) -> Option<HostUid> {
self._allocate_host_id_max("user", "user_host_uid", range)
.ok()
.map(|x| x.map(|y| y + 1).unwrap_or(range.start))
}
pub fn allocate_host_gid(&self, range: RangeInclusive<HostGid>) -> Option<HostGid> {
self._allocate_host_id_max("group", "group_host_gid", range)
.ok()
.map(|x| x.map(|y| y + 1).unwrap_or(range.start))
}
pub fn is_first_run(&self) -> bool {
!self._kv_get("initialized").is_some()
}
@@ -349,6 +361,25 @@ impl LocalDb {
.ok()
}
fn _allocate_host_id_max<T: FromSql + ToSql + PartialEq>(
&self,
table: &'static str,
column: &'static str,
range: RangeInclusive<T>,
) -> rusqlite::Result<Option<T>> {
let lock = self.0.lock().unwrap();
let mut stmt = lock.prepare_cached(&format!(
"SELECT MAX(CAST(\"{column}\" AS INTEGER))
FROM \"{table}\"
WHERE CAST(\"{column}\" AS INTEGER) BETWEEN ?1 AND ?2"
))?;
let max: Option<T> = stmt.query_one(params![range.start, range.last], |row| row.get(0))?;
if max == Some(range.last) {
return Ok(None);
}
Ok(max)
}
fn _kv_get(&self, key: &str) -> Option<String> {
let lock = self.0.lock().unwrap();
let mut stmt = lock