diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e7c1d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..2c1c20d --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,125 @@ +# Development Dockerfile with FrankenPHP +FROM dunglas/frankenphp:latest-php8.3-alpine + +# Install system dependencies +RUN apk add --no-cache \ + git \ + postgresql-client \ + vim \ + bash \ + nano \ + curl + +# Install Node.js 20.19.0+ from unofficial builds (for Vite 7 compatibility) +RUN curl -fsSL https://unofficial-builds.nodejs.org/download/release/v20.19.0/node-v20.19.0-linux-x64-musl.tar.xz | tar -xJ -C /usr/local --strip-components=1 + +# Install PHP extensions including xdebug for development +RUN install-php-extensions \ + pdo_pgsql \ + opcache \ + zip \ + gd \ + intl \ + redis \ + xdebug + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Configure PHP for development +RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" + +# Configure Xdebug (disabled by default to reduce noise) +RUN echo "xdebug.mode=off" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +# Configure Caddy for development +RUN cat > /etc/caddy/Caddyfile < /start.sh <<'EOF' +#!/bin/sh +set -e + +# Create .env file if it doesn't exist +if [ ! -f ".env" ]; then + echo "Creating .env file from .env.example..." + cp .env.example .env +fi + +# Install composer dependencies if vendor is empty +if [ ! -f "vendor/autoload.php" ]; then + echo "Installing Composer dependencies..." + composer install --no-interaction +fi + +# Install npm dependencies if node_modules is empty +if [ ! -d "node_modules" ] && [ -f "package.json" ]; then + echo "Installing npm dependencies..." + npm install +fi + +# Clear Laravel caches +php artisan config:clear || true +php artisan cache:clear || true + +# Wait for database +echo "Waiting for database..." +until pg_isready -h db -U trove -q; do + echo "Database not ready, retrying..." + sleep 2 +done +echo "Database is ready!" + +# Generate app key if not set +if grep -q "^APP_KEY=$" .env 2>/dev/null; then + echo "Generating application key..." + php artisan key:generate +fi + +# Run migrations +php artisan migrate --force + +# Start Vite dev server in background (if package.json exists) +if [ -f "package.json" ]; then + npm run dev & +fi + +# Start FrankenPHP +exec frankenphp run --config /etc/caddy/Caddyfile +EOF + +RUN chmod +x /start.sh + +# Expose ports +EXPOSE 8000 5173 + +# Use the startup script +CMD ["/start.sh"] diff --git a/README.md b/README.md index 95dca18..d7db23f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # trove +A small web search engine. diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml new file mode 100644 index 0000000..927213b --- /dev/null +++ b/docker/dev/docker-compose.yml @@ -0,0 +1,96 @@ +# =================== +# Trove Development Services +# =================== +# Port allocation: +# App: 8200 (frankenphp), 5175 (vite) +# DB: 5433 (postgresql) +# Redis: 6380 + +services: + app: + build: + context: ../.. + dockerfile: Dockerfile.dev + container_name: trove_dev_app + restart: unless-stopped + ports: + - "8200:8000" + - "5175:5173" + volumes: + - ../..:/app + - app_vendor:/app/vendor + - app_node_modules:/app/node_modules + environment: + APP_NAME: "${APP_NAME:-Trove}" + APP_ENV: "${APP_ENV:-local}" + APP_DEBUG: "${APP_DEBUG:-true}" + APP_URL: "${APP_URL:-http://localhost:8200}" + DB_CONNECTION: pgsql + DB_HOST: db + DB_PORT: 5432 + DB_DATABASE: "${DB_DATABASE:-trove}" + DB_USERNAME: "${DB_USERNAME:-trove}" + DB_PASSWORD: "${DB_PASSWORD:-trove}" + REDIS_HOST: redis + REDIS_PORT: 6379 + SESSION_DRIVER: "${SESSION_DRIVER:-database}" + CACHE_STORE: "${CACHE_STORE:-redis}" + QUEUE_CONNECTION: "${QUEUE_CONNECTION:-redis}" + MAIL_MAILER: "${MAIL_MAILER:-log}" + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + networks: + - trove-network + + db: + image: postgres:17-alpine + container_name: trove_dev_db + hostname: db + restart: unless-stopped + ports: + - "5433:5432" + environment: + POSTGRES_DB: "${DB_DATABASE:-trove}" + POSTGRES_USER: "${DB_USERNAME:-trove}" + POSTGRES_PASSWORD: "${DB_PASSWORD:-trove}" + volumes: + - db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-trove}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + networks: + - trove-network + + redis: + image: redis:7-alpine + container_name: trove_dev_redis + hostname: redis + restart: unless-stopped + ports: + - "6380:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + networks: + - trove-network + +networks: + trove-network: + driver: bridge + +volumes: + db_data: + redis_data: + app_vendor: + app_node_modules: diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..b07745c --- /dev/null +++ b/shell.nix @@ -0,0 +1,153 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + # PHP and tools + php83 + php83Packages.composer + + # Node.js (needed for Tailwind CSS / Vite) + nodejs_22 + + # Container tools + podman + podman-compose + + # Database client + postgresql + + # Utilities + git + curl + gnumake + ]; + + shellHook = '' + export USER_ID=$(id -u) + export GROUP_ID=$(id -g) + export PODMAN_USERNS=keep-id + + # Compose file location + COMPOSE_FILE="$PWD/docker/dev/docker-compose.yml" + + # =================== + # ALIASES + # =================== + alias pc='podman-compose -f $COMPOSE_FILE' + + # =================== + # DEV COMMANDS + # =================== + dev-up() { + echo "Starting services..." + PODMAN_USERNS=keep-id podman-compose -f $COMPOSE_FILE up -d "$@" + echo "" + podman-compose -f $COMPOSE_FILE ps + echo "" + echo "App available at: http://localhost:8200" + } + + dev-down() { + if [[ "$1" == "-v" ]]; then + echo "Stopping services and removing volumes..." + podman-compose -f $COMPOSE_FILE down -v + else + echo "Stopping services..." + podman-compose -f $COMPOSE_FILE down + fi + } + + dev-restart() { + echo "Restarting services..." + podman-compose -f $COMPOSE_FILE restart "$@" + } + + dev-rebuild() { + echo "Rebuilding services (down -v + up)..." + podman-compose -f $COMPOSE_FILE down -v + PODMAN_USERNS=keep-id podman-compose -f $COMPOSE_FILE up -d "$@" + echo "" + podman-compose -f $COMPOSE_FILE ps + echo "" + echo "App available at: http://localhost:8200" + } + + dev-logs() { + podman-compose -f $COMPOSE_FILE logs -f app "$@" + } + + dev-logs-db() { + podman-compose -f $COMPOSE_FILE logs -f db "$@" + } + + dev-logs-redis() { + podman-compose -f $COMPOSE_FILE logs -f redis "$@" + } + + dev-shell() { + podman-compose -f $COMPOSE_FILE exec app bash + } + + dev-artisan() { + podman-compose -f $COMPOSE_FILE exec app php artisan "$@" + } + + # =================== + # BUILD COMMANDS + # =================== + base-build() { + local image="forge.lvl0.xyz/lvl0/trove:latest" + + # Check if logged in, prompt if not + if ! podman login --get-login forge.lvl0.xyz &>/dev/null; then + echo "Not logged in to forge.lvl0.xyz" + podman login forge.lvl0.xyz || return 1 + fi + + echo "Building image: $image" + if ! podman build -t "$image" -f Dockerfile .; then + echo "Build failed!" + return 1 + fi + + echo "" + echo "Pushing to registry..." + if ! podman push "$image"; then + echo "Push failed!" + return 1 + fi + + echo "" + echo "Done! Image pushed: $image" + } + + # =================== + # WELCOME MESSAGE + # =================== + echo "" + echo "=================================================" + echo " Trove Dev Environment " + echo "=================================================" + echo "" + echo " Podman: $(podman --version | cut -d' ' -f3)" + echo "" + echo "Commands:" + echo " dev-up [services] Start all or specific services" + echo " dev-down [-v] Stop services (-v removes volumes)" + echo " dev-rebuild Fresh start (down -v + up)" + echo " dev-restart Restart services" + echo " dev-logs Tail app logs" + echo " dev-logs-db Tail database logs" + echo " dev-logs-redis Tail Redis logs" + echo " dev-shell Shell into app container" + echo " dev-artisan Run artisan command" + echo " base-build Build and push image" + echo "" + echo "Services:" + echo " app Laravel http://localhost:8200" + echo " vite HMR http://localhost:5175" + echo " db PostgreSQL localhost:5433" + echo " redis Redis localhost:6380" + echo "" + ''; +}