This commit is contained in:
pbao9
2026-02-03 09:22:40 +07:00
parent e45556d09c
commit 214fca9ef6
7 changed files with 285 additions and 233 deletions
+179
View File
@@ -0,0 +1,179 @@
---
description: Code Style Guide
---
# Laravel 10 – Repository Pattern
## Strict Coding Rules (Anti-Trash & Anti-Overengineering)
This document defines mandatory rules to prevent meaningless files, useless comments, and over-engineered architecture.
---
## 0. Core Principle
Do not create code unless it introduces real behavior.
Do not write comments to compensate for bad code.
Readable code replaces comments.
Behavior justifies files.
---
## 1. Comment Rules (VERY STRICT)
Forbidden:
- Comments repeating code meaning
- Comments describing method names
- Placeholder comments
- TODO without ticket reference
Allowed only to explain WHY:
- Architectural decisions
- Non-obvious side effects
- Constraints or workarounds
---
## 2. File Creation Rules
Do NOT create files:
- For clean structure only
- For symmetry
- For future assumptions
- To wrap a single call
---
## 3. When a New File Is Allowed
At least ONE condition must be true:
1. Real business rules exist
2. Logic reused in 2+ places
3. Side effects exist (transaction, cache, events)
---
## 4. Repository Rules
Repositories MAY:
- Query data
- Filter
- Paginate
- Cache
Repositories MUST NOT:
- Validate
- Apply business rules
- Format responses
- Check permissions
---
## 5. Service Layer Rules
Service allowed only if:
- Uses multiple repositories
- Manages transactions
- Coordinates caching
- Enforces domain rules
Proxy-only services are forbidden.
---
## 6. Controller Rules
Controllers MAY:
- Accept Request
- Call Service or Repository
- Return Response
Controllers MUST NOT:
- Contain business logic
- Query database directly
---
## 7. Interface Rules
Do NOT create interfaces if only one implementation exists.
Interfaces allowed only when:
- Multiple implementations exist
- Cache on/off switching
- Required for testing
---
## 8. Helper Rules
Forbidden:
- Business logic in helpers
Allowed:
- String
- Date
- Array utilities
---
## 9. Naming Rules
Forbidden:
handle, process, execute, run, doSomething
Required:
publishCourse, syncStudentProgress, calculateFinalScore
---
## 10. Exception Rules
Forbidden:
throw new Exception()
Required:
Explicit domain-specific exceptions
---
## 11. Folder Structure Rules
Avoid over-fragmentation.
One concept = minimal files.
---
## 12. File Count Sanity Check
CRUD feature <= 5 files is acceptable.
More than 10 files indicates over-engineering.
---
## 13. Code Review Rejection Checklist
Reject if:
- File has no behavior
- Service only proxies calls
- Comment explains WHAT not WHY
- Interface has one implementation
- File exists for future use
---
## 14. AI Enforcement Rules
- Do not create files without behavior
- Do not generate meaningless comments
- Do not create proxy layers
- Prefer fewer, clearer files
- Reject over-engineering
---
## 15. Final Rule
Architecture serves behavior.
If code has no clear reason to exist, remove it.
-151
View File
@@ -1,151 +0,0 @@
# GitHub Actions Deployment Workflows
Các workflow tự động deploy được tạo cho project Laravel Portal.
## Workflows Available
### 1. `deploy.yml` - Deployment thông thường
- **Trigger**: Push vào branches `main`, `master`, `develop` hoặc manual trigger
- **Mục đích**: Deploy cho staging/development environment
- **Actions**: Git pull, npm run production, Laravel optimization
### 2. `deploy-minified.yml` - Production Deployment với minification
- **Trigger**: Push vào branches `main`, `master` hoặc manual trigger
- **Mục đích**: Deploy cho production environment
- **Actions**: Git pull, npm run minify-all, database migration, backup, cleanup
## Setup Requirements
### 1. GitHub Secrets Configuration
Cần setup các secrets sau trong GitHub repository:
#### For `deploy.yml`:
```
HOST - IP address hoặc domain của server
USERNAME - SSH username
SSH_KEY - SSH private key content
PORT - SSH port (optional, default: 22)
PROJECT_PATH - Đường dẫn tới project (optional, default: /var/www/portal)
```
#### For `deploy-minified.yml`:
```
PROD_HOST - Production server IP/domain
PROD_USERNAME - Production SSH username
PROD_SSH_KEY - Production SSH private key
PROD_PORT - Production SSH port (optional, default: 22)
PROD_PROJECT_PATH - Production project path (optional, default: /var/www/portal)
```
### 2. Server Requirements
#### Server cần có sẵn:
- Git
- PHP >= 8.0
- Composer
- Node.js & NPM
- Web server (nginx/apache) với quyền www-data
#### Project directory structure:
```
/var/www/portal/ # Main project
/var/www/portal-backup-* # Backup directories (auto-created)
```
### 3. SSH Key Setup
1. **Generate SSH key pair:**
```bash
ssh-keygen -t rsa -b 4096 -C "github-actions@yourdomain.com"
```
2. **Add public key to server:**
```bash
# Copy public key to server
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
```
3. **Add private key to GitHub Secrets:**
- Copy nội dung của private key file
- Paste vào GitHub Secrets với tên `SSH_KEY` hoặc `PROD_SSH_KEY`
## Manual Deployment
Có thể trigger deployment manually:
1. Vào GitHub repository
2. Click tab "Actions"
3. Chọn workflow muốn chạy
4. Click "Run workflow"
5. Chọn branch và click "Run workflow"
## Workflow Features
### Security Features:
- SSH key authentication
- Separate production secrets
- Backup before deployment
### Performance Features:
- Asset minification cho production
- Laravel optimization commands
- Cache clearing/rebuilding
### Maintenance Features:
- Automatic backup rotation (keep last 5)
- Queue restart
- Permission fixing
- Database migration
## Troubleshooting
### Common Issues:
1. **SSH Connection Failed:**
- Kiểm tra HOST, USERNAME, SSH_KEY secrets
- Verify SSH key format (complete private key including headers)
2. **Permission Denied:**
- Kiểm tra SSH user có quyền access project directory
- Verify www-data ownership
3. **Build Failed:**
- Kiểm tra Node.js version trên server
- Verify npm dependencies
4. **Laravel Commands Failed:**
- Kiểm tra PHP version compatibility
- Verify .env file exists và correct
### Debug Commands:
Để debug, có thể thêm các commands sau vào workflow:
```yaml
# Add to workflow script section
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo "PHP version: $(php -v)"
echo "Node version: $(node -v)"
echo "NPM version: $(npm -v)"
```
## Environment-specific Customization
Có thể customize workflows theo environment:
### Development:
- Sử dụng `npm run dev` thay vì `production`
- Skip database migrations
- Enable debug mode
### Staging:
- Sử dụng `npm run production`
- Include database migrations
- Moderate optimization
### Production:
- Sử dụng `npm run minify-all`
- Full optimization
- Backup và cleanup
- Queue restart
+37
View File
@@ -0,0 +1,37 @@
<?php
return [
'vite' => [
'css' => [
'resources/sass/app.scss',
'resources/css/components/micromodal.css',
],
'js' => [
'resources/js/pages/color-modes.js',
'resources/js/app-vite.js',
'resources/js/pages/template.js',
'resources/js/components/micromodal.js',
],
],
'static' => [
'css' => [
'build/plugins/perfect-scrollbar/perfect-scrollbar.css',
],
'js' => [
'build/plugins/bootstrap/bootstrap.bundle.min.js',
'build/plugins/lucide/lucide.min.js',
'build/plugins/perfect-scrollbar/perfect-scrollbar.min.js',
],
],
'datatable' => [
'css'=> [
'build/plugins/select2/select2.min.css'
],
'js' => [
'build/plugins/jquery/jquery.min.js',
'build/plugins/select2/select2.min.js',
'build/plugins/datatables.net/dataTables.min.js',
'build/plugins/datatables.net-bs5/dataTables.bootstrap5.min.js'
]
]
];
@@ -15,11 +15,8 @@
<title>Thế Giới Số Admin | Service Management Portal</title>
<!-- color-modes:js -->
@vite(['resources/js/pages/color-modes.js'])
<script>
(function () {
(function () {
const theme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ?
'dark' : 'light');
document.documentElement.setAttribute('data-bs-theme', theme);
@@ -36,17 +33,14 @@
<link rel="shortcut icon" href="{{ asset('/favicon.ico') }}">
<!-- plugin css -->
<link href="{{ asset('build/plugins/perfect-scrollbar/perfect-scrollbar.css') }}" rel="stylesheet" />
@foreach (config('assets.static.css') as $css)
<link href="{{ asset($css) }}" rel="stylesheet" />
@endforeach
@stack('plugin-styles')
<!-- CSS for LTR layout-->
@vite(['resources/sass/app.scss'])
<!-- Global Modal CSS -->
@vite(['resources/css/components/micromodal.css'])
<!-- CSS for RTL layout-->
<!-- Vite Assets (CSS & JS) -->
@vite(array_merge(config('assets.vite.css'), config('assets.vite.js')))
@stack('style')
</head>
@@ -65,24 +59,16 @@
</div>
</div>
<!-- base js -->
@vite(['resources/js/app-vite.js'])
<script src="{{ asset('build/plugins/bootstrap/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('build/plugins/lucide/lucide.min.js') }}"></script>
<script src="{{ asset('build/plugins/perfect-scrollbar/perfect-scrollbar.min.js') }}"></script>
<!-- base js (static) -->
@foreach (config('assets.static.js') as $js)
<script src="{{ asset($js) }}"></script>
@endforeach
<!-- end base js -->
<!-- plugin js -->
@stack('plugin-scripts')
<!-- end plugin js -->
<!-- common js -->
@vite(['resources/js/pages/template.js'])
<!-- Global Modal JS -->
@vite(['resources/js/components/micromodal.js'])
<!-- end common js -->
<!-- Global Select2 initialization -->
<script>
window.initSelect2 = function() {
@@ -92,7 +78,7 @@
height: "100%",
});
};
// Global ModalManager initialization
document.addEventListener('DOMContentLoaded', function() {
if (typeof ModalManager !== 'undefined') {
@@ -1,58 +1,58 @@
@extends("administrator.layout.master")
@push('plugin-styles')
<link href="{{ asset('build/plugins/select2/select2.min.css') }}" rel="stylesheet" />
@foreach (config('assets.datatable.css') as $css)
<link href="{{ asset($css) }}" rel="stylesheet" />
@endforeach
@endpush
@section('breadcrumb')
@php
$breadcrumbItems = [
['label' => 'Modules'],
['label' => 'Package vCenter'],
['label' => 'Datacenter', 'active' => true],
];
@endphp
<x-admin-breadcrumb :items="$breadcrumbItems" />
@endsection
@section("content")
@if(auth()->guard('admins')->user()->can("add module vcenter"))
<div class="card card-outline card-primary">
<form action="{{ route('package.datacenter.store') }}" method="post">
@csrf
<div class="card-body">
<x-module.v-center :model="null" />
@section('breadcrumb')
@php
$breadcrumbItems = [
['label' => 'Modules',],
['label' => 'Package vCenter', 'url' => route('package.datacenter.index')],
['label' => 'Datacenter', 'active' => true],
];
@endphp
<x-admin-breadcrumb :items="$breadcrumbItems" />
@endsection
@section("content")
@if(auth()->guard('admins')->user()->can("add module vcenter"))
<div class="card card-outline card-primary">
<form action="{{ route('package.datacenter.store') }}" method="post">
@csrf
<div class="card-body">
<x-module.v-center :model="null" />
{{--Name--}}
<div class="row form-group align-items-center mb-0">
<label for="name" class="col-md-3 text-right">Name</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="name" name="name" placeholder="" value="" class="form-control">
{{--Name--}}
<div class="row form-group align-items-center mb-0">
<label for="name" class="col-md-3 text-right">Name</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="name" name="name" placeholder="" value="" class="form-control">
</div>
</div>
{{--Datacenter ID--}}
<div class="row form-group align-items-center mb-0">
<label for="datacenter_id" class="col-md-3 text-right">Datacenter ID</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="datacenter_id" name="datacenter_id" placeholder="" value=""
class="form-control">
</div>
</div>
</div>
{{--Datacenter ID--}}
<div class="row form-group align-items-center mb-0">
<label for="datacenter_id" class="col-md-3 text-right">Datacenter ID</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="datacenter_id" name="datacenter_id" placeholder="" value=""
class="form-control">
</div>
<div class="card-footer">
<a href="{{ route('package.datacenter.index') }}" class="btn btn-outline-secondary">Trở về</a>
<button type="submit" class="btn btn-outline-primary">Lưu dữ liệu</button>
</div>
</div>
<div class="card-footer">
<a href="{{ route('package.datacenter.index') }}" class="btn btn-outline-secondary">Trở về</a>
<button type="submit" class="btn btn-outline-primary">Lưu dữ liệu</button>
</div>
</form>
</div>
@else
<div class="alert alert-danger alert-dismissible">
<h5 class="mb-0"><i class="icon fas fa-ban"></i> Access Denied - You are not assigned to any Module vCenter
management department</h5>
</div>
@endif
@endsection
</form>
</div>
@else
<div class="alert alert-danger alert-dismissible">
<h5 class="mb-0"><i class="icon fas fa-ban"></i> Access Denied - You are not assigned to any Module vCenter
management department</h5>
</div>
@endif
@endsection
@push('plugin-scripts')
<script src="{{ asset('build/plugins/jquery/jquery.min.js') }}"></script>
<script src="{{ asset('build/plugins/select2/select2.min.js') }}"></script>
<script src="{{ asset('build/plugins/datatables.net/dataTables.min.js') }}"></script>
<script src="{{ asset('build/plugins/datatables.net-bs5/dataTables.bootstrap5.min.js') }}"></script>
@foreach (config('assets.datatable.js') as $js)
<script src="{{ asset($js) }}"></script>
@endforeach
@endpush
@@ -1,6 +1,8 @@
@extends('administrator.layout.master')
@push('plugin-styles')
<link href="{{ asset('build/plugins/select2/select2.min.css') }}" rel="stylesheet" />
@foreach (config('assets.datatable.css') as $datatable)
<link href="{{ asset($datatable) }}" rel="stylesheet" />
@endforeach
@endpush
@section('breadcrumb')
@@ -35,9 +37,8 @@ $breadcrumbItems = [
</div>
@endif
@endsection
@push('plugin-scripts')
<script src="{{ asset('build/plugins/jquery/jquery.min.js') }}"></script>
<script src="{{ asset('build/plugins/select2/select2.min.js') }}"></script>
<script src="{{ asset('build/plugins/datatables.net/dataTables.min.js') }}"></script>
<script src="{{ asset('build/plugins/datatables.net-bs5/dataTables.bootstrap5.min.js') }}"></script>
@endpush
@push('plugin-scripts')
@foreach (config('assets.datatable.js') as $datatable)
<script src="{{ asset($datatable) }}"></script>
@endforeach
@endpush
@@ -1,7 +1,7 @@
@props(['items' => []])
<nav aria-label="breadcrumb">
<ol class="breadcrumb bg-white p-3 rounded-4 d-flex align-items-center mb-4">
<ol class="breadcrumb bg-white shadow-sm p-3 rounded-4 d-flex align-items-center mb-4">
<li class="breadcrumb-item">
<a href="{{ route('dashboard') }}"
class="d-flex align-items-center text-muted text-decoration-none transition-colors hover-text-primary">