diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1981900..c638705 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,6 +22,11 @@ updates: ignore: - dependency-name: "next" update-types: ["version-update:semver-major"] + # Prisma 7 has breaking changes incompatible with our schema + - dependency-name: "prisma" + update-types: ["version-update:semver-major"] + - dependency-name: "@prisma/client" + update-types: ["version-update:semver-major"] - package-ecosystem: "github-actions" directory: "/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a8c285..9e43a28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ # CI Workflow - Validates PRs and pushes, builds Docker images for version tags -# Builds multi-platform images (amd64 + arm64) and pushes to Docker Hub +# Builds amd64 images and pushes to Docker Hub name: CI @@ -99,7 +99,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index fb1e971..cf27355 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,8 @@ COPY prisma ./prisma/ RUN npm ci # Generate Prisma client (needs dummy DATABASE_URL for generation) +# SECURITY NOTE: This is ONLY for build time to generate client artifacts. +# It is NOT used in production and does NOT persist to the final image. ENV DATABASE_URL="postgresql://user:pass@localhost:5432/db" RUN npx prisma generate @@ -31,8 +33,12 @@ FROM node:20-alpine AS runner WORKDIR /app # Install runtime dependencies +# Added: prisma for migrations RUN apk add --no-cache libc6-compat openssl git curl docker-cli +# Install global prisma for the entrypoint script (pinned to match project version) +RUN npm install -g prisma@6.19.1 + # Create non-root user RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs @@ -44,11 +50,17 @@ COPY --from=builder /app/.next/static ./.next/static COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma -# Set ownership +# Copy entrypoint script +COPY scripts/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# Set ownership of app directory RUN chown -R nextjs:nodejs /app -# Switch to non-root user -USER nextjs +# Note: We run as root because: +# 1. Docker socket access requires root +# 2. Mounted volumes (/data/core, /data/projects) are created as root +# In a more secure setup, you could use rootless Docker or adjust volume permissions # Expose port EXPOSE 3000 @@ -63,5 +75,6 @@ ENV HOSTNAME="0.0.0.0" HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/api/health || exit 1 -# Start the application +# Start the application using entrypoint +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] CMD ["node", "server.js"] diff --git a/README.md b/README.md index 3a78783..41d7f42 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ SupaPanel is a web-based control panel that simplifies the deployment and manage - [Quick Start](#quick-start) - [Tech Stack](#tech-stack) - [Usage Guide](#usage-guide) +- [Uninstallation](#uninstallation) - [Project Structure](#project-structure) - [Configuration](#configuration) - [Contributing](#contributing) @@ -196,6 +197,28 @@ After configuration: --- +## Uninstallation + +To completely remove SupaPanel from your server, follow these steps: + +1. **Stop and remove containers**: + ```bash + cd /etc/supapanel + docker compose down -v + ``` + +2. **Remove data directory** (WARNING: This will delete all your projects and data): + ```bash + sudo rm -rf /etc/supapanel + ``` + +3. **Remove Docker image** (optional): + ```bash + docker rmi alanmf30/supapanel:latest + ``` + +--- + ## Project Structure ``` diff --git a/install.sh b/install.sh index 8e1683a..a8b5251 100644 --- a/install.sh +++ b/install.sh @@ -7,18 +7,18 @@ set -e SUPAPANEL_VERSION="${SUPAPANEL_VERSION:-latest}" SUPAPANEL_DATA_DIR="/etc/supapanel" -echo "╔═══════════════════════════════════════════════════════════════╗" -echo "║ ║" -echo "║ ███████╗██╗ ██╗██████╗ █████╗ ██████╗███╗ ██╗███████╗ ║" -echo "║ ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝ ║" -echo "║ ███████╗██║ ██║██████╔╝███████║██║ ██╔██╗ ██║███████╗ ║" -echo "║ ╚════██║██║ ██║██╔═══╝ ██╔══██║██║ ██║╚██╗██║╚════██║ ║" -echo "║ ███████║╚██████╔╝██║ ██║ ██║╚██████╗██║ ╚████║███████║ ║" -echo "║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═══╝╚══════╝ ║" -echo "║ ║" -echo "║ Self-Hosted Supabase Management Panel ║" -echo "║ ║" -echo "╚═══════════════════════════════════════════════════════════════╝" +echo "╔══════════════════════════════════════════════════════════════════════════════╗" +echo "║ ║" +echo "║ ███████╗██╗ ██╗██████╗ █████╗ ██████╗ █████╗ ███╗ ██╗███████╗██╗ ║" +echo "║ ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗████╗ ██║██╔════╝██║ ║" +echo "║ ███████╗██║ ██║██████╔╝███████║██████╔╝███████║██╔██╗ ██║█████╗ ██║ ║" +echo "║ ╚════██║██║ ██║██╔═══╝ ██╔══██║██╔═══╝ ██╔══██║██║╚██╗██║██╔══╝ ██║ ║" +echo "║ ███████║╚██████╔╝██║ ██║ ██║██║ ██║ ██║██║ ╚████║███████╗███████╗║" +echo "║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝║" +echo "║ ║" +echo "║ Self-Hosted Supabase Management Panel ║" +echo "║ ║" +echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" # Check if running as root diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100755 index 0000000..4ddb3f4 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -e + +# Wait for the database to be ready +# (Optional: we can rely on Docker Compose healthcheck, but a quick check here is good) + +echo "Starting SupaPanel..." + +if [ -z "$DATABASE_URL" ]; then + echo "Error: DATABASE_URL is not set." + exit 1 +fi + +# Run database migrations +echo "Running database migrations..." +# We use db push for now to ensure schema is in sync. +# --skip-generate is needed because the client was already generated during build, +# and the nextjs user doesn't have write permissions to regenerate it. +npx prisma db push --accept-data-loss --skip-generate + +# Execute the main command +echo "Starting application..." +exec "$@" diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index c0c6333..a83874c 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -46,11 +46,17 @@ export async function POST(request: NextRequest) { }, }) + // Only set secure if actually using HTTPS (not just based on NODE_ENV) + // This allows HTTP access via IP:3000 while still being secure over HTTPS + const isSecure = request.headers.get('x-forwarded-proto') === 'https' || + request.url.startsWith('https://') + response.cookies.set('session', token, { httpOnly: true, - secure: process.env.NODE_ENV === 'production', + secure: isSecure, sameSite: 'lax', maxAge: 24 * 60 * 60, // 24 hours + path: '/', }) return response diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index e468290..d628ec7 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -58,11 +58,17 @@ export async function POST(request: NextRequest) { }, }) + // Only set secure if actually using HTTPS (not just based on NODE_ENV) + // This allows HTTP access via IP:3000 while still being secure over HTTPS + const isSecure = request.headers.get('x-forwarded-proto') === 'https' || + request.url.startsWith('https://') + response.cookies.set('session', token, { httpOnly: true, - secure: process.env.NODE_ENV === 'production', + secure: isSecure, sameSite: 'lax', maxAge: 24 * 60 * 60, // 24 hours + path: '/', }) return response