update
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user