Skip to content
This repository was archived by the owner on Sep 1, 2025. It is now read-only.

Commit 4f0c31b

Browse files
committed
[Release]: V1.0.2 + Docker Fix
1 parent 1405d86 commit 4f0c31b

File tree

14 files changed

+529
-538
lines changed

14 files changed

+529
-538
lines changed

.env.example

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ DB_USER=accounting_user
3131
DB_PASS=CHANGE_THIS_DATABASE_PASSWORD
3232
DB_PORT=3306
3333

34-
# Database root password (for administration)
35-
DB_ROOT_PASSWORD=CHANGE_THIS_ROOT_PASSWORD
36-
3734
# ==============================================================================
3835
# Security Configuration
3936
# ==============================================================================
@@ -75,18 +72,14 @@ LOG_LEVEL=warning
7572
# Maximum number of log files to keep
7673
LOG_MAX_FILES=10
7774

78-
# ==============================================================================
79-
# Admin Configuration
80-
# ==============================================================================
81-
82-
# Default admin user credentials
83-
ADMIN_EMAIL=admin@your-domain.com
84-
ADMIN_PASSWORD=CHANGE_THIS_ADMIN_PASSWORD
8575

8676
# ==============================================================================
8777
# Docker Configuration
8878
# ==============================================================================
8979

80+
# Default admin user credentials (Docker version)
81+
ADMIN_EMAIL=admin@your-domain.com
82+
ADMIN_PASSWORD=CHANGE_THIS_ADMIN_PASSWORD
9083
# Docker Compose project name
9184
COMPOSE_PROJECT_NAME=accounting_panel
9285

@@ -95,10 +88,11 @@ HTTP_PORT=80
9588
HTTPS_PORT=443
9689
PHPMYADMIN_PORT=8080
9790
DB_PORT_EXPOSE=3306
98-
91+
# Database root password (for administration in docker)
92+
DB_ROOT_PASSWORD=CHANGE_THIS_ROOT_PASSWORD
9993

10094
# ==============================================================================
101-
# Development Configuration
95+
# Docker Development Configuration
10296
# ==============================================================================
10397

10498
# Development-specific settings (only for APP_ENV=development)

Dockerfile

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
FROM php:8.2-fpm
1+
FROM php:8.2-fpm-alpine
22

33
# Install system dependencies
4-
RUN apt-get update && apt-get install -y \
4+
RUN apk add --no-cache \
55
git \
66
curl \
7-
libpng-dev \
8-
libonig-dev \
9-
libxml2-dev \
10-
libzip-dev \
11-
libfreetype6-dev \
12-
libjpeg62-turbo-dev \
7+
wget \
138
libpng-dev \
149
libwebp-dev \
15-
libxpm-dev \
10+
libjpeg-turbo-dev \
11+
freetype-dev \
12+
libzip-dev \
1613
zip \
1714
unzip \
1815
mariadb-client \
19-
cron \
20-
supervisor \
21-
&& rm -rf /var/lib/apt/lists/*
16+
bash \
17+
icu-dev \
18+
oniguruma-dev \
19+
libxml2-dev \
20+
linux-headers \
21+
&& rm -rf /var/cache/apk/*
2222

2323
# Configure and install PHP extensions
24-
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-xpm \
24+
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
2525
&& docker-php-ext-install -j$(nproc) \
2626
pdo_mysql \
2727
mbstring \
@@ -32,15 +32,20 @@ RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-x
3232
bcmath \
3333
exif \
3434
intl \
35-
pcntl \
36-
sockets
35+
pcntl
3736

3837
# Install Composer
3938
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
4039

4140
# Set working directory
4241
WORKDIR /var/www/html
4342

43+
# Create necessary directories
44+
RUN mkdir -p /var/www/html/logs \
45+
&& mkdir -p /var/www/html/sessions \
46+
&& mkdir -p /var/www/html/public/uploads \
47+
&& mkdir -p /var/www/html/vendor
48+
4449
# Copy composer files first for better Docker layer caching
4550
COPY composer.json composer.lock ./
4651

@@ -53,25 +58,66 @@ COPY . .
5358
# Copy PHP configuration
5459
COPY docker/php/php.ini /usr/local/etc/php/conf.d/99-custom.ini
5560

56-
# Set proper permissions
61+
# Create PHP-FPM configuration
62+
RUN echo "[www]" > /usr/local/etc/php-fpm.d/www.conf \
63+
&& echo "user = www-data" >> /usr/local/etc/php-fpm.d/www.conf \
64+
&& echo "group = www-data" >> /usr/local/etc/php-fpm.d/www.conf \
65+
&& echo "listen = 0.0.0.0:9000" >> /usr/local/etc/php-fpm.d/www.conf \
66+
&& echo "listen.owner = www-data" >> /usr/local/etc/php-fpm.d/www.conf \
67+
&& echo "listen.group = www-data" >> /usr/local/etc/php-fpm.d/www.conf \
68+
&& echo "pm = dynamic" >> /usr/local/etc/php-fpm.d/www.conf \
69+
&& echo "pm.max_children = 20" >> /usr/local/etc/php-fpm.d/www.conf \
70+
&& echo "pm.start_servers = 2" >> /usr/local/etc/php-fpm.d/www.conf \
71+
&& echo "pm.min_spare_servers = 1" >> /usr/local/etc/php-fpm.d/www.conf \
72+
&& echo "pm.max_spare_servers = 3" >> /usr/local/etc/php-fpm.d/www.conf \
73+
&& echo "pm.process_idle_timeout = 10s" >> /usr/local/etc/php-fpm.d/www.conf \
74+
&& echo "pm.max_requests = 500" >> /usr/local/etc/php-fpm.d/www.conf \
75+
&& echo "catch_workers_output = yes" >> /usr/local/etc/php-fpm.d/www.conf \
76+
&& echo "decorate_workers_output = no" >> /usr/local/etc/php-fpm.d/www.conf
77+
78+
# Set proper permissions for www-data
5779
RUN chown -R www-data:www-data /var/www/html \
5880
&& chmod -R 755 /var/www/html \
59-
&& mkdir -p /var/www/html/logs \
60-
&& mkdir -p /var/www/html/sessions \
61-
&& mkdir -p /var/www/html/public/uploads \
6281
&& chmod -R 777 /var/www/html/logs \
6382
&& chmod -R 777 /var/www/html/sessions \
64-
&& chmod -R 777 /var/www/html/public/uploads
83+
&& chmod -R 777 /var/www/html/public/uploads \
84+
&& chown -R www-data:www-data /var/www/html/logs \
85+
&& chown -R www-data:www-data /var/www/html/sessions \
86+
&& chown -R www-data:www-data /var/www/html/public/uploads \
87+
&& chmod +x /var/www/html/control
6588

6689
# Run composer scripts after copying all files
67-
RUN composer dump-autoload --optimize
90+
RUN composer dump-autoload --optimize --no-dev
91+
92+
# Create startup script
93+
RUN echo '#!/bin/bash' > /usr/local/bin/start-app.sh \
94+
&& echo 'set -e' >> /usr/local/bin/start-app.sh \
95+
&& echo '' >> /usr/local/bin/start-app.sh \
96+
&& echo '# Wait for database' >> /usr/local/bin/start-app.sh \
97+
&& echo 'echo "Waiting for database connection..."' >> /usr/local/bin/start-app.sh \
98+
&& echo 'until mariadb -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" --skip-ssl -e "SELECT 1;" >/dev/null 2>&1; do' >> /usr/local/bin/start-app.sh \
99+
&& echo ' echo "Database not ready, waiting 2 seconds..."' >> /usr/local/bin/start-app.sh \
100+
&& echo ' sleep 2' >> /usr/local/bin/start-app.sh \
101+
&& echo 'done' >> /usr/local/bin/start-app.sh \
102+
&& echo 'echo "Database connected successfully!"' >> /usr/local/bin/start-app.sh \
103+
&& echo '' >> /usr/local/bin/start-app.sh \
104+
&& echo '# Run Docker-specific migrations if control script exists' >> /usr/local/bin/start-app.sh \
105+
&& echo 'if [ -f "/var/www/html/control" ]; then' >> /usr/local/bin/start-app.sh \
106+
&& echo ' echo "Running Docker migrations..."' >> /usr/local/bin/start-app.sh \
107+
&& echo ' cd /var/www/html && php control migrate docker 2>/dev/null || echo "Migrations completed or not needed"' >> /usr/local/bin/start-app.sh \
108+
&& echo 'fi' >> /usr/local/bin/start-app.sh \
109+
&& echo '' >> /usr/local/bin/start-app.sh \
110+
&& echo '# Start PHP-FPM' >> /usr/local/bin/start-app.sh \
111+
&& echo 'echo "Starting PHP-FPM..."' >> /usr/local/bin/start-app.sh \
112+
&& echo 'exec php-fpm' >> /usr/local/bin/start-app.sh \
113+
&& chmod +x /usr/local/bin/start-app.sh
68114

69115
# Expose port 9000
70116
EXPOSE 9000
71117

72118
# Health check
73119
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
74-
CMD pidof php-fpm || exit 1
120+
CMD pidof php-fpm8.2 || exit 1
75121

76-
# Start PHP-FPM
77-
CMD ["php-fpm"]
122+
# Start the application
123+
CMD ["/usr/local/bin/start-app.sh"]

app/Services/ApiMiddleware.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ public static function cors($request) {
122122
public static function securityHeaders($request) {
123123
header('Content-Type: application/json; charset=utf-8');
124124
header('X-Content-Type-Options: nosniff');
125-
header('X-Frame-Options: DENY');
126125
header('X-XSS-Protection: 1; mode=block');
127126
header('Referrer-Policy: no-referrer');
128127
header('Content-Security-Policy: frame-ancestors \'none\'');

app/Services/Middleware.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ public static function init()
119119
// Security Headers Middleware
120120
self::register('security_headers', function($request) {
121121
header('X-Content-Type-Options: nosniff');
122-
header('X-Frame-Options: SAMEORIGIN');
123122
header('X-XSS-Protection: 1; mode=block');
124123
header('Referrer-Policy: strict-origin-when-cross-origin');
125124

app/Views/layouts/header.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
<?php
33
// Add basic security headers
44
header("X-Content-Type-Options: nosniff");
5-
header("X-Frame-Options: DENY");
65
header("X-XSS-Protection: 1; mode=block");
76
header("Referrer-Policy: strict-origin-when-cross-origin");
87
// Basic CSP - can be enhanced as needed

control

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,12 @@ class Control
111111
case 'reset':
112112
$this->resetMigrations();
113113
break;
114+
case 'docker':
115+
$this->runDockerMigrations();
116+
break;
114117
default:
115118
$this->error("Unknown migrate command: {$subCommand}");
116-
$this->info("Available migrate commands: run, fresh, rollback, status, reset");
119+
$this->info("Available migrate commands: run, fresh, rollback, status, reset, docker");
117120
}
118121
}
119122

@@ -398,6 +401,72 @@ class Control
398401
}
399402
}
400403

404+
private function runDockerMigrations()
405+
{
406+
$this->info("Running database migrations for Docker environment...");
407+
408+
$migrationFiles = $this->getMigrationFiles();
409+
$executedMigrations = $this->getExecutedMigrations();
410+
411+
if (empty($migrationFiles)) {
412+
$this->warning("No migration files found.");
413+
return;
414+
}
415+
416+
$batch = $this->getNextBatchNumber();
417+
$executed = 0;
418+
419+
foreach ($migrationFiles as $file) {
420+
$migrationName = basename($file, '.php');
421+
422+
if (in_array($migrationName, $executedMigrations)) {
423+
continue; // Skip already executed migrations
424+
}
425+
426+
try {
427+
require_once $file;
428+
429+
// Get class name from file name
430+
$className = $this->getClassNameFromFile($migrationName);
431+
432+
if (!class_exists($className)) {
433+
$this->error("Migration class {$className} not found in {$file}");
434+
continue;
435+
}
436+
437+
$migration = new $className($this->database);
438+
439+
$this->info("Migrating: {$migrationName}");
440+
$migration->up();
441+
442+
// Record migration
443+
$this->database->insert('migrations', [
444+
'migration' => $migrationName,
445+
'batch' => $batch
446+
]);
447+
448+
$this->success("Migrated: {$migrationName}");
449+
$executed++;
450+
451+
} catch (Exception $e) {
452+
$this->error("Migration failed for {$migrationName}: " . $e->getMessage());
453+
break;
454+
}
455+
}
456+
457+
if ($executed > 0) {
458+
$this->success("Executed {$executed} migrations successfully!");
459+
460+
// Create admin user from environment variables for Docker
461+
$this->createDockerAdminUser();
462+
} else {
463+
$this->info("Nothing to migrate.");
464+
465+
// Still try to create admin user if it doesn't exist
466+
$this->createDockerAdminUser();
467+
}
468+
}
469+
401470
private function freshMigrations()
402471
{
403472
$this->info("Running fresh migrations (dropping all tables)...");
@@ -695,6 +764,71 @@ class {$className} extends Migration
695764
}
696765
}
697766

767+
private function createDockerAdminUser()
768+
{
769+
try {
770+
// Check if any admin user already exists
771+
$existingAdmin = $this->database->get('users', '*', ['role' => 'superadmin']);
772+
773+
if ($existingAdmin) {
774+
$this->info("Admin user already exists: " . $existingAdmin['email']);
775+
return;
776+
}
777+
778+
// Get admin credentials from environment variables
779+
$adminEmail = $_ENV['ADMIN_EMAIL'] ?? null;
780+
$adminPassword = $_ENV['ADMIN_PASSWORD'] ?? null;
781+
$adminName = $_ENV['ADMIN_NAME'] ?? 'Admin';
782+
783+
if (!$adminEmail || !$adminPassword) {
784+
$this->error("ADMIN_EMAIL and ADMIN_PASSWORD environment variables are required for Docker setup.");
785+
$this->info("Please set these in your .env file:");
786+
$this->info("ADMIN_EMAIL=admin@example.com");
787+
$this->info("ADMIN_PASSWORD=your_secure_password");
788+
return;
789+
}
790+
791+
// Validate email format
792+
if (!filter_var($adminEmail, FILTER_VALIDATE_EMAIL)) {
793+
$this->error("Invalid email format in ADMIN_EMAIL environment variable: {$adminEmail}");
794+
return;
795+
}
796+
797+
// Check if user with this email already exists
798+
$existingUser = $this->database->get('users', '*', ['email' => $adminEmail]);
799+
if ($existingUser) {
800+
$this->info("User with email '{$adminEmail}' already exists.");
801+
return;
802+
}
803+
804+
// Validate password length
805+
if (strlen($adminPassword) < 8) {
806+
$this->error("ADMIN_PASSWORD must be at least 8 characters long.");
807+
return;
808+
}
809+
810+
// Create admin user from environment variables
811+
$this->info("Creating admin user from environment variables...");
812+
813+
$hashedPassword = password_hash($adminPassword, PASSWORD_DEFAULT);
814+
$this->database->insert('users', [
815+
'name' => $adminName,
816+
'email' => $adminEmail,
817+
'password' => $hashedPassword,
818+
'role' => 'superadmin',
819+
'created_at' => date('Y-m-d H:i:s'),
820+
'updated_at' => date('Y-m-d H:i:s')
821+
]);
822+
823+
$this->success("Admin user created successfully!");
824+
$this->info("Email: {$adminEmail}");
825+
$this->info("Role: superadmin");
826+
827+
} catch (Exception $e) {
828+
$this->error("Failed to create admin user: " . $e->getMessage());
829+
}
830+
}
831+
698832
private function listUsers()
699833
{
700834
try {
@@ -996,6 +1130,7 @@ class {$className} extends Migration
9961130
$this->info(" migrate rollback [steps] Rollback migrations (default: 1 batch)");
9971131
$this->info(" migrate status Show migration status");
9981132
$this->info(" migrate reset Reset database (fresh + seed)");
1133+
$this->info(" migrate docker Run migrations for Docker environment");
9991134
$this->info("");
10001135
$this->info("Make commands:");
10011136
$this->info(" make migration <name> Create a new migration file");

0 commit comments

Comments
 (0)