2 Commits

Author SHA1 Message Date
Connor Johnstone
74d636117d Added the volume mounts 2025-08-31 17:55:06 -04:00
Connor Johnstone
ed458e6c3a Implement complete Docker Compose CI/CD setup with optimized builds
- Add multi-stage Dockerfile with dependency caching for both frontend and backend
- Implement Docker Compose configuration with separate frontend/backend services
- Configure Caddy as reverse proxy with proper WASM and static file serving
- Add volume mounting for frontend assets shared between containers
- Optimize build process with staged compilation and workspace handling
- Add debug logging and WASM initialization tracking for production deployment
- Update README with project motivation and "vibe coded" disclaimer

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 17:54:44 -04:00
8 changed files with 146 additions and 56 deletions

View File

@@ -1,6 +1,9 @@
# Build artifacts # Build artifacts
target/ target/
dist/ frontend/dist/
backend/target/
# Allow backend binary for multi-stage builds
!backend/target/release/backend
# Git # Git
.git/ .git/
@@ -21,8 +24,18 @@ Thumbs.db
# Documentation # Documentation
README.md README.md
*.md
# Docker # Development files
Dockerfile CLAUDE.md
.dockerignore *.txt
test_*.js
# Database files
*.db
calendar.db
# Test files
**/tests/
# Migrations (not needed for builds)
migrations/

4
.gitignore vendored
View File

@@ -19,4 +19,6 @@ dist/
.env.*.local .env.*.local
# Development notes (keep local) # Development notes (keep local)
CLAUDE.md CLAUDE.md
data/

10
Caddyfile Normal file
View File

@@ -0,0 +1,10 @@
{
default_sni rcjohnstone.com
key_type rsa4096
email c@rcjohnstone.com
}
:80, :443 {
root * /srv/www
file_server
}

View File

@@ -1,67 +1,96 @@
# Build stage # Build stage
# --------------------------------------- # -----------------------------------------------------------
FROM rust:alpine AS builder FROM rust:alpine AS builder
# Install build dependencies RUN apk add --no-cache musl-dev pkgconfig openssl-dev openssl-libs-static nodejs npm
RUN apk add --no-cache \
musl-dev \
pkgconfig \
openssl-dev \
nodejs \
npm
# Install trunk for building Yew apps # Install trunk ahead of the compilation. This may break and then you'll have to update the version.
RUN cargo install trunk wasm-pack RUN cargo install trunk@0.21.14 wasm-pack@0.13.1 wasm-bindgen-cli@0.2.100
# Add wasm32 target
RUN rustup target add wasm32-unknown-unknown RUN rustup target add wasm32-unknown-unknown
# Set working directory
WORKDIR /app WORKDIR /app
# Copy dependency files # Copy workspace files to maintain workspace structure
COPY Cargo.toml ./ COPY Cargo.toml Cargo.lock ./
COPY src ./src COPY calendar-models ./calendar-models
COPY frontend/Cargo.toml ./frontend/
COPY frontend/Trunk.toml ./frontend/
COPY frontend/index.html ./frontend/
COPY frontend/styles.css ./frontend/
# Copy web assets # Create empty backend directory to satisfy workspace
COPY index.html ./ RUN mkdir -p backend/src && \
COPY Trunk.toml ./ printf '[package]\nname = "calendar-backend"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\n' > backend/Cargo.toml && \
echo 'fn main() {}' > backend/src/main.rs
# Create dummy source files to build dependencies first
RUN mkdir -p frontend/src && \
echo "use web_sys::*; fn main() {}" > frontend/src/main.rs && \
echo "pub fn add(a: usize, b: usize) -> usize { a + b }" > calendar-models/src/lib.rs
# Build dependencies (this layer will be cached unless dependencies change)
RUN cargo build --release --target wasm32-unknown-unknown --bin calendar-app
# Copy actual source code and build the frontend application
RUN rm -rf frontend
COPY frontend ./frontend
RUN trunk build --release --config ./frontend/Trunk.toml
# Backend build stage
# -----------------------------------------------------------
FROM rust:alpine AS backend-builder
# Install build dependencies for backend
WORKDIR /app
RUN apk add --no-cache musl-dev pkgconfig openssl-dev openssl-libs-static
# Copy shared models
COPY calendar-models ./calendar-models
# Create empty frontend directory to satisfy workspace
RUN mkdir -p frontend/src && \
printf '[package]\nname = "calendar-app"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\n' > frontend/Cargo.toml && \
echo 'fn main() {}' > frontend/src/main.rs
# Create dummy backend source to build dependencies first
RUN mkdir -p backend/src && \
echo "fn main() {}" > backend/src/main.rs
# Build dependencies (this layer will be cached unless dependencies change)
COPY Cargo.toml Cargo.lock ./
COPY backend/Cargo.toml ./backend/
RUN cargo build --release
# Build the backend
COPY backend ./backend
RUN cargo build --release --bin backend
# Build the application
RUN trunk build --release
# Runtime stage # Runtime stage
# --------------------------------------- # -----------------------------------------------------------
FROM docker.io/nginx:alpine FROM alpine:latest
# Remove default nginx content # Install runtime dependencies
RUN rm -rf /usr/share/nginx/html/* RUN apk add --no-cache ca-certificates tzdata
# Copy built application from builder stage # Copy frontend files to temporary location
COPY --from=builder /app/dist/* /usr/share/nginx/html/ COPY --from=builder /app/frontend/dist /app/frontend-dist
# Add nginx configuration for SPA # Copy backend binary (built in workspace root)
RUN echo 'server { \ COPY --from=backend-builder /app/target/release/backend /usr/local/bin/backend
listen 80; \
server_name localhost; \
root /usr/share/nginx/html; \
index index.html; \
location / { \
try_files $uri $uri/ /index.html; \
} \
# Enable gzip compression \
gzip on; \
gzip_types text/css application/javascript application/wasm; \
}' > /etc/nginx/conf.d/default.conf
# Expose port # Create startup script to copy frontend files to shared volume
EXPOSE 80 RUN mkdir -p /srv/www
RUN echo '#!/bin/sh' > /usr/local/bin/start.sh && \
echo 'cp -r /app/frontend-dist/* /srv/www/' >> /usr/local/bin/start.sh && \
echo 'echo "Starting backend server..."' >> /usr/local/bin/start.sh && \
echo '/usr/local/bin/backend' >> /usr/local/bin/start.sh && \
chmod +x /usr/local/bin/start.sh
# Health check # Start with script that copies frontend files then starts backend
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD ["/usr/local/bin/start.sh"]
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,5 +1,8 @@
# Modern CalDAV Web Client # Modern CalDAV Web Client
>[!WARNING]
>This project was entirely vibe coded. It's my first attempt vibe coding anything, but I just sat down one day and realized this was something I've wanted to build for many years, but would just take way too long. With AI, I've been able to lay out the majority of the app in one long weekend. So proceed at your own risk, but I actually think the app is pretty solid.
A full-stack calendar application built with Rust, featuring a modern web interface for CalDAV calendar management. A full-stack calendar application built with Rust, featuring a modern web interface for CalDAV calendar management.
## Motivation ## Motivation
@@ -122,4 +125,4 @@ This client is designed to work with any RFC-compliant CalDAV server:
- **Apple Calendar Server** - 🚧 Planned standards-compliant operation - **Apple Calendar Server** - 🚧 Planned standards-compliant operation
- **Google Calendar** - 🚧 Planned CalDAV API compatibility - **Google Calendar** - 🚧 Planned CalDAV API compatibility
*Note: While the client follows RFC standards and should work with any compliant CalDAV server, we have currently only tested extensively with Baikal. Testing with other servers is planned.* *Note: While the client follows RFC standards and should work with any compliant CalDAV server, we have currently only tested extensively with Baikal. Testing with other servers is planned.*

22
compose.yml Normal file
View File

@@ -0,0 +1,22 @@
services:
calendar-backend:
build: .
env_file:
- .env
ports:
- "3000:3000"
volumes:
- ./data/site_dist:/srv/www
calendar-frontend:
image: caddy
env_file:
- .env
ports:
- "80:80"
- "443:443"
volumes:
- ./data/site_dist:/srv/www:ro
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./data/caddy/data:/data
- ./data/caddy/config:/config

View File

@@ -4,7 +4,15 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Calendar App</title> <title>Calendar App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<base data-trunk-public-url />
<link data-trunk rel="css" href="styles.css"> <link data-trunk rel="css" href="styles.css">
</head> </head>
<body></body> <body>
<script>
console.log("HTML loaded, waiting for WASM...");
window.addEventListener('TrunkApplicationStarted', () => {
console.log("Trunk application started successfully!");
});
</script>
</body>
</html> </html>

View File

@@ -575,6 +575,9 @@ pub fn App() -> Html {
}) })
}; };
// Debug logging
web_sys::console::log_1(&format!("App rendering: auth_token = {:?}", auth_token.is_some()).into());
html! { html! {
<BrowserRouter> <BrowserRouter>
<div class="app" onclick={on_outside_click}> <div class="app" onclick={on_outside_click}>