feat(nextcloud): add Nextcloud-compatible API layer
Implement a complete Nextcloud client compatibility layer so that Nextcloud desktop/mobile sync clients can connect to OxiCloud. Key additions: - Login Flow v2 (device auth) with OIDC bridge support - WebDAV handler compatible with Nextcloud clients (PROPFIND, GET, PUT, DELETE, MKCOL, MOVE, COPY, HEAD, PROPPATCH) - OCS API endpoints (user info, capabilities, notifications stubs, sharees, unified search) - Basic Auth middleware with app password verification, account lockout integration, and blake3-keyed auth cache - App password management: create, list, revoke via both native API (JWT-authenticated profile page) and Nextcloud OCS endpoints - Nextcloud file ID mapping (oc:fileid) with persistent DB storage - Chunked upload support (Nextcloud v2 chunking protocol) - Trashbin WebDAV interface - Avatar (SVG placeholder) and preview (redirect) handlers - User profile page with app password management UI - URL user validation on all DAV routes (403 on mismatch) - Database schema for app_passwords and nextcloud_object_ids tables All services are behind a `nextcloud.enabled` config flag and cleanly separated under src/interfaces/nextcloud/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
// Shared display helpers for DTOs.
|
||||
//
|
||||
// These functions centralise the mime→icon / mime→category / size→human-string
|
||||
// logic so that every API response carries pre-computed display fields and the
|
||||
// frontend does **not** need to duplicate these mappings.
|
||||
//
|
||||
// The approach is: try MIME first (specific matches beat prefix matches),
|
||||
// then fall back to the file extension when the MIME is generic
|
||||
// (`application/octet-stream` or empty).
|
||||
//! Shared display helpers for DTOs.
|
||||
//!
|
||||
//! These functions centralise the mime→icon / mime→category / size→human-string
|
||||
//! logic so that every API response carries pre-computed display fields and the
|
||||
//! frontend does **not** need to duplicate these mappings.
|
||||
//!
|
||||
//! The approach is: try MIME first (specific matches beat prefix matches),
|
||||
//! then fall back to the file extension when the MIME is generic
|
||||
//! (`application/octet-stream` or empty).
|
||||
|
||||
// ─── Private: extract lowercase extension from a filename ────────────
|
||||
|
||||
fn ext_of(name: &str) -> Option<&str> {
|
||||
let name = name.rsplit('/').next().unwrap_or(name); // strip path
|
||||
let after_dot = name.rsplit('.').next()?;
|
||||
|
||||
@@ -87,6 +87,30 @@ pub struct CurrentUser {
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// App Password DTOs
|
||||
// ============================================================================
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateAppPasswordDto {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AppPasswordCreatedDto {
|
||||
pub id: String,
|
||||
pub label: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AppPasswordDto {
|
||||
pub id: String,
|
||||
pub label: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_used_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// OIDC DTOs
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user