commit 874dbbe8e8bfd4fc4ce88e69f8861ae4a0d4b789 Author: Power BI Dev Date: Sat May 2 10:07:56 2026 +0700 Add Docker and Coolify deployment setup Add the container configuration, environment templates, and storage settings needed to deploy SPOTA with Docker and Coolify. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cec3a05 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,27 @@ +.git +.gitignore +spota_db/openfileslimit +spota_db +*.zip +*.rar +*.7z +*.tar +*.tar.gz +*.tgz +error_log +**/error_log +**/.DS_Store +index_gacor.php +index_gacor.html +cgi-bin +bekap +direktori-spn +pkm +rekomendasi-artikel +simpanghilir +sisesta +pedoman-old +files +files/** +!files/.gitkeep +img/curiga diff --git a/.env.coolify.example b/.env.coolify.example new file mode 100644 index 0000000..9bf2e30 --- /dev/null +++ b/.env.coolify.example @@ -0,0 +1,14 @@ +APP_URL=https://spota.example.com + +DB_USER=spota_user +DB_PASSWORD=change_this_database_password +DB_NAME=spota_spotadb +DB_SPOTA=spota_spotadb +DB_KONSULTASI=spota_konsultasi +DB_BIO=spota_spotadb +DB_DOSEN=spota_spotadb +SERVICE_DB_NAME=spota_spotadb + +MYSQL_ROOT_PASSWORD=change_this_root_password +PHP_DISPLAY_ERRORS=0 +FILES_STORAGE_PATH=/var/www/html/files diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..38ad7cc --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +APP_PORT=8080 +APP_URL=http://localhost:8080 + +DB_HOST=db +DB_PORT=3307 +DB_USER=spota_user +DB_PASSWORD=spota_password +DB_NAME=spota_spotadb +DB_SPOTA=spota_spotadb +DB_KONSULTASI=spota_konsultasi +DB_BIO=spota_spotadb +DB_DOSEN=spota_spotadb +SERVICE_DB_NAME=spota_spotadb + +MYSQL_ROOT_PASSWORD=root_password +PHP_DISPLAY_ERRORS=1 +FILES_STORAGE_PATH=/var/www/html/files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01a4654 --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +# Environment and secrets +.env +.env.* +!.env.example +!.env.coolify.example + +# Dependencies / generated packages +/vendor/ +node_modules/ +npm-debug.log* +yarn-error.log* +composer.lock +package-lock.json + +# Runtime uploads and user-generated files +/files/ +/files/** +!/files/.gitkeep +/img/curiga/ + +# Logs and temporary files +error_log +**/error_log +*.log +*.tmp +*.temp +tmp/ +temp/ +cache/ + +# Backups, archives, and exported bundles +*.zip +*.rar +*.7z +*.tar +*.tar.gz +*.tgz +*.bak +*.backup +*.old +bekap/ +backup/ +backups/ + +# Known unrelated/backup project folders +/direktori-spn/ +/pkm/ +/simpanghilir/ +/sisesta/ +/rekomendasi-artikel/ +/pedoman-old/ + +# OS/editor files +.DS_Store +Thumbs.db +desktop.ini +.idea/ +.vscode/ + +# Docker local data should be volumes, not Git content +docker-data/ +.docker-data/ diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..572620c --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,74 @@ +# SPOTA Docker + +Setup ini menjalankan aplikasi legacy SPOTA dengan PHP 7.4 Apache dan MySQL 8 dalam satu `docker-compose.yml`. + +## Jalankan Lokal + +```powershell +copy .env.example .env +docker compose up -d --build +``` + +Akses aplikasi: + +```text +http://localhost:8080 +http://localhost:8080/admin/login.php +http://localhost:8080/dosen/login.php +http://localhost:8080/mahasiswa/login.php +``` + +## Stop + +```powershell +docker compose down +``` + +## Reset Database + +Perintah ini menghapus volume DB dan import ulang dump dari `spota_db`. + +```powershell +docker compose down -v +docker compose up -d +``` + +## Database + +Dump diambil dari folder `spota_db` saat volume DB pertama kali dibuat. + +Database yang di-import: + +- `spota_spotadb` +- `spota_konsultasi` +- `spota_doxid` +- `spota_rek_artikel` + +## Volume/Data + +Folder berikut di-mount dari host supaya data upload tidak masuk image: + +- `./files:/var/www/html/files` +- `./img:/var/www/html/img` + +Opsional, upload berkas bisa dipindah ke lokasi di luar folder project dengan environment variable: + +- `FILES_STORAGE_PATH=/absolute/path/to/files` + +Jika `FILES_STORAGE_PATH` tidak diisi, aplikasi tetap memakai default `files/` di dalam document root. +Folder target akan dibuat otomatis saat upload pertama. +Jika path tersebut berada di luar `/var/www/html/files`, sesuaikan juga volume mount container ke lokasi yang sama. + +Untuk Coolify, jadikan path ini persistent storage. + +## Coolify + +Sesuaikan environment berikut di Coolify: + +- `APP_URL` +- `DB_USER` +- `DB_PASSWORD` +- `MYSQL_ROOT_PASSWORD` +- `PHP_DISPLAY_ERRORS=0` untuk production + +Port internal app adalah `80`. Database internal memakai service name `db` pada port `3306`. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1782bb8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM php:7.4-apache + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libonig-dev \ + libpng-dev \ + libzip-dev \ + unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd zip mbstring exif \ + && a2enmod rewrite headers \ + && rm -rf /var/lib/apt/lists/* + +COPY docker/php.ini /usr/local/etc/php/conf.d/spota.ini +COPY docker/apache-vhost.conf /etc/apache2/sites-available/000-default.conf + +WORKDIR /var/www/html +COPY . /var/www/html + +RUN mkdir -p /var/www/html/files /var/www/html/img /tmp/spota_sessions \ + && chown -R www-data:www-data /var/www/html/files /var/www/html/img /tmp/spota_sessions diff --git a/Dockerfile.coolify b/Dockerfile.coolify new file mode 100644 index 0000000..8a64e38 --- /dev/null +++ b/Dockerfile.coolify @@ -0,0 +1,31 @@ +FROM php:8.2-apache-bookworm + +RUN rm -rf /var/lib/apt/lists/* \ + && apt-get clean \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + libcurl4-openssl-dev \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libonig-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev \ + unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd curl zip mbstring exif \ + && a2enmod rewrite headers \ + && printf 'ServerName localhost\n' > /etc/apache2/conf-available/server-name.conf \ + && a2enconf server-name \ + && rm -rf /var/lib/apt/lists/* + +COPY docker/php.ini /usr/local/etc/php/conf.d/spota.ini +COPY docker/apache-vhost.conf /etc/apache2/sites-available/000-default.conf + +WORKDIR /var/www/html +COPY . /var/www/html/ + +RUN mkdir -p /var/www/html/files /var/www/html/img /tmp/spota_sessions \ + && chown -R www-data:www-data /var/www/html /tmp/spota_sessions + +EXPOSE 80 diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml new file mode 100644 index 0000000..80582a0 --- /dev/null +++ b/docker-compose.coolify.yml @@ -0,0 +1,49 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile.coolify + restart: unless-stopped + environment: + APP_URL: ${APP_URL} + DB_HOST: db + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + DB_NAME: ${DB_NAME:-spota_spotadb} + DB_SPOTA: ${DB_SPOTA:-spota_spotadb} + DB_KONSULTASI: ${DB_KONSULTASI:-spota_konsultasi} + DB_BIO: ${DB_BIO:-spota_spotadb} + DB_DOSEN: ${DB_DOSEN:-spota_spotadb} + SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb} + PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-0} + FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files} + volumes: + - spota_files:/var/www/html/files + - spota_img:/var/www/html/img + depends_on: + db: + condition: service_healthy + + db: + image: mysql:8.0 + restart: unless-stopped + command: --default-authentication-plugin=mysql_native_password --character-set-server=latin1 --collation-server=latin1_swedish_ci --sql-mode=NO_ENGINE_SUBSTITUTION + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_DATABASE: ${DB_NAME:-spota_spotadb} + volumes: + - spota_db_data:/var/lib/mysql + - ./docker/mysql-init:/docker-entrypoint-initdb.d:ro + - ./spota_db:/spota_db:ro + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$${MYSQL_ROOT_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 20 + +volumes: + spota_db_data: + spota_files: + spota_img: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e3c5016 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +services: + app: + build: . + container_name: spota-app + restart: unless-stopped + ports: + - "${APP_PORT:-8080}:80" + environment: + APP_URL: ${APP_URL:-http://localhost:8080} + DB_HOST: ${DB_HOST:-db} + DB_USER: ${DB_USER:-spota_user} + DB_PASSWORD: ${DB_PASSWORD:-spota_password} + DB_NAME: ${DB_NAME:-spota_spotadb} + DB_SPOTA: ${DB_SPOTA:-spota_spotadb} + DB_KONSULTASI: ${DB_KONSULTASI:-spota_konsultasi} + DB_BIO: ${DB_BIO:-spota_spotadb} + DB_DOSEN: ${DB_DOSEN:-spota_spotadb} + SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb} + PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-1} + FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files} + volumes: + - ./files:/var/www/html/files + - ./img:/var/www/html/img + depends_on: + db: + condition: service_healthy + + db: + image: mysql:8.0 + container_name: spota-db + restart: unless-stopped + command: --default-authentication-plugin=mysql_native_password --character-set-server=latin1 --collation-server=latin1_swedish_ci + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password} + MYSQL_USER: ${DB_USER:-spota_user} + MYSQL_PASSWORD: ${DB_PASSWORD:-spota_password} + MYSQL_DATABASE: ${DB_NAME:-spota_spotadb} + ports: + - "${DB_PORT:-3307}:3306" + volumes: + - db_data:/var/lib/mysql + - ./docker/mysql-init:/docker-entrypoint-initdb.d:ro + - ./spota_db:/spota_db:ro + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$${MYSQL_ROOT_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 20 + +volumes: + db_data: diff --git a/docker/apache-vhost.conf b/docker/apache-vhost.conf new file mode 100644 index 0000000..868eed6 --- /dev/null +++ b/docker/apache-vhost.conf @@ -0,0 +1,13 @@ + + ServerName localhost + DocumentRoot /var/www/html + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/docker/mysql-init/00-import-spota-databases.sh b/docker/mysql-init/00-import-spota-databases.sh new file mode 100644 index 0000000..7f532e1 --- /dev/null +++ b/docker/mysql-init/00-import-spota-databases.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -e + +mysql_exec() { + mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$@" +} + +import_database() { + db_name="$1" + create_file="$2" + data_file="$3" + + if [ -f "$create_file" ]; then + mysql_exec < "$create_file" + else + mysql_exec -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;" + fi + + if [ -f "$data_file" ]; then + mysql_exec "$db_name" < "$data_file" + fi +} + +import_database "spota_spotadb" "/spota_db/spota_spotadb.create" "/spota_db/spota_spotadb.sql" +import_database "spota_konsultasi" "/spota_db/spota_konsultasi.create" "/spota_db/spota_konsultasi.sql" +import_database "spota_doxid" "/spota_db/spota_doxid.create" "/spota_db/spota_doxid.sql" +import_database "spota_rek_artikel" "/spota_db/spota_rek_artikel.create" "/spota_db/spota_rek_artikel.sql" + +mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_spotadb\`.* TO '$MYSQL_USER'@'%';" +mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_konsultasi\`.* TO '$MYSQL_USER'@'%';" +mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_doxid\`.* TO '$MYSQL_USER'@'%';" +mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_rek_artikel\`.* TO '$MYSQL_USER'@'%';" +mysql_exec -e "FLUSH PRIVILEGES;" diff --git a/docker/php.ini b/docker/php.ini new file mode 100644 index 0000000..511b20d --- /dev/null +++ b/docker/php.ini @@ -0,0 +1,13 @@ +display_errors = ${PHP_DISPLAY_ERRORS} +log_errors = On +error_reporting = E_ALL +max_execution_time = 120 +max_input_time = 120 +max_input_vars = 5000 +memory_limit = 512M +post_max_size = 32M +upload_max_filesize = 32M +session.gc_maxlifetime = 604800 +session.save_path = "/tmp/spota_sessions" +allow_url_fopen = On +date.timezone = Asia/Jakarta