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:
@@ -435,6 +435,39 @@ impl Default for WopiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Nextcloud compatibility configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NextcloudConfig {
|
||||
/// Whether the Nextcloud compatibility layer is enabled
|
||||
pub enabled: bool,
|
||||
/// Instance ID suffix for oc:id formatting (e.g., "ocnca")
|
||||
pub instance_id: String,
|
||||
/// Emulated Nextcloud version (major.minor.patch).
|
||||
/// Clients use this to decide which features to enable.
|
||||
pub emulated_version: (u32, u32, u32),
|
||||
/// Login Flow v2 token TTL in seconds (default: 600 = 10 minutes)
|
||||
pub login_flow_ttl_secs: u64,
|
||||
}
|
||||
|
||||
impl Default for NextcloudConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
instance_id: "ocnca".to_string(),
|
||||
emulated_version: (28, 0, 4),
|
||||
login_flow_ttl_secs: 600,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NextcloudConfig {
|
||||
/// Version string, e.g. "28.0.4".
|
||||
pub fn version_string(&self) -> String {
|
||||
let (maj, min, pat) = self.emulated_version;
|
||||
format!("{}.{}.{}", maj, min, pat)
|
||||
}
|
||||
}
|
||||
|
||||
/// Feature configuration (feature flags)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FeaturesConfig {
|
||||
@@ -488,6 +521,8 @@ pub struct AppConfig {
|
||||
pub oidc: OidcConfig,
|
||||
/// WOPI configuration
|
||||
pub wopi: WopiConfig,
|
||||
/// Nextcloud compatibility configuration
|
||||
pub nextcloud: NextcloudConfig,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
@@ -507,6 +542,7 @@ impl Default for AppConfig {
|
||||
features: FeaturesConfig::default(),
|
||||
oidc: OidcConfig::default(),
|
||||
wopi: WopiConfig::default(),
|
||||
nextcloud: NextcloudConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,6 +833,30 @@ impl AppConfig {
|
||||
tracing::info!("WOPI secret not set, falling back to JWT secret");
|
||||
}
|
||||
|
||||
// Nextcloud compatibility configuration
|
||||
if let Ok(v) = env::var("OXICLOUD_NEXTCLOUD_ENABLED") {
|
||||
config.nextcloud.enabled = v.parse::<bool>().unwrap_or(false);
|
||||
}
|
||||
if let Ok(v) = env::var("OXICLOUD_NEXTCLOUD_INSTANCE_ID") {
|
||||
let trimmed = v.trim();
|
||||
if !trimmed.is_empty() {
|
||||
config.nextcloud.instance_id = trimmed.to_string();
|
||||
}
|
||||
}
|
||||
if let Ok(v) = env::var("OXICLOUD_NEXTCLOUD_VERSION") {
|
||||
// Expected format: "28.0.4"
|
||||
let parts: Vec<&str> = v.trim().splitn(3, '.').collect();
|
||||
if parts.len() == 3
|
||||
&& let (Ok(maj), Ok(min), Ok(pat)) = (
|
||||
parts[0].parse::<u32>(),
|
||||
parts[1].parse::<u32>(),
|
||||
parts[2].parse::<u32>(),
|
||||
)
|
||||
{
|
||||
config.nextcloud.emulated_version = (maj, min, pat);
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user