From 6023bca2e85a9dcf60839b58c20233a982a2156a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 17:13:49 +0000 Subject: [PATCH] fix(build): render Nextcloud login page via askama, not include_str!(OUT_DIR) The SvelteKit migration gated build.rs's static-dist/OUT_DIR generation behind OXICLOUD_RUST_ASSETS=1 (early return), but login_v2_handler.rs still embedded the page with `include_str!(concat!(env!("OUT_DIR"), "/nextcloud-login.html"))`. With OXICLOUD_RUST_ASSETS unset (the default), that file is never written to OUT_DIR, so a clean `cargo build` failed to compile. (#489) Migrate the page off include_str! to an askama template (templates/nextcloud/login.html), mirroring the existing DrivePickerTemplate in the same handler. This drops the only compile-time dependency on the legacy build.rs pipeline, so the OXICLOUD_RUST_ASSETS=1 CI workaround is no longer needed and is removed from ci.yml, load-smoke.yml and load-nightly.yml. Also fix the second failure on #489: with OXICLOUD_RUST_ASSETS=1 the release pipeline panicked in copy_dir_recursive because `static/locales` is now a symlink to frontend/static/locales. entry.file_type() reports the link itself (not its target), so the symlinked directory was routed to fs::copy and failed with "the source path is neither a regular file nor a symlink to a regular file". Classify entries with fs::metadata, which follows symlinks, so symlinked directories are traversed. static/nextcloud-login.html is removed (its content moved into the template; no other consumer) and dropped from build.rs HTML_INCLUDE. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CNCEMN6fC2xSmxqCVbstkd --- .github/workflows/ci.yml | 6 ----- .github/workflows/load-nightly.yml | 2 -- .github/workflows/load-smoke.yml | 2 -- build.rs | 12 ++++++--- src/interfaces/nextcloud/login_v2_handler.rs | 21 +++++++++++---- .../nextcloud/login.html | 26 +++++++++---------- 6 files changed, 38 insertions(+), 31 deletions(-) rename static/nextcloud-login.html => templates/nextcloud/login.html (87%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14233d08..e06283ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,12 +14,6 @@ env: CARGO_TERM_COLOR: always RUSTFLAGS: "-Dwarnings" DATABASE_URL: "postgres://postgres:postgres@localhost/oxicloud_test" - # Re-enable the legacy build.rs static-dist + OUT_DIR HTML pipeline. - # Required while login_v2_handler.rs still uses - # `include_str!(concat!(env!("OUT_DIR"), "/nextcloud-login.html"))`; - # without this, every cargo job fails to compile that file. - # Remove once the Nextcloud login page moves off include_str! (askama). - OXICLOUD_RUST_ASSETS: "1" jobs: diff --git a/.github/workflows/load-nightly.yml b/.github/workflows/load-nightly.yml index 0c09bcce..31e16c73 100644 --- a/.github/workflows/load-nightly.yml +++ b/.github/workflows/load-nightly.yml @@ -22,8 +22,6 @@ on: env: CARGO_TERM_COLOR: always - # See ci.yml — required while login_v2_handler.rs uses include_str! against OUT_DIR. - OXICLOUD_RUST_ASSETS: "1" jobs: load: diff --git a/.github/workflows/load-smoke.yml b/.github/workflows/load-smoke.yml index 931bdeec..330f5536 100644 --- a/.github/workflows/load-smoke.yml +++ b/.github/workflows/load-smoke.yml @@ -18,8 +18,6 @@ on: env: CARGO_TERM_COLOR: always - # See ci.yml — required while login_v2_handler.rs uses include_str! against OUT_DIR. - OXICLOUD_RUST_ASSETS: "1" jobs: smoke: diff --git a/build.rs b/build.rs index 64333458..98a07fed 100644 --- a/build.rs +++ b/build.rs @@ -25,7 +25,6 @@ const HTML_INCLUDE: &[&str] = &[ "profile.html", "admin.html", "device-verify.html", - "nextcloud-login.html", "share.html", ]; @@ -1239,14 +1238,21 @@ fn fnv_hash(data: &[u8]) -> String { format!("{h:016x}") } -/// Recursively copy a directory tree. +/// Recursively copy a directory tree, following symlinks. +/// +/// `fs::metadata` (not `entry.file_type()`) is used to classify each entry so +/// that symlinked directories are traversed into rather than handed to +/// `fs::copy`. `static/locales` is a symlink to `frontend/static/locales`; +/// `entry.file_type()` reports the link itself, so the old code routed it to +/// the `fs::copy` branch and failed with "the source path is neither a regular +/// file nor a symlink to a regular file". fn copy_dir_recursive(src: &Path, dst: &Path) -> io::Result<()> { fs::create_dir_all(dst)?; for entry in fs::read_dir(src)? { let entry = entry?; let src_path = entry.path(); let dst_path = dst.join(entry.file_name()); - if entry.file_type()?.is_dir() { + if fs::metadata(&src_path)?.is_dir() { copy_dir_recursive(&src_path, &dst_path)?; } else { fs::copy(&src_path, &dst_path)?; diff --git a/src/interfaces/nextcloud/login_v2_handler.rs b/src/interfaces/nextcloud/login_v2_handler.rs index 810fc773..5eda15da 100644 --- a/src/interfaces/nextcloud/login_v2_handler.rs +++ b/src/interfaces/nextcloud/login_v2_handler.rs @@ -29,6 +29,14 @@ struct DrivePickerTemplate { drives: Vec, } +/// The Nextcloud Login Flow v2 "Grant Access" page. Rendered server-side via +/// askama (no template variables — the username/password are collected by the +/// embedded form) instead of `include_str!` so the build no longer depends on +/// the legacy `build.rs` static-asset pipeline / `OUT_DIR`. +#[derive(Template)] +#[template(path = "nextcloud/login.html")] +struct NextcloudLoginTemplate; + // Home identification is via `position_of_user_home_root_folder` from // `domain::repositories::drive_repository` — a generic helper that // keys off `drives.default_for_user == user_id` rather than folder @@ -36,7 +44,7 @@ struct DrivePickerTemplate { // picker UX. /// Serve an HTML page with a Content-Security-Policy header as defense-in-depth. -fn html_with_csp(html: &'static str) -> Response { +fn html_with_csp(html: String) -> Response { ( [( header::CONTENT_SECURITY_POLICY, @@ -160,10 +168,13 @@ pub async fn handle_login_page( return StatusCode::NOT_FOUND.into_response(); } - html_with_csp(include_str!(concat!( - env!("OUT_DIR"), - "/nextcloud-login.html" - ))) + match NextcloudLoginTemplate.render() { + Ok(html) => html_with_csp(html), + Err(e) => { + tracing::error!(error = %e, "Login Flow v2: login page template render failed"); + StatusCode::INTERNAL_SERVER_ERROR.into_response() + } + } } pub async fn handle_login_submit( diff --git a/static/nextcloud-login.html b/templates/nextcloud/login.html similarity index 87% rename from static/nextcloud-login.html rename to templates/nextcloud/login.html index 708e31b9..2d7c499a 100644 --- a/static/nextcloud-login.html +++ b/templates/nextcloud/login.html @@ -19,40 +19,40 @@
OxiCloud
- +

Grant Access

A Nextcloud client is requesting access to your account.

- +
-
- +
-
- +
@@ -65,7 +65,7 @@ - +