diff --git a/.gitignore b/.gitignore index 22bd45e..2442919 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,5 @@ yarn-debug.log* yarn-error.log* *.tmp *.temp -Tasks/ \ No newline at end of file +Tasks/ +**/.env.app \ No newline at end of file diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100644 new mode 100755 diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..c8797a7 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,17 @@ +FROM php:8.3-fpm + +WORKDIR /var/www/html + +RUN apt-get update && apt-get install -y \ + git unzip zip curl \ + libpng-dev libjpeg-dev libfreetype6-dev \ + libonig-dev libxml2-dev libzip-dev \ + && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +COPY docker/php/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["php-fpm"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..26aee84 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,113 @@ +# Laravel Docker Setup - thankcard-system + +## 1. Cấu trúc thư mục + +```txt +thankcard-system/ +├─ .env +├─ artisan +├─ composer.json +├─ public/ +├─ storage/ +├─ bootstrap/ +├─ doc/ +└─ docker/ + ├─ docker-compose.yml + ├─ Dockerfile + ├─ README.md + ├─ env/ + │ └─ .env.app + ├─ nginx/ + │ └─ default.conf + └─ php/ + └─ entrypoint.sh +``` + +--- + +## 2. Khởi chạy dự án (Docker) + +Đứng tại thư mục root của dự án `thankcard-system` và chạy: + +```bash +docker compose -f docker/docker-compose.yml up -d --build +``` + +Hoặc di chuyển vào thư mục `docker/` rồi chạy: + +```bash +cd docker +docker compose up -d --build +``` + +Sau khi các container chạy thành công, truy cập hệ thống tại trình duyệt: + +```txt +http://localhost:8888 +``` + +--- + +## 3. Cơ chế cấu hình môi trường (.env) + +* File `.env` chính của Laravel vẫn nằm ở thư mục root để phục vụ máy local. +* Khi chạy trong container Docker, hệ thống sẽ sử dụng file **`docker/env/.env.app`** để ghi đè các cấu hình cần thiết (ví dụ: `DB_HOST=host.docker.internal` giúp PHP container kết nối ra cơ sở dữ liệu trên máy thật). + +--- + +## 4. Các lệnh cần chạy thủ công khi cập nhật code + +### 4.1. Khi thêm/cập nhật thư viện mới (`composer.json`) +Khi cập nhật hoặc cài đặt gói package PHP mới, bạn cần chạy lệnh cài đặt **bên trong container**: +```bash +docker compose -f docker/docker-compose.yml exec app composer install +``` + +### 4.2. Khi cập nhật giao diện (CSS / JS / Tailwind classes mới) +Do Docker container không cài Node.js, bạn cần chạy các lệnh compile frontend trực tiếp **ở máy thật (host)**: +* **Chạy chế độ phát triển (Hot Reload):** + ```bash + npm run dev + ``` +* **Build production tĩnh:** + ```bash + npm run build + ``` + +### 4.3. Khi cập nhật Database (Migrations) +Để cập nhật cơ sở dữ liệu khi có migrations mới: +```bash +docker compose -f docker/docker-compose.yml exec app php artisan migrate +``` + +### 4.4. Khi chỉnh sửa file `.env` hoặc `.env.app` +Bạn cần khởi động lại container để cập nhật biến môi trường mới và tiến hành xóa cache cấu hình: +```bash +# 1. Restart container app +docker compose -f docker/docker-compose.yml restart app + +# 2. Xóa cache cấu hình trong container +docker compose -f docker/docker-compose.yml exec app php artisan config:clear +``` + +--- + +## 5. Các lệnh tiện ích khác + +* **Truy cập vào Bash của container app:** + ```bash + docker compose -f docker/docker-compose.yml exec app bash + ``` +* **Xem log Laravel thời gian thực:** + ```bash + docker compose -f docker/docker-compose.yml exec app tail -f storage/logs/laravel.log + ``` +* **Dọn dẹp toàn bộ cache của Laravel:** + ```bash + docker compose -f docker/docker-compose.yml exec app php artisan optimize:clear + ``` +* **Dừng các container:** + ```bash + docker compose -f docker/docker-compose.yml down + ``` + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..549c9f9 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,33 @@ +services: + app: + build: + context: .. + dockerfile: docker/Dockerfile + image: thankcard-system-app + container_name: thankcard-system-app + working_dir: /var/www/html + volumes: + - ..:/var/www/html + env_file: + - ./env/.env.app + networks: + - thankcard-system-network + extra_hosts: + - "host.docker.internal:host-gateway" + + nginx: + image: nginx:1.25-alpine + container_name: thankcard-system-nginx + ports: + - "8888:80" + volumes: + - ..:/var/www/html + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf + depends_on: + - app + networks: + - thankcard-system-network + +networks: + thankcard-system-network: + name: thankcard-system-network diff --git a/docker/env/.env.example b/docker/env/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf new file mode 100644 index 0000000..0657e5b --- /dev/null +++ b/docker/nginx/default.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name localhost; + + root /var/www/html/public; + index index.php index.html; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param DOCUMENT_ROOT $realpath_root; + } + + location ~ /\.ht { + deny all; + } +} diff --git a/docker/php/entrypoint.sh b/docker/php/entrypoint.sh new file mode 100644 index 0000000..c9d0ab9 --- /dev/null +++ b/docker/php/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +cd /var/www/html + +if [ ! -f "vendor/autoload.php" ]; then + composer install +fi + +mkdir -p storage/logs \ + storage/framework/cache \ + storage/framework/sessions \ + storage/framework/views \ + bootstrap/cache + +chmod -R 777 storage bootstrap/cache + +exec "$@" diff --git a/resources/css/app.css b/resources/css/app.css index 0aa9be9..db67d17 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -113,7 +113,7 @@ } .sidebar-link { - @apply flex items-center gap-3 px-4 py-3 text-text-medium font-medium rounded-lg hover:bg-gray-50 hover:text-primary transition-all duration-200; + @apply flex items-center gap-3 px-4 py-3 text-text-medium text-sm font-medium rounded-lg hover:bg-gray-50 hover:text-primary transition-all duration-200; } .sidebar-link-active { @@ -174,7 +174,7 @@ } .form-header-title { - @apply text-[24px] sm:text-[32px] font-[600] text-text-dark leading-tight; + @apply text-sm font-[600] text-text-dark leading-tight; } .form-body { @@ -191,7 +191,7 @@ /* Standard Input & Select */ .form-input { - @apply w-full h-[48px] px-4 bg-white border border-[#d9dfe7] rounded-lg outline-none text-text-dark text-base placeholder-text-muted focus:border-primary focus:ring-2 focus:ring-primary/20 hover:border-gray-400 transition-all duration-200 shadow-sm; + @apply w-full h-[48px] px-4 bg-white border border-[#d9dfe7] rounded-lg outline-none text-text-dark text-sm placeholder-text-muted focus:border-primary focus:ring-2 focus:ring-primary/20 hover:border-gray-400 transition-all duration-200 shadow-sm; } select.form-input { @@ -235,11 +235,11 @@ select.form-input { /* Action Buttons */ .btn-primary { - @apply bg-primary hover:bg-primary-hover text-white font-[600] h-[45px] max-h-[45px] px-8 border border-transparent rounded-lg shadow-sm transition-all duration-200 text-center cursor-pointer inline-flex items-center justify-center focus:ring-2 focus:ring-offset-1 focus:ring-primary/50 box-border; + @apply bg-primary hover:bg-primary-hover text-white text-sm font-[600] h-[45px] max-h-[45px] px-8 border border-transparent rounded-lg shadow-sm transition-all duration-200 text-center cursor-pointer inline-flex items-center justify-center focus:ring-2 focus:ring-offset-1 focus:ring-primary/50 box-border; } .btn-secondary { - @apply bg-white hover:bg-gray-50 text-text-medium border border-[#d9dfe7] font-[600] h-[45px] max-h-[45px] px-8 rounded-lg shadow-sm transition-all duration-200 text-center cursor-pointer inline-flex items-center justify-center focus:ring-2 focus:ring-offset-1 focus:ring-gray-200 box-border; + @apply bg-white hover:bg-gray-50 text-text-medium text-sm border border-[#d9dfe7] font-[600] h-[45px] max-h-[45px] px-8 rounded-lg shadow-sm transition-all duration-200 text-center cursor-pointer inline-flex items-center justify-center focus:ring-2 focus:ring-offset-1 focus:ring-gray-200 box-border; } .nav-badge { diff --git a/storage/app/.gitignore b/storage/app/.gitignore deleted file mode 100644 index fedb287..0000000 --- a/storage/app/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!private/ -!public/ -!.gitignore diff --git a/storage/app/private/.gitignore b/storage/app/private/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/app/private/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/app/public/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore deleted file mode 100644 index 05c4471..0000000 --- a/storage/framework/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -compiled.php -config.php -down -events.scanned.php -maintenance.php -routes.php -routes.scanned.php -schedule-* -services.json diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore deleted file mode 100644 index 01e4a6c..0000000 --- a/storage/framework/cache/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!data/ -!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/cache/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/sessions/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/testing/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/views/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/logs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore