Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Docker Image CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Build the Docker image
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)
name: Deploy New Design

on:
push:
branches:
- main # Trigger deployment on push to the main branch

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'

- name: Install Dependencies
run: npm install

- name: Build Project
run: npm run build

- name: Deploy to Hosting Service
env:
API_KEY: ${{ secrets.DEPLOY_KEY }}
run: npm run deploy
51 changes: 51 additions & 0 deletions .github/workflows/jekyll-gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled

on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v3

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
43 changes: 43 additions & 0 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
135 changes: 135 additions & 0 deletions CODE_REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# مراجعة كود مشروع Iris Web Framework

## نظرة عامة على المشروع

هذا المشروع هو **Iris Web Framework** - إطار عمل ويب مكتوب بلغة Go (الإصدار 12.2.11).
يتكون من ~282 ملف Go موزعة على حوالي 18 حزمة رئيسية.

---

## هيكل المشروع

```
iris/
├── iris.go # النقطة الرئيسية - Application struct
├── configuration.go # إعدادات الإطار (YAML/TOML/JSON)
├── aliases.go # Type aliases للتسهيل على المستخدمين
├── context/ # السياق الأساسي للطلبات (context.Context)
├── core/ # النواة: router, host, netutil, memstore
├── hero/ # Dependency Injection
├── middleware/ # Middlewares مدمجة (cors, recover, requestid...)
├── mvc/ # نمط MVC
├── sessions/ # إدارة الجلسات
├── view/ # محركات القوالب (HTML, Pug, Jet, Django...)
├── i18n/ # التعريب والترجمة
├── auth/ # المصادقة
├── cache/ # التخزين المؤقت
├── websocket/ # WebSocket
├── macro/ # Route macros و path parameters
├── versioning/ # API versioning
├── httptest/ # أدوات الاختبار
├── apps/ # Multi-app support
├── x/ # حزم تجريبية
├── _examples/ # أمثلة شاملة
└── _benchmarks/ # اختبارات الأداء
```

---

## النقاط الإيجابية

### 1. تصميم معماري ناضج
- فصل واضح بين الطبقات (Context, Router, Host, View)
- نمط الـ **Configurator Pattern** يوفر مرونة ممتازة في الإعدادات
- دعم متعدد لمحركات القوالب عبر interface موحد (`view.Engine`)

### 2. API سهل الاستخدام
- `aliases.go` يوفر اختصارات ذكية تقلل من الـ imports المطلوبة
- `New()` و `Default()` يقدمان مستويين من التهيئة (بسيط ومتقدم)
- Method chaining مريح: `app.Configure(...).SetName(...)`

### 3. إدارة جيدة للموارد
- استخدام `sync.Pool` للـ Context عبر `context.Pool` - ممتاز للأداء
- Mutex protection مناسب على `Application` struct (`mu sync.RWMutex`)
- دعم graceful shutdown مدمج مع signal handling

### 4. ميزات شاملة
- دعم TLS و AutoTLS (Let's Encrypt) مدمج
- Tunneling (ngrok) مدمج
- Minification للاستجابات (CSS, HTML, JS, JSON, XML, SVG)
- Compression مدمج
- نظام I18n متكامل

### 5. تغطية اختبارات
- وجود اختبارات في حزمة `hero/` مع ملفات `*_test.go`
- حزمة `httptest/` مخصصة لتسهيل الاختبارات

---

## الملاحظات والمشاكل المكتشفة

### 1. أخطاء إملائية في الكود (Typos)

| الموقع | الخطأ | الصحيح |
|--------|-------|--------|
| `iris.go:88` | `builded` | `built` |
| `iris.go:101` | `envrinoment` (في التعليق) | `environment` |
| `iris.go:329` | `existss` (في التعليق) | `exists` |
| `iris.go:389` | `hoods` (في التعليق) | `hood` |
| `aliases.go:377` | `clopy` (في التعليق) | `copy` |
| `aliases.go:489` | `instaed` (في التعليق) | `instead` |
| `aliases.go:665` | `recude` (في التعليق) | `reduce` |
| `iris.go:692` | `builded = true` | `built = true` |

### 2. استخدام `interface{}` بدل Generics
- `go.mod` يحدد `go 1.24` لكن الكود لا يزال يستخدم `interface{}` بكثرة بدل `any` (الذي هو alias لـ `interface{}` منذ Go 1.18)
- أمثلة: `iris.go:131`, `iris.go:342`, `iris.go:424`
- الـ README يذكر العمل على إصدار يعتمد Generics لكنه لم ينعكس بالكامل

### 3. Global State و Package-level Variables
- `context.GetDomain` (في `iris.go:547`) هو متغير global يُعدّل في runtime - هذا خطير في حالة تشغيل عدة تطبيقات Iris في نفس العملية
- `context.WriteJSON`, `context.WriteJSONP`, إلخ (في `aliases.go`) - نفس المشكلة
- `context.SetCookieKVExpiration` - حالة مشتركة عالمية

### 4. كود مُعلّق (Dead Code)
- `iris.go:151-167`: كتلة كود معلقة كبيرة تتعلق بـ access log (`/* #2046 ... */`)
- `iris.go:631-638`: دالة `OnShutdown` معلقة بالكامل
- `iris.go:346-355`: كود reflect معلق داخل `Validate()`
- `iris.go:797`: تعليق غير مكتمل (`// if end := time.Since(start)...`)

### 5. معالجة أخطاء يمكن تحسينها
- `iris.go:342-365` (`Validate`): إذا كان `Validator` هو `nil`، يرجع `nil` بصمت بدون أي validation. قد يكون من الأفضل إرجاع خطأ أو على الأقل تسجيل تحذير
- `iris.go:1219`: `nolint:errcheck` يتجاهل خطأ الكتابة - يجب التعامل معه

### 6. حقل `builded` بدل `built`
- `iris.go:87-88`: الحقل `builded` ليس فقط خطأ إملائي، بل أيضاً لا يوجد حماية thread-safe عليه (لا يستخدم atomic أو mutex عند قراءته في `Build()`)

### 7. تعقيد الـ `aliases.go`
- الملف يحتوي على ~880 سطر معظمه type aliases وconstant aliases
- هذا يجعل من الصعب معرفة الأنواع الحقيقية ومصادرها
- يضيف طبقة indirection قد تربك المطورين الجدد

### 8. شجرة اعتماديات ثقيلة
- `go.mod` يحتوي على ~50+ اعتماد مباشر و ~40+ اعتماد غير مباشر
- اعتماديات كبيرة مثل `badger/v4`, `bbolt`, `redis`, `protobuf` قد لا يحتاجها كل مستخدم
- يُفضل فصلها كحزم اختيارية (plugins)

### 9. `Default()` يفتح CORS للجميع
- `iris.go:179-183`: `cors.AllowAnyOrigin` - هذا قد يكون خطير أمنياً إذا استُخدم في الإنتاج دون تعديل
- يجب أن يكون هناك تحذير أوضح في التوثيق

---

## التقييم العام

| المعيار | التقييم |
|---------|---------|
| هيكل المشروع | جيد جداً |
| جودة الكود | جيد (مع بعض الملاحظات) |
| التوثيق | جيد (أمثلة كثيرة) |
| الأمان | يحتاج انتباه (CORS, global state) |
| الاختبارات | متوسط (ليست شاملة لكل الحزم) |
| الأداء | ممتاز (sync.Pool, minification, compression) |
| سهولة الاستخدام | ممتاز |

**الخلاصة**: مشروع ناضج وشامل مع API مصمم بعناية. أبرز ما يحتاج تحسين هو: تنظيف الكود المعلق، إصلاح الأخطاء الإملائية، تقليل الـ global state، والانتقال الكامل لـ Generics بما يتوافق مع Go 1.24.
113 changes: 113 additions & 0 deletions _examples/dashboard-ar/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package main

import (
"fmt"
"runtime"
"sync/atomic"
"time"

"github.com/kataras/iris/v12"
)

var (
startTime = time.Now()
requestCount atomic.Int64
)

func main() {
app := iris.New()

app.RegisterView(iris.HTML("./views", ".html"))
app.HandleDir("/static", "./static")

// Middleware to count requests.
app.Use(func(ctx iris.Context) {
requestCount.Add(1)
ctx.Next()
})

app.Get("/", dashboardHandler)
app.Get("/routes", routesHandler)
app.Get("/api/stats", statsAPIHandler)

app.Listen(":8080")
}

func dashboardHandler(ctx iris.Context) {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)

data := iris.Map{
"Title": "لوحة التحكم",
"Page": "dashboard",
"Version": iris.Version,
"Uptime": formatDuration(time.Since(startTime)),
"Requests": requestCount.Load(),
"RoutesCount": len(ctx.Application().GetRoutes()),
"MemoryMB": fmt.Sprintf("%.1f", float64(mem.Alloc)/1024/1024),
"GoVersion": runtime.Version(),
"NumGoroutine": runtime.NumGoroutine(),
}

ctx.ViewLayout("layouts/main")
if err := ctx.View("index", data); err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.WriteString(err.Error())
}
}

func routesHandler(ctx iris.Context) {
type routeInfo struct {
Method string
Path string
Handler string
}

routes := ctx.Application().GetRoutes()
routeList := make([]routeInfo, 0, len(routes))
for _, r := range routes {
routeList = append(routeList, routeInfo{
Method: r.Method,
Path: r.Path,
Handler: r.MainHandlerName,
})
}

data := iris.Map{
"Title": "المسارات المسجلة",
"Page": "routes",
"Routes": routeList,
}

ctx.ViewLayout("layouts/main")
if err := ctx.View("routes", data); err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.WriteString(err.Error())
}
}

func statsAPIHandler(ctx iris.Context) {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)

ctx.JSON(iris.Map{
"uptime": formatDuration(time.Since(startTime)),
"requests": requestCount.Load(),
"memoryMB": fmt.Sprintf("%.1f", float64(mem.Alloc)/1024/1024),
"numGoroutine": runtime.NumGoroutine(),
})
}

func formatDuration(d time.Duration) string {
h := int(d.Hours())
m := int(d.Minutes()) % 60
s := int(d.Seconds()) % 60

if h > 0 {
return fmt.Sprintf("%d س %d د %d ث", h, m, s)
}
if m > 0 {
return fmt.Sprintf("%d د %d ث", m, s)
}
return fmt.Sprintf("%d ث", s)
}
Loading