perf: migrate all user/session/auth IDs from VARCHAR(36) to native UUID

- Schema: all ~15 VARCHAR(36) columns → UUID with DEFAULT gen_random_uuid()
- Domain entities: User, Session, DeviceCode, AppPassword, Share → id: Uuid
- DTOs: CurrentUser.id → Uuid (API boundary DTOs keep String for JSON)
- Auth middleware: parse JWT claims.sub (String) → Uuid at boundary
- All repository traits, port traits, service impls updated end-to-end
- Handlers: pass Uuid by value (Copy, 16 bytes) instead of String refs
- Settings chain: updated_by column → Uuid (was text, caused setup crash)
- Removed ~650 lines of String↔Uuid conversion boilerplate
- Eliminates per-request heap allocations for ID cloning
- 16-byte binary comparison vs 36-byte string comparison in all queries
- Native UUID indexing in PostgreSQL (btree on 16 bytes vs 36-char text)

85 files changed, 1090 insertions(+), 1739 deletions(-)
This commit is contained in:
Diocrafts
2026-03-07 14:59:32 +01:00
parent 9f08460027
commit 06ed0455ce
85 changed files with 1090 additions and 1739 deletions
@@ -22,6 +22,7 @@ use crate::infrastructure::repositories::pg::DeviceCodePgRepository;
use crate::infrastructure::repositories::pg::SessionPgRepository;
use crate::infrastructure::repositories::pg::UserPgRepository;
use crate::infrastructure::services::jwt_service::JwtTokenService;
use uuid::Uuid;
/// Default device code lifetime: 15 minutes (RFC 8628 recommends 5-30 min).
const DEVICE_CODE_LIFETIME_SECS: i64 = 900;
@@ -155,7 +156,7 @@ impl DeviceAuthService {
///
/// * `user_code` — the code from the verification page
/// * `user_id` — the authenticated user's ID (from session/JWT)
pub async fn approve(&self, user_code: &str, user_id: &str) -> Result<(), DomainError> {
pub async fn approve(&self, user_code: &str, user_id: Uuid) -> Result<(), DomainError> {
let normalized = user_code.trim().to_uppercase().replace(' ', "");
let mut dc = self
@@ -180,7 +181,7 @@ impl DeviceAuthService {
// Persist refresh token as a session
let session = Session::new(
user_id.to_string(),
user_id,
refresh_token.clone(),
None, // ip_address
Some(format!("device:{}", dc.client_name())), // user_agent
@@ -189,7 +190,7 @@ impl DeviceAuthService {
self.session_storage.create_session(session).await?;
// Store tokens on the device code entity
dc.authorize(user_id.to_string(), access_token, refresh_token);
dc.authorize(user_id, access_token, refresh_token);
self.device_code_storage.update_device_code(dc).await?;
tracing::info!(
@@ -297,7 +298,7 @@ impl DeviceAuthService {
pub async fn list_user_devices(
&self,
user_id: &str,
user_id: Uuid,
) -> Result<Vec<DeviceInfoDto>, DomainError> {
let codes = self.device_code_storage.list_by_user(user_id).await?;
Ok(codes
@@ -318,7 +319,7 @@ impl DeviceAuthService {
// 8. Revoke — user revokes a device authorization
// ========================================================================
pub async fn revoke_device(&self, device_id: &str, user_id: &str) -> Result<(), DomainError> {
pub async fn revoke_device(&self, device_id: Uuid, user_id: Uuid) -> Result<(), DomainError> {
// Verify ownership before deleting
let devices = self.device_code_storage.list_by_user(user_id).await?;
let found = devices.iter().any(|d| d.id() == device_id);