الملاحق
شجرةُ المهارات — مكتملةً
الشجرتان نبتتا متوازيتين والتحمتا في الكابستون. كلُّ عقدةٍ متّصلةٌ صراحةً بما تحتها؛ لا شيءَ يطفو معلّقاً. الأرقامُ أقاليم.
ثلاثُ سلاسلَ ناظمةٍ تمرّ عبر الشجرة
- خيطُ الـ erasure: (١) الأنواع تُمحى → (٣)
req.body: any→ (٥) Prisma يعيد النوعَ من سكيمة القاعدة → (٦) Zod يعيده من سكيمة التحقّق → (٩) رحلةُ النوع كاملةً. - خيطُ انعدام الحالة: (٠) HTTP بلا ذاكرة (لِلتوسّع) → (٤) الحالةُ في القاعدة → (٧) الهويّةُ يحملها العميل (JWT) → (٩) الثالوث: خادمٌ عديمُ الحالة + حالةٌ في Postgres + هويّةٌ محمولة.
- خيطُ "لا تحجب الحلقة": (٢) خيطٌ واحدٌ مقدّس → (٧) bcrypt على المجمّع → (٥) استعلامٌ لا-حاجب → (١٠) تصريفُ C في عمليّةٍ فرعيّة.
الحرّاسُ الثلاثة (أين يقف كلٌّ)
| الحارس | الزمن | الموضع في المسار | المرجع |
|---|---|---|---|
| مترجم TS | الكتابة/الترجمة | المصدر (ثم يُمحى) | (١) |
| Zod | التشغيل | مدخلُ سلسلة Express، قبل المتحكّم | (٦) |
| قيودُ Postgres | التشغيل | القاع، آخرُ خطّ | (٤) |
النماذجُ الذهنيّة
عشرةُ نماذجَ، كلٌّ تختزل إقليماً في جملةٍ تشتقّ منها الباقي. هذه ما يبقى بعد أن تنسى التفاصيل.
١. HTTP = رسالةٌ مؤطَّرةٌ ذاتُ معنى داخل قناة TCP
TCP بايتاتٌ بلا حدود؛ HTTP يرسم الحدودَ والمعنى فوقها: الرأسُ نصٌّ مؤطَّرٌ بفاصل (CRLF + سطرٌ فارغ)، والجسمُ مؤطَّرٌ بطولٍ مُعلَن (Content-Length). الخادمُ برنامجٌ يقرأ هذا الشكلَ ويكتب مثلَه. (٠)
٢. النوعُ على القيمة، لا المتغيّر — وأنواعُ TS تُمحى لصفر
في C النوعُ على المتغيّر ويترك أثراً في الكود؛ في JS القيمةُ تحمل نوعَها وقت التشغيل (tagged value) والمتغيّرُ مجرّد اسم. وأنواعُ TS خياليّةٌ تُمحى بلا أيِّ أثرٍ وقت التشغيل — شِيكٌ خالصٌ ينتهي عند حدّ البرنامج. (١)
٣. الخادمُ السريعُ لا ينتظر وهو واقف (event loop = reactor)
Node = V8 + libuv فوق epoll/kqueue. خيطٌ واحد، I/O لا-حاجب، دوالٌّ كـ callbacks (إغلاق). await = تنازلٌ للحلقة + استئنافٌ لاحق، لا حجبُ خيط. والعملُ المحسوب على خيط الحلقة يجمّد الجميع → ادفعه للمجمّع/عمليّةٍ فرعيّة. (٢)
٤. الإطارُ = عقدُ دالةٍ موحَّدٌ + مُشغِّلٌ يمرّر التحكّم
Express سلسلةٌ من دوالِّ (req,res,next)؛ كلٌّ تفعل شيئاً ثم تمرّر أو تنهي أو تقفز للخطأ. الأمن/CORS/التحليل/التحقّق/الهويّة/التوجيه/الأخطاء — كلُّها حلقاتٌ في سلسلةٍ واحدة. الترتيبُ بنيةٌ لا زينة. (٣)
٥. الجدولُ = مجموعةٌ رياضيّة، لا ملفٌّ بصفوف
العلائقيّ يفصل "ما البيانات" عن "كيف تُخزَّن". كلُّ حقيقةٍ تعيش مرّةً واحدةً (تطبيع)؛ المفاتيحُ الأجنبيّةُ تربط؛ القيودُ تحرس؛ SQL تصريحيّةٌ (تقول ماذا، يقرّر المخطِّطُ كيف). (٤)
٦. توليدُ الكود يجسر هاوية الـ erasure
الأنواعُ تُمحى وقت التشغيل، فتُستعاد من سكيمةٍ تعيش وقت التشغيل: Prisma يولّدها من سكيمة القاعدة، وZod يشتقّها من سكيمة التحقّق. الوجهان يجسران نفسَ الهاوية من طرفين. (٥، ٦)
٧. كلُّ حدٍّ يحتاج بوّابةً تعمل وقت التشغيل
ضمانةُ المترجم تنتهي عند حدّ البرنامج؛ ما يدخل من الخارج (شبكة/ملفّ/قاعدة) يصل حيث لا أنواع. ثلاثُ حراساتٍ: مترجمٌ (داخليّ، مُحيٌّ) / Zod (حدُّ الشبكة، لطفٌ) / قيودُ القاعدة (القاع، صحّةٌ حتميّة). لا غنى عن أيٍّ. (٦، ٤)
٨. الثقةُ بلا حالةٍ ولا استرجاع = اللاتماثل
bcrypt: سهلٌ أماماً (جزّئ)، مستحيلٌ عكساً (استرجِع) — تجزئةٌ بطيئةٌ مملَّحة. JWT: سهلٌ التحقّق بالسرّ، مستحيلٌ التزوير بلا السرّ. التوقيعُ ≠ تشفير — payload مقروءٌ للجميع، لا تضع فيه سرّاً. فيبقى الخادمُ عديمَ الحالة ويعرفك. (٧)
٩. الواجهةُ = دالّةٌ في الحالة (UI = f(state))
لا تعبث بـ DOM؛ صف كيف تبدو الشاشةُ لكلِّ حالة، وغيّر الحالةَ فقط — وReact يطبّق الفرقَ الأدنى. الحالةُ مصدرُ حقيقةٍ واحد، فلا تباعدَ بين البيانات والشاشة. الـ hooks تقف على الإغلاق. (٨)
١٠. أعلى الستاك يخدم أعمقَ مهاراتك
المنصّةُ العاليةُ كلُّها قشرةٌ تُنسّق تشغيلَ C في Docker بأمان: child_process لا-حاجب (٢) + عزلُ الحاوية (cgroups/namespaces/non-root/no-net) + معاملةٌ ذرّيّة. كلُّ تهديدٍ يقابله بدائيٌّ تعرفه. (١٠)
الثالوثُ المعماريّ (لماذا يتوسّع النظام)
خادمٌ عديمُ الحالة (أيُّ نسخةٍ تخدم أيَّ طلب) + حالةٌ في Postgres + هويّةٌ يحملها العميلُ في JWT. هذا الثالوثُ هو وعدُ منهج البنية، محقَّقاً بآليّاتٍ بنيتَها.
الورقةُ المرجعيّة
مرجعٌ سريعٌ لِما بنيتَه. ليس بديلاً عن الأقاليم — تذكيرٌ بعد الإتقان.
HTTP (٠)
- رسالة:
METHOD target HTTP/1.1⏎Header: value⏎ (سطرٌ فارغ ⏎) ⏎ جسم. الأسطرCRLF. - الأفعال:
GET(آمن/idempotent) ·POST(إنشاء، لا idempotent) ·PUT(استبدال، idempotent) ·PATCH·DELETE(idempotent). - الرموز:
2xxنجح ·3xxتحويل ·4xxخطؤك (400تحقّق ·401من أنت ·403ممنوع ·404·409تعارض) ·5xxخطأ الخادم (500). - تأطير:
Content-Type(تفسيرُ الجسم) +Content-Length(نهايتُه). بلا حالة.
Node (٢)
- خيطٌ واحد + event loop (epoll).
awaitلا يحجب. لا تضع عملاً محسوباً على الحلقة. - ثقيل/غريب →
child_process(عمليّة) أو مجمّع libuv (fs/crypto/bcrypt). process.env.Xللإعدادات/الأسرار.req/res= streams (الجسمُ يصل قطعاً).- وحدات:
./xملفُّك ·xمنnode_modules.package.json/-lock+npm ci.
Express (٣)
tsapp.use(express.json()); // محلّلُ جسم → req.body app.use(helmet()); app.use(cors()); // حلقاتٌ في السلسلة app.get("/path/:id", (req,res) => res.status(200).json({success:true,data:{}})); const r = express.Router(); r.post("/register", ctrl); app.use("/auth", r); app.use((err,req,res,next) => res.status(err.status||500).json({success:false,message:err.message}));
- middleware:
(req,res,next)→next()/res.json()/next(err). - الترتيب: helmet→cors→json→rate-limit→validate→authenticate→authorize→controller→errorHandler.
- طبقات: route(خريطة)/controller(HTTP)/service(منطق+Prisma)/schema(Zod).
- Express 5 يلتقط رفضَ async تلقائياً.
SQL / Postgres (٤)
sqlCREATE TYPE "Role" AS ENUM ('USER','ADMIN'); CREATE TABLE users (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT NOT NULL UNIQUE, role "Role" NOT NULL DEFAULT 'USER'); CREATE TABLE sprints (id UUID PRIMARY KEY, curriculum_id UUID NOT NULL REFERENCES curricula(id) ON DELETE CASCADE); -- مفتاحٌ أجنبيّ (١:كثير) SELECT s.* FROM sprints s JOIN curricula c ON s.curriculum_id=c.id WHERE c.id=$1; SELECT c.title, COUNT(e.id) FROM curricula c LEFT JOIN enrollments e ON e.curriculum_id=c.id GROUP BY c.id; BEGIN; ...; COMMIT; -- أو ROLLBACK (ذرّيّة ACID) CREATE INDEX idx ON sprints(curriculum_id); EXPLAIN ANALYZE SELECT ...;
- تطبيع: كلُّ حقيقةٍ مرّةً واحدة. كثير-لكثير = جدولُ وصلٍ (junction).
Prisma (٥)
tsprisma.user.findUnique({ where:{ email } }); prisma.user.findMany({ where:{role:"ADMIN"}, select:{id:true}, orderBy:{createdAt:"desc"} }); prisma.user.create({ data:{...}, select:{id:true} }); // select يُخفي password prisma.curriculum.findUnique({ where:{id}, include:{ sprints:true } }); // include = JOIN prisma.$transaction(async (tx) => { await tx.a.create(); await tx.b.update(); }); new PrismaClient({ log:["query"] }); // اطبع الـ SQL دائماً
- خطّان:
migrate dev/deploy(بنية) ·generate(أنواع). احذر N+1 → استعملinclude.
Zod (٦)
tsconst s = z.object({ email: z.email(), password: z.string().min(8), role: z.enum(["USER","ADMIN"]).optional() }); const r = s.safeParse(req.body); if (!r.success) return res.status(400).json({ details: z.flattenError(r.error).fieldErrors }); type T = z.infer<typeof s>; // النوعُ من السكيمة (وجهٌ مقابلٌ لـ Prisma) // middleware: const validate = (schema)=>(req,res,next)=>{const r=schema.safeParse(req.body); ...}
الهويّة (٧)
tsconst hash = await bcrypt.hash(pw, 10); // مملَّحٌ بطيءٌ على المجمّع const ok = await bcrypt.compare(candidate, hash);// لا decrypt // JWT من الصفر: base64url(header).base64url(payload).HMAC-SHA256(.,SECRET) crypto.createHmac("sha256", SECRET).update(`${h}.${p}`).digest("base64url"); // تحقّق: أعِد حسابَ التوقيع + timingSafeEqual + افحص exp. payload مقروءٌ — لا سرَّ فيه. // authenticate → 401 ، authorize(role) → 403 . الحامل: Bearer أو كوكي httpOnly.
React (٨)
jsxconst [v, setV] = useState(init); // حالةٌ تبقى (إغلاق) وتُطلِق رسماً useEffect(() => { fetchData(); }, []); // أثرٌ بعد الرسم ([] = مرّة) fetch(`${import.meta.env.VITE_API_URL}/x`, { headers:{ Authorization:`Bearer ${token}` }}) .then(r=>r.json()).then(env=>setV(env.data)); // المظروف، عبر CORS <input value={v} onChange={e=>setV(e.target.value)} /> // خانةٌ مضبوطة
- UI=f(state). لا تلمس DOM. props تتدفّق لأسفل. Vite: تطويرٌ + بناءٌ ثابت.
الكابستون: التصحيح (١٠)
shdocker run -d --rm --network none --memory 128m --pids-limit 64 --cpus 1 \ --read-only --tmpfs /tmp:rw,size=64m selflab-sandbox sleep infinity # عزلٌ docker cp solution.c CID:/tmp/ ; docker exec CID gcc -Wall -Werror ... # تصريفٌ مرّة echo "input" | docker exec -i CID timeout 2 /tmp/solution # تشغيلٌ لكلِّ حالة docker rm -f CID # في finally
- خرائطُ الخروج:
124→timeout ·≠0→runtime_error · تطابق→pass. التصحيحُ في الـ backend. - التهديد↔البدائيّ: network none·pids-limit·memory·cpu·read-only·non-root·timeout. سجّل بـ
$transaction.
أوامرُ مشروعك السريعة
shcp .env.example .env # ثم عدّل الأسرار docker compose up # بعد إضافة Dockerfiles docker compose --profile build-only build sandbox cd backend && npm run dev # nodemon + tsx npx prisma migrate dev --name <x> ; npx prisma generate
مخطّطُ التدفّق الكامل
ثلاثةُ مسارات. اطبعها وعلّقها — هي خريطتُك حين تدير المشروع.
١) الكتابة: POST /api/v1/auth/register
٢) القراءة: GET /api/v1/me (انعدامُ الحالة)
٣) الكابستون: POST /api/v1/tasks/:taskId/check
التهديد ↔ البدائيّ (الكابستون)
دفاعُ العمق: لو فشلت طبقةٌ، تمسك الأخرى.