From 1e8a7dd5bb9849eff9def701093a3af54280628a Mon Sep 17 00:00:00 2001 From: Dionisio Date: Wed, 11 Feb 2026 14:09:40 +0100 Subject: [PATCH] feat: redesign admin panel & profile page, optimize Dockerfile, remove rootless - Completely redesign admin.html matching OxiCloud design system - Create standalone profile.html page with avatar, details & password change - Optimize Dockerfile: 3-stage build, non-root user, OCI labels, layer cache - Add .dockerignore to reduce build context - Remove redundant Dockerfile.rootless & rootless-compose.yml - Redesign login language selector as compact dropdown with search - Fix CI/CD workflows (ci.yml, docker-build.yml, docker-publish.yml) - Update app.js to navigate to profile page instead of modal --- .dockerignore | 32 +++ .github/workflows/ci.yml | 10 +- .github/workflows/docker-build.yml | 4 +- .github/workflows/docker-publish.yml | 6 +- Cargo.toml | 4 +- Dockerfile | 45 ++- Dockerfile.rootless | 61 ---- rootless-compose.yml | 48 ---- static/admin.html | 398 +++++++++++++++----------- static/css/auth.css | 408 ++++++++++----------------- static/js/app.js | 4 +- static/js/auth.js | 279 +++++++----------- static/login.html | 50 ++-- static/profile.html | 367 ++++++++++++++++++++++++ 14 files changed, 957 insertions(+), 759 deletions(-) create mode 100644 .dockerignore delete mode 100644 Dockerfile.rootless delete mode 100644 rootless-compose.yml create mode 100644 static/profile.html diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3b855bb0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# Build artifacts +target/ + +# Git +.git/ +.gitignore + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# Docker +Dockerfile +Dockerfile.rootless +docker-compose.yml +rootless-compose.yml + +# Runtime data (mapped via volumes) +storage/ + +# Documentation +doc/ +*.md +LICENSE +CODEOWNERS + +# Miscellaneous +.github/ +.env +*.bak diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13434fba..a27b3980 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt @@ -26,7 +26,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -51,7 +51,7 @@ jobs: --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 @@ -69,7 +69,7 @@ jobs: name: Security Audit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: rustsec/audit-check@v2.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-latest needs: [fmt, clippy, test] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo build --release diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index a1bebffe..0b347043 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -30,7 +30,7 @@ jobs: type=sha - name: Build Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: false diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 5d900d77..205b565a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -18,7 +18,7 @@ jobs: name: Pre-publish Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo test --workspace @@ -30,7 +30,7 @@ jobs: needs: test steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set version tag id: version @@ -58,7 +58,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and Push Multi-Arch Image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/Cargo.toml b/Cargo.toml index 8c205cbd..3ab83a18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ tokio = { version = "1.49.0", features = ["full"] } tokio-util = { version = "0.7.18", features = ["io", "codec"] } tokio-stream = { version = "0.1.18", features = ["fs"] } bytes = "1.11.0" -tempfile = "3.24.0" +tempfile = "3.25.0" tower = "0.5.3" tower-http = { version = "0.6.8", features = ["fs", "compression-gzip", "trace", "cors", "add-extension", "request-id"] } flate2 = "1.1.9" @@ -32,7 +32,7 @@ sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-rustls anyhow = "1.0.101" jsonwebtoken = { version = "10.1.0", features = ["rust_crypto"] } argon2 = "0.5.3" -rand_core = { version = "0.6.4", features = ["std"] } +rand_core = { version = "0.9.5", features = ["std"] } hyper = { version = "1.8.1", features = ["full"] } quick-xml = "0.39.0" dotenv = "0.15.0" diff --git a/Dockerfile b/Dockerfile index 215d91e0..ad847116 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,39 +14,54 @@ FROM rust:1.93.0-alpine3.23 AS builder WORKDIR /app RUN apk --no-cache upgrade && \ apk add --no-cache musl-dev pkgconfig postgresql-dev gcc perl make -# Copy cached dependencies +# Copy cached dependencies (only target dir and cargo registry) COPY --from=cacher /app/target target -COPY --from=cacher /usr/local/cargo /usr/local/cargo -# Copy ALL files needed for compilation, including static files -COPY src src -COPY static static -COPY db db +COPY --from=cacher /usr/local/cargo/registry /usr/local/cargo/registry +# Copy only what Cargo needs to compile (static/db are runtime-only, copied in final stage) COPY Cargo.toml Cargo.lock ./ -# Build with all optimizations -ENV DATABASE_URL="postgres://postgres:postgres@postgres/oxicloud" -RUN cargo build --release +COPY src src +# Build with all optimizations (DATABASE_URL only needed at compile-time for sqlx) +ARG DATABASE_URL="postgres://postgres:postgres@localhost/oxicloud" +RUN DATABASE_URL="${DATABASE_URL}" cargo build --release # Stage 3: Create minimal final image FROM alpine:3.23.3 + +# OCI image metadata +LABEL org.opencontainers.image.title="OxiCloud" \ + org.opencontainers.image.description="Ultra-fast, secure & lightweight self-hosted cloud storage built in Rust" \ + org.opencontainers.image.url="https://github.com/DioCrafts/OxiCloud" \ + org.opencontainers.image.source="https://github.com/DioCrafts/OxiCloud" \ + org.opencontainers.image.vendor="DioCrafts" \ + org.opencontainers.image.licenses="MIT" + # Install only necessary runtime dependencies and update packages RUN apk --no-cache upgrade && \ apk add --no-cache libgcc ca-certificates libpq tzdata +# Create non-root user +RUN addgroup -g 1001 -S oxicloud && \ + adduser -u 1001 -S oxicloud -G oxicloud + # Copy only the compiled binary COPY --from=builder /app/target/release/oxicloud /usr/local/bin/ +RUN chmod +x /usr/local/bin/oxicloud # Copy static files and other resources needed at runtime -COPY static /app/static -COPY db /app/db +COPY --chown=oxicloud:oxicloud static /app/static +COPY --chown=oxicloud:oxicloud db /app/db # Create storage directory with proper permissions -RUN mkdir -p /app/storage && chmod 777 /app/storage - -# Set proper permissions -RUN chmod +x /usr/local/bin/oxicloud +RUN mkdir -p /app/storage && chown oxicloud:oxicloud /app/storage # Set working directory WORKDIR /app +# Expose application port +EXPOSE 8086 + +# Run as non-root user +USER oxicloud + # Run the application CMD ["oxicloud"] diff --git a/Dockerfile.rootless b/Dockerfile.rootless deleted file mode 100644 index fb407c53..00000000 --- a/Dockerfile.rootless +++ /dev/null @@ -1,61 +0,0 @@ -# Stage 1: Builder – compile the application -FROM rust:1.93.0-alpine3.23 AS builder - -# Install build dependencies -RUN apk add --no-cache musl-dev pkgconfig openssl-dev postgresql-dev - -# Create a non-root user with UID 10001 (we use the same UID across stages) -RUN adduser -D -u 10001 oxicloud - -WORKDIR /app - -# Copy dependency files first to leverage Docker cache for dependency compilation -COPY Cargo.toml Cargo.lock ./ - -# Prepare dummy source to build dependencies (improves caching) -RUN mkdir -p src && \ - echo "fn main() {}" > src/main.rs && \ - touch src/lib.rs && \ - cargo build --release && \ - rm -rf src - -# Copy the actual source code and additional files -COPY src ./src -COPY static ./static -COPY db ./db - -# Build the actual application and strip debug symbols for a smaller binary -RUN cargo build --release && \ - strip target/release/oxicloud - -# Stage 2: Runtime – only include what is necessary to run the app -FROM alpine:3.23 - -# Install runtime dependencies and clean up cache -RUN apk add --no-cache libgcc openssl ca-certificates tzdata && \ - rm -rf /var/cache/apk/* - -# Create a non-root user with the same UID (10001) for consistent file ownership -RUN adduser -D -u 10001 oxicloud - -# Create application directories, assign proper permissions -WORKDIR /app -RUN mkdir -p /app/static /app/storage /app/db && \ - chown -R oxicloud:oxicloud /app - -# Copy the built binary from the builder stage and additional runtime files -COPY --from=builder /app/target/release/oxicloud /app/oxicloud -COPY --from=builder /app/static /app/static -COPY --from=builder /app/db /app/db - -# Ensure all files are owned by the non-root user -RUN chown -R oxicloud:oxicloud /app - -# Set the non-root user for running the application -USER oxicloud - -# Expose the port the application listens on -EXPOSE 8086 8085 - -# Run the binary in release mode -CMD ["./oxicloud", "--release"] diff --git a/rootless-compose.yml b/rootless-compose.yml deleted file mode 100644 index d9647791..00000000 --- a/rootless-compose.yml +++ /dev/null @@ -1,48 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - restart: always - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: oxicloud -# ports: -# - "5432:5432" - networks: - - oxicloud - volumes: - - pg_data:/var/lib/postgresql/data - - ./db/schema.sql:/docker-entrypoint-initdb.d/10-schema.sql - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s - timeout: 5s - retries: 5 - - oxicloud: - image: oxicloud-rootless - restart: always - build: - context: . - dockerfile: Dockerfile.rootless - ports: - - "8086:8086" - - "8085:8085" - networks: - - oxicloud - depends_on: - - postgres - environment: - - "OXICLOUD_DB_CONNECTION_STRING=postgres://postgres:postgres@postgres/oxicloud" - # Ensure the container runs with the non-root user (UID:GID 10001:10001) - user: "10001:10001" - volumes: - - storage_data:/app/storage - -networks: - oxicloud: - driver: bridge - -volumes: - pg_data: - storage_data: diff --git a/static/admin.html b/static/admin.html index 68cc39e0..4d2f77e8 100644 --- a/static/admin.html +++ b/static/admin.html @@ -4,185 +4,259 @@ OxiCloud — Admin Panel + -
-
-

⚙️ Admin Panel

- ← Back to OxiCloud -
-
Loading…
-

Access Denied

Administrator privileges required.

Sign in
+ +
+
+ +
OxiCloud · Admin
+
+ +
+ +
+
Loading…
+
+
+

Access Denied

+

Administrator privileges required to access this panel.

+ Sign in +