SELFLAB الستاك الكامل · ١١ إقليم SYSTEM ONLINE
مراجعخريطتُك حين تدير

الملاحق

شجرةُ المهارات — مكتملةً

الشجرتان نبتتا متوازيتين والتحمتا في الكابستون. كلُّ عقدةٍ متّصلةٌ صراحةً بما تحتها؛ لا شيءَ يطفو معلّقاً. الأرقامُ أقاليم.

المعرفةُ السابقة (مناهجُك) الستاكُ الجديد (هذا المنهج) ┌──────────────────────────────┐ │ الشبكات: TCP/ports/sockets │────────────────▶ (٠) HTTP ───────────────┐ │ "تكلّمت HTTP بأصابعك (nc)" │ التأطير/الأفعال/الرموز/ │ └──────────────────────────────┘ انعدامُ الحالة/REST │ │ │ ┌──────────────────────────────┐ ▼ │ │ أنواعُ TypeScript (منهج سابق) │── مرجعٌ ──▶ (١) JS + TS من عيون C ◀────────┘ │ "الأنواع تُمحى (erasure)" │ اللغة/الإغلاق/erasure التطبيقيّ └──────────────────────────────┘ │ │ ▼ │ ┌──────────────────────────────┐ (٢) Node من C │ │ C: epoll/poll/fds/stack-heap │──────────▶ event loop=reactor │ │ منهج Docker: socket/exec/flags│──┐ await لا يحجب │ │ namespaces/cgroups/overlay │ │ │ │ │ الأمن/non-root/threat-model │ │ ▼ ▼ └──────────────────────────────┘ │ (٣) http→Express ◀──┘ │ السلسلة (middleware) │ │ │ │ ▼ ▼ │ (٦) Zod (٤) العلائقيّ+SQL ◀── منهج بنية الويب │ حارسُ الحدّ الجداول/المفاتيح/ (الـ db كصندوق) │ │ التطبيع/JOIN/ACID/الفهارس │ │ │ │ │ ▼ │ │ (٥) Prisma │ │ ORM/migrate/generate │ │ (يعيد النوعَ المُحيَّ) │ └──────┬───────┘ │ (٧) الهويّة بلا حالة │ bcrypt + JWT من الصفر (HMAC) │ │ │ ▼ │ (٨) React │ UI=f(state)/hooks (إغلاق)/fetch │ │ │ ▼ │ (٩) التدفّقُ الكامل │ بايتةٌ من النقرة للصفّ ورجوعاً │ │ └────────────────▶(١٠) الكابستون: التصحيح C + Docker.sock + child_process + عزلٌ + معاملةٌ ذرّيّة + كلُّ المسار ▲ هنا تلتقي الشجرتان ▲

ثلاثُ سلاسلَ ناظمةٍ تمرّ عبر الشجرة

الحرّاسُ الثلاثة (أين يقف كلٌّ)

الحارسالزمنالموضع في المسارالمرجع
مترجم 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 (٠)

Node (٢)

Express (٣)

ts
app.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}));

SQL / Postgres (٤)

sql
CREATE 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 ...;

Prisma (٥)

ts
prisma.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 دائماً

Zod (٦)

ts
const 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); ...}

الهويّة (٧)

ts
const 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 (٨)

jsx
const [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)} /> // خانةٌ مضبوطة

الكابستون: التصحيح (١٠)

sh
docker 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

أوامرُ مشروعك السريعة

sh
cp .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

React [٨] الشبكة Express/الخادم Postgres ───────── ────── ───────────── ──────── onChange→setState ▼ submit→fetch(POST,JSON) ──HTTP[٠]──▶ epoll يقبل [٢] body:{password,...} TCP ▼ (النوعُ مُحي → نصّ) CORS[٣] helmet→cors→express.json() [٣] (stream→req.body: any) ▼ validate(Zod) [٦] ──فشل──▶ 400 + fieldErrors ──▶ (يعود) ▼ (data موثوقةٌ مكتوبةُ النوع) controller [٣] → service ▼ await bcrypt.hash [٧] (مجمّع libuv [٢]) ▼ prisma.user.create [٥] ──SQL مُعامَل──▶ INSERT...RETURNING ▼ قيود: UNIQUE/NOT NULL/enum [٤] (الحارسُ الأخير) → صفّ ┌──────────────────────────────────────┘ ▼ Prisma: صفّ→User مكتوبُ النوع [٥] مظروف {success,data} بلا password [٠/٥] ┌──── 201 + جسم ◀──── res.json [٢/٠] ──┘ ▼ r.json()→env.data→setState→إعادةُ رسمٍ→DOM الأدنى [٨]

٢) القراءة: GET /api/v1/me (انعدامُ الحالة)

fetch(GET, Authorization: Bearer <jwt>) [٨/٧] ▼ epoll [٢] → helmet→cors→authenticate [٣/٧] ▼ authenticate: فُكّ Bearer → verifyJWT (أعِد HMAC + قارن) [٧] • لا بحثَ جلسةٍ، لا تخزين → الخادمُ عديمُ الحالة [٠] • صالح؟ req.user={sub,role} | وإلّا 401 ▼ (اختياري) prisma.findUnique [٥] → مظروف 200 → بايتات → setState [٨]

٣) الكابستون: POST /api/v1/tasks/:taskId/check

React (واجهةُ التصحيح) [٨]: أرسِل كودَ C (Bearer) ▼ epoll [٢] → helmet→cors→json→authenticate[٧]→authorize→validate[٦] → controller→service [٣] ▼ Prisma [٥]: اجلب المهمّة + حالاتِ الاختبار + الحدود (timeout/memory) ▼ ── الانعطافةُ عن المسار العاديّ ── child_process [٢] + Docker (المنهج السابق): docker run -d (network none/memory/pids/cpu/read-only/non-root) ← عزلٌ، حاويةٌ واحدةٌ حيّة docker cp + exec gcc ← تصريفٌ مرّة (فشل→compile_error) لكلِّ حالة: exec -i | timeout → التقط stdout + exit + زمن ← 124→timeout, ≠0→runtime_error finally: docker rm -f ← لا تسرّب حاوية ▼ الـ backend: قارن المخرجات، احسب النقطة، خرائطِ evaluation_status ▼ prisma.$transaction [٤/٥]: نتيجةُ التسليم + تحديثُ TASK_PROGRESS (ذرّياً) ▼ مظروف {success,data:results} → بايتات → setState → عرضُ النتائج لكلِّ حالة [٨] (أثناء كلِّ هذا: GET /health يردّ فوراً — العملُ الثقيلُ خارجَ خيط الحلقة [٢])

التهديد ↔ البدائيّ (الكابستون)

while(1); → timeout 2 (حدُّ وقت) fork() لانهائيّ → --pids-limit 64 (cgroup: pids) malloc بلا حدّ → --memory 128m (cgroup: memory + OOM) احتكارُ المعالج → --cpus 1 (cgroup: cpu) socket خارجيّ → --network none (net namespace فارغ) كتابة/قراءةُ الـ host → --read-only +tmpfs (mnt ns + OverlayFS) تصعيدٌ لـ root → USER sandbox (non-root) أضعفُ حلقة → docker.sock (مقايضةٌ معروفةٌ — to revisit)

دفاعُ العمق: لو فشلت طبقةٌ، تمسك الأخرى.

الستاك الكامل · من باب الخادم إلى داخله ليش قبل كيف · صُمّم لـ يزيد