Files
cloud-host/scripts/audit-report.fa.html
keyhan 22359be40e fix(platform): apply production hardening from audit plan
Close billing, tenancy, migration, build, and CI/CD gaps identified in the
audit: wallet/gateway guards, full-UUID namespaces, idempotent migrations with
base schema, stateful service stability, safer Dockerfiles/git builds, and
platform chart hardening (Redis auth, RollingUpdate, backups, Swagger off).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 19:35:07 +03:30

318 lines
27 KiB
HTML

<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8" />
<title>گزارش بررسی فنی CloudHost</title>
<style>
@page { size: A4; }
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
font-family: "Vazirmatn", "IRANSans", "Tahoma", "Segoe UI", sans-serif;
color: #1f2933;
line-height: 1.85;
font-size: 12px;
margin: 0;
}
h1, h2, h3 { line-height: 1.5; }
h1 { font-size: 26px; margin: 0 0 4px; color: #0b3d2e; }
.subtitle { color: #52606d; font-size: 13px; margin: 0; }
.meta { color: #7b8794; font-size: 11px; margin-top: 8px; }
h2 {
font-size: 17px; color: #0b3d2e; margin: 26px 0 10px;
border-bottom: 2px solid #d9e2ec; padding-bottom: 6px;
}
h3 { font-size: 14px; color: #243b53; margin: 18px 0 6px; }
.cover {
background: linear-gradient(135deg, #0b3d2e, #1f6f54);
color: #fff; padding: 42px 34px; border-radius: 14px; margin-bottom: 8px;
}
.cover h1 { color: #fff; }
.cover .subtitle { color: #cfe9df; }
.cover .meta { color: #a7d3c4; }
code {
background: #f0f4f8; color: #b91c1c; padding: 1px 5px;
border-radius: 4px; font-family: "SFMono-Regular", Consolas, monospace;
font-size: 10.5px; direction: ltr; unicode-bidi: embed; display: inline-block;
}
.path { color: #334e68; font-family: "SFMono-Regular", Consolas, monospace; font-size: 10.5px; direction: ltr; unicode-bidi: embed; }
ul, ol { margin: 6px 0; padding-inline-start: 22px; }
li { margin: 5px 0; }
.finding { margin: 10px 0; padding: 10px 12px; border-radius: 8px; border: 1px solid #e4e7eb; background: #fafbfc; }
.finding .head { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; }
.badge {
display: inline-block; font-size: 10px; font-weight: 700; padding: 2px 8px;
border-radius: 999px; color: #fff; white-space: nowrap;
}
.bug { background: #b91c1c; }
.sec { background: #7c2d12; }
.risk { background: #b45309; }
.imp { background: #1d4ed8; }
.finding .title { font-weight: 700; color: #102a43; }
.finding .desc { margin: 2px 0 0; }
.tag-legend { display: flex; gap: 10px; flex-wrap: wrap; margin: 10px 0 4px; }
table { border-collapse: collapse; width: 100%; margin: 10px 0; font-size: 11px; }
th, td { border: 1px solid #d9e2ec; padding: 6px 8px; text-align: right; vertical-align: top; }
th { background: #f0f4f8; color: #243b53; }
.prio-num { font-weight: 700; color: #0b3d2e; }
.section { page-break-inside: avoid; }
.pagebreak { page-break-before: always; }
.summary-box { background: #fff7ed; border: 1px solid #fed7aa; border-radius: 10px; padding: 14px 18px; margin: 14px 0; }
.summary-box ol { padding-inline-start: 20px; }
footer { margin-top: 30px; color: #9aa5b1; font-size: 10px; text-align: center; border-top: 1px solid #e4e7eb; padding-top: 8px; }
</style>
</head>
<body>
<div class="cover">
<h1>گزارش بررسی فنی پلتفرم CloudHost</h1>
<p class="subtitle">باگ‌ها، ریسک‌های پروداکشن و موارد بهبود — بیلد، دیتابیس‌ها، GitOps/CI-CD و امنیت اپلیکیشن</p>
<p class="meta">تاریخ: ۲ تیر ۱۴۰۴ (2 Jul 2026) · محدوده: کل مخزن cloud-host</p>
</div>
<div class="tag-legend">
<span class="badge bug">BUG — قطعاً می‌شکند</span>
<span class="badge sec">SECURITY — حفره امنیتی</span>
<span class="badge risk">RISK — احتمال شکست در پروداکشن</span>
<span class="badge imp">IMPROVEMENT — بهبود</span>
</div>
<div class="section">
<h2>خلاصه مدیریتی</h2>
<p>پروژه معماری خوبی دارد اما در وضعیت فعلی <strong>آماده پروداکشن نیست</strong>. چند دسته مشکل بحرانی وجود دارد که یا هم‌اکنون باگ هستند یا حتماً در پروداکشن (به‌ویژه در شبکه ایران) می‌شکنند:</p>
<ol>
<li><strong>باگ‌های قطعی بیلد</strong> — برخی Dockerfileها اصلاً build نمی‌شوند (مثلاً Go).</li>
<li><strong>باگ چرخه دوم آپگرید</strong> — سیستم migration در دومین <code>helm upgrade</code> قطعاً می‌شکند.</li>
<li><strong>حفره‌های امنیتی مالی</strong> — کاربر می‌تواند کیف پول خود را رایگان شارژ کند و بدون پرداخت دیپلوی کند.</li>
<li><strong>وابستگی به Docker Hub</strong> بدون آینه (mirror) برای ایمیج دیتابیس‌ها و base imageها.</li>
<li><strong>چرخش رمز سرویس‌ها</strong> — رمز Redis/RabbitMQ در هر آپگرید عوض می‌شود و اتصال اپ قطع می‌شود.</li>
</ol>
</div>
<div class="section pagebreak">
<h2>۱. فرایند بیلد اپلیکیشن‌ها (Kaniko + Dockerfile هر رانتایم)</h2>
<h3>باگ‌های قطعی</h3>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">Go — سینتکس نامعتبر COPY؛ هر بیلد Go خراب می‌شود</span></div>
<p class="desc"><span class="path">backend/src/build/build.service.ts:1311-1314</span> — دستور <code>COPY ... 2&gt;/dev/null || true</code> از ریدایرکت شل پشتیبانی نمی‌کند؛ Kaniko این خطوط را رد می‌کند و بیلد هر اپ Go شکست می‌خورد.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">Node.js — شکست بیلد نادیده گرفته می‌شود</span></div>
<p class="desc"><span class="path">backend/src/build/build.service.ts:1034</span><code>RUN npm run build || echo "..."</code>؛ اگر بیلد خطا بدهد باز هم ایمیج ساخته می‌شود و اپ خراب دیپلوی می‌شود. کاربر «بیلد موفق» می‌بیند ولی اپ کار نمی‌کند.</p>
</div>
<h3>ریسک‌های جدی</h3>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">Base imageها بدون آینه، از Docker Hub / GCR / MCR</span></div>
<p class="desc">همه رانتایم‌ها (<code>node:</code>, <code>php:</code>, <code>python:</code>, <code>golang:</code>, <code>wordpress:</code>) و ایمیج Kaniko و init pods (<code>alpine:3.19</code>, <code>alpine/git</code>) مستقیم از رجیستری‌های عمومی pull می‌شوند. آینه فقط برای استک لاگینگ تعریف شده (<span class="path">configuration.ts:151</span>). در ایران بیشترین منبع شکست بیلد است.</p>
</div>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">Laravel — نبود اکستنشن‌های ضروری PHP</span></div>
<p class="desc"><span class="path">backend/src/build/build.service.ts:1085</span> — فقط <code>pdo, pdo_mysql, opcache</code> نصب می‌شود؛ <code>mbstring, xml, bcmath, zip, fileinfo, tokenizer</code> که Laravel استاندارد لازم دارد نصب نمی‌شود.</p>
</div>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">Python — پروژه‌های pyproject.toml پشتیبانی نمی‌شوند</span></div>
<p class="desc"><span class="path">backend/src/build/build.service.ts:1413</span> — تشخیص‌دهنده <code>pyproject.toml</code> را Python می‌شناسد ولی Dockerfile فقط <code>requirements.txt</code> نصب می‌کند؛ پروژه‌های Poetry/PDM فقط Flask+gunicorn پیش‌فرض می‌گیرند. اگر install خطا بدهد، fallback خاموش (<code>2&gt;/dev/null ||</code>) اپ اشتباه بالا می‌آورد.</p>
</div>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">حافظه Kaniko فقط ۴Gi و PVC بیلد بدون StorageClass</span></div>
<p class="desc"><span class="path">build.service.ts:588</span> بیلد Next.js/.NET/Composer اغلب بیشتر می‌خواهد → OOMKilled. <span class="path">build.service.ts:775</span> PVC بیلد <code>storageClassName</code> ندارد → در کلاستر بدون SC پیش‌فرض برای همیشه Pending می‌ماند. همچنین <code>npm install --legacy-peer-deps</code> به‌جای <code>npm ci</code> (خط ۱۰۱۸).</p>
</div>
<h3>امنیت بیلد</h3>
<div class="finding">
<div class="head"><span class="badge sec">SECURITY</span><span class="title">توکن Git داخل spec پاد و تزریق دستور از branch</span></div>
<p class="desc"><span class="path">build.service.ts:498-523</span><code>cloneUrl</code> با توکن embed‌شده در command کانتینر → قابل دیدن در <code>kubectl get pod -o yaml</code>، etcd و audit log. همچنین <code>${branch}</code> بدون کوت داخل شل → نامی مثل <code>main; curl evil</code> کد اجرا می‌کند. بدون اعتبارسنجی URL گیت (SSRF به IPهای داخلی کلاستر). خطر Zip slip / zip bomb در استخراج با <code>unzip</code> (خط ۴۶۲) با سقف آپلود ۱۰GiB.</p>
</div>
<h3>پایداری فرایند</h3>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">ری‌استارت backend وسط بیلد → deployment گیر می‌کند</span></div>
<p class="desc"><span class="path">build.service.ts:56</span> — state بیلد در <code>Map</code> حافظه است؛ بعد از ری‌استارت، Job روی کلاستر ادامه می‌دهد ولی deployment در وضعیت <code>BUILDING</code> گیر می‌کند و reconcile نمی‌شود. همچنین دیپلوی هم‌زمان برای یک اپ قفل ندارد و روی همان Helm release رقابت می‌کنند.</p>
</div>
</div>
<div class="section pagebreak">
<h2>۲. پیش‌نمایش و دیپلوی</h2>
<p>پیش‌نمایش با ساخت یک عدد ۷ رقمی پایدار برای هر اپ و host به‌شکل <code>{userPrefix}-{previewNumber}.{previewRootDomain}</code> کار می‌کند.</p>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">با ست‌شدن دامنه اختصاصی، پیش‌نمایش بلافاصله حذف می‌شود</span></div>
<p class="desc"><span class="path">kubernetes.service.ts:291</span> — حتی قبل از تأیید DNS؛ کاربر تا وریفای شدن دامنه هیچ آدرس قابل‌دسترسی ندارد. پیش‌نمایش نیازمند DNS wildcard فعال + cert-manager و مقدار <code>PREVIEW_BASE_DOMAIN</code> است.</p>
</div>
<div class="finding">
<div class="head"><span class="badge risk">RISK</span><span class="title">getPreviewInfo روی هر فراخوانی Service را به NodePort پچ می‌کند</span></div>
<p class="desc"><span class="path">kubernetes.service.ts:2955</span> — عارضه جانبی که ممکن است اپ را ناخواسته روی IP نود باز کند.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">رجیستری per-cluster + fallback بین‌کلاستری → ImagePullBackOff</span></div>
<p class="desc"><span class="path">deployments.service.ts:360</span> — ایمیج روی رجیستری کلاستر A ساخته و push می‌شود، ولی <code>deployWithClusterFallback</code> می‌تواند روی کلاستر B دیپلوی کند که آن ایمیج را ندارد.</p>
</div>
</div>
<div class="section pagebreak">
<h2>۳. دیتابیس‌ها و سرویس‌های اختیاری</h2>
<h3>باگ‌ها</h3>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">رمز Redis و RabbitMQ در هر helm upgrade عوض می‌شود</span></div>
<p class="desc"><span class="path">redis-deployment.yaml:18</span>، <span class="path">rabbitmq-deployment.yaml:19</span><code>randAlphaNum 16</code> بدون <code>lookup</code> هر بار مقدار جدید تولید می‌کند؛ <code>resource-policy: keep</code> فقط جلوی حذف را می‌گیرد نه تغییر. بعد از هر redeploy رمز عوض می‌شود ولی داده PVC رمز قدیمی دارد → قطع اتصال. الگوی درست در چارت پلتفرم (<span class="path">cloudhost-platform/templates/secret.yaml</span>) با <code>lookup</code> موجود است.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">Health probe رِدیس/مونگو بدون احراز هویت</span></div>
<p class="desc"><span class="path">redis-deployment.yaml:80</span><code>redis-cli ping</code> بدون <code>-a</code>؛ با <code>--requirepass</code> جواب NOAUTH → probe رد → CrashLoopBackOff. همین برای probe مونگو بدون credential.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">MongoDB در snapshot و wp-content restore پشتیبانی نمی‌شوند</span></div>
<p class="desc"><span class="path">kubernetes.service.ts:4389</span> — export/restore فقط Postgres و MySQL دارد؛ اپ Mongo dump خراب می‌گیرد. <span class="path">kubernetes.service.ts:4724</span> — restore محتوای wp-content از طریق Secret ذخیره می‌شود که محدودیت ~۱MiB دارد؛ هر wp-content واقعی بزرگ‌تر است → شکست.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">WordPress + PostgreSQL و WordPress بدون دیتابیس مجاز است</span></div>
<p class="desc">ایمیج رسمی وردپرس فقط MySQL/MariaDB را می‌شناسد ولی پلتفرم <code>databaseType: postgresql</code> یا حتی <code>none</code> را می‌پذیرد → سایت بالا نمی‌آید. باید هنگام رانتایم WordPress دیتابیس اجباراً MySQL شود.</p>
</div>
<h3>ریسک‌ها</h3>
<ul>
<li><span class="badge risk">RISK</span> <strong>ایمیج همه سرویس‌ها از Docker Hub</strong> بدون مکانیزم آینه در چارت اپ (<code>postgres:16-alpine</code>, <code>mysql:8.0</code>, ...)؛ override <code>database.image</code> هست ولی backend هرگز آن را ست نمی‌کند.</li>
<li><span class="badge risk">RISK</span> <strong>Deployment + PVC نوع RWO بدون <code>strategy: Recreate</code></strong> برای دیتابیس/Redis/RabbitMQ → در آپگرید ایمیج پاد جدید منتظر ولوم می‌ماند.</li>
<li><span class="badge risk">RISK</span> <strong>fallback تولید رمز DB</strong> (<span class="path">kubernetes.service.ts:333</span>): اگر <code>dbPassword</code> خالی باشد هر دیپلوی رمز جدید می‌سازد و با داده قدیمی PVC ناسازگار می‌شود.</li>
<li><span class="badge risk">RISK</span> <strong>خاموش‌کردن سرویس PVC یتیم جا می‌گذارد</strong> — کاربر آن‌ها را نمی‌بیند ولی هزینه استوریج ادامه دارد.</li>
<li><span class="badge risk">RISK</span> <strong>دسترسی خارجی NodePort — host اشتباه</strong> (<span class="path">kubernetes.service.ts:2691</span>): IP از API server گرفته می‌شود نه worker node؛ رشته اتصال بلااستفاده است. <code>suspend</code> هم گرنت‌های NodePort را باطل نمی‌کند.</li>
</ul>
</div>
<div class="section pagebreak">
<h2>۴. کنترل‌پلین، GitOps و CI/CD</h2>
<h3>باگ‌ها (باید قبل از دیپلوی بعدی رفع شوند)</h3>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">سیستم migration در آپگرید دوم می‌شکند</span></div>
<p class="desc"><span class="path">migrations-job.yaml:50-53</span> — Job همه فایل‌های SQL را در هر اجرا دوباره اجرا می‌کند بدون جدول ردیابی نسخه. <span class="path">001_service_access_grants.sql:2,9</span> از <code>CREATE TYPE</code> بدون گارد استفاده می‌کند → آپگرید دوم: <code>ERROR: type already exists</code> → sync fail.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">migration هوک بعد از دیپلوی backend اجرا می‌شود</span></div>
<p class="desc"><span class="path">migrations-job.yaml:10-11</span><code>post-upgrade</code>؛ backend جدید ممکن است قبل از آماده شدن اسکیما بالا بیاید → CrashLoop. باید <code>pre-upgrade</code> باشد.</p>
</div>
<div class="finding">
<div class="head"><span class="badge bug">BUG</span><span class="title">نبود base schema و نام ستون اشتباه در migration 015</span></div>
<p class="desc">هیچ SQL جدول‌های <code>users</code>/<code>applications</code> را نمی‌سازد؛ روی دیتابیس خالی اولین migration شکست می‌خورد. <span class="path">015_application_product_type.sql:5-6</span> ستون <code>user_id</code> می‌سازد ولی entity آن را <code>userId</code> تعریف کرده (<span class="path">application.entity.ts:150</span>) → ساخت ایندکس fail.</p>
</div>
<h3>رمزهای هاردکد شده در گیت</h3>
<div class="finding">
<div class="head"><span class="badge sec">SECURITY</span><span class="title">رمزهای الستیک‌سرچ در فایل commit‌شده</span></div>
<p class="desc"><span class="path">backend/k8s/logging/elasticsearch-stack.yaml:21-23</span><code>ELASTIC_PASSWORD: "CloudHost2024!Secure"</code> و <code>FLUENTBIT_PASSWORD</code>. باید rotate و از گیت خارج شوند. همین‌ها به‌عنوان default در <span class="path">configuration.ts:148-150</span> هستند و در validate-production بررسی نمی‌شوند.</p>
</div>
<h3>ریسک‌های CI/CD و کنترل‌پلین</h3>
<ul>
<li><span class="badge risk">RISK</span> workflow کامیت‌شده <strong>auth کانیکو به Harbor</strong> و <strong>توکن clone</strong> ندارد (<span class="path">.gitea/workflows/build-deploy.yaml:74</span>) → push/clone شکست می‌خورد؛ اصلاحات در تغییرات uncommit هستند.</li>
<li><span class="badge risk">RISK</span> <strong>تست‌ها در مسیر Gitea اجرا نمی‌شوند</strong> (فقط GitHub Actions) → کد خراب می‌تواند به پروداکشن برسد.</li>
<li><span class="badge risk">RISK</span> ایمیج backend حین بیلد <strong>Helm و kubectl را از اینترنت دانلود می‌کند</strong> (<span class="path">backend/Dockerfile:16-20</span>) بدون پروکسی.</li>
<li><span class="badge risk">RISK</span> <code>git push</code> بدون <code>pull --rebase</code> (workflow:177) → احتمال half-done deploy.</li>
<li><span class="badge risk">RISK</span> postgres/redis پلتفرم در <span class="path">values-abrban.yaml</span> آینه نشده و imagePullSecret ندارند.</li>
<li><span class="badge risk">RISK</span> <code>strategy: Recreate</code> روی backend (<span class="path">backend-deployment.yaml:12</span>) → داون‌تایم کامل API در هر دیپلوی.</li>
<li><span class="badge risk">RISK</span> بدون resource limits در values پروداکشن → ریسک OOM روی k3s تک‌نود؛ Redis پلتفرم بدون <code>requirepass</code>؛ backup پستگرس خاموش.</li>
<li><span class="badge risk">RISK</span> <code>docker compose up --build</code> کامل کار نمی‌کند — backend با <code>NODE_ENV=production</code><code>synchronize:false</code> و بدون migration → جدول‌ها موجود نیست.</li>
</ul>
</div>
<div class="section pagebreak">
<h2>۵. امنیت و کیفیت کد اپلیکیشن</h2>
<h3>حفره‌های امنیتی بحرانی (P0)</h3>
<div class="finding">
<div class="head"><span class="badge sec">SECURITY</span><span class="title">هر کاربر لاگین‌شده می‌تواند کیف پول خود را رایگان شارژ کند</span></div>
<p class="desc"><span class="path">billing-wallet.controller.ts:45-49</span><code>POST /billing/wallet/charge</code> بدون درگاه پرداخت مستقیم <code>chargeWallet</code> را صدا می‌زند → پول رایگان در پروداکشن. همچنین <code>gateway/verify</code> با <code>PAYMENT_GATEWAY_STUB_ENABLED=true</code> مبلغ دلخواه را می‌پذیرد.</p>
</div>
<div class="finding">
<div class="head"><span class="badge sec">SECURITY</span><span class="title">دور زدن بیلینگ در deploy / start / resources</span></div>
<p class="desc"><span class="path">deployments.service.ts:637</span> <code>startDeployment</code> اپ suspend‌شده را بدون بررسی وضعیت/کیف پول resume می‌کند. <code>triggerDeployment</code> (دیپلوی اول) گارد بیلینگ ندارد. <span class="path">applications.controller.ts:375</span> <code>PATCH resources</code> ارتقا را بدون مسیر پرداخت انجام می‌دهد.</p>
</div>
<div class="finding">
<div class="head"><span class="badge sec">SECURITY</span><span class="title">تداخل namespace بین کاربران (۸ کاراکتر اول UUID)</span></div>
<p class="desc"><span class="path">kubernetes.service.ts:2687-2689</span><code>user-${userId.split('-')[0]}</code>؛ دو کاربر با ۸ کاراکتر اول یکسان namespace مشترک و دسترسی به workload/secret همدیگر می‌گیرند. همین مشکل در ایزوله‌سازی لاگ الستیک (<span class="path">elasticsearch.service.ts:676</span>).</p>
</div>
<h3>امنیتی (P1)</h3>
<ul>
<li><span class="badge sec">SECURITY</span> <code>gitToken</code> و <code>dbPassword</code> در پاسخ API برمی‌گردند (<span class="path">application.entity.ts:54,114</span>) — نیاز به <code>@Exclude</code>.</li>
<li><span class="badge risk">RISK</span> عملیات کیف پول بدون transaction/lock (<span class="path">billing.service.ts:210</span>) — کسر هم‌زمان می‌تواند overdraw کند.</li>
<li><span class="badge risk">RISK</span> اسکنر auto-renew idempotent نیست بین رپلیکاها (<span class="path">app-lifecycle.service.ts:39</span>) — دو پاد یک اپ را دوبار شارژ می‌کنند.</li>
<li><span class="badge bug">BUG</span> proration ارتقا همیشه نرخ ساعتی را استفاده می‌کند (<span class="path">billing.service.ts:755</span>) → ارتقای ماهانه/سالانه undercharge یا رایگان.</li>
<li><span class="badge sec">SECURITY</span> توکن‌ها در <code>localStorage</code> (<span class="path">frontend/src/lib/store.ts:43</span>) → در معرض XSS.</li>
<li><span class="badge sec">SECURITY</span> refresh token بدون rotation/ابطال و context جعل هویت روی refresh دوباره اعتبارسنجی نمی‌شود (<span class="path">auth.service.ts:165</span>).</li>
</ul>
<h3>ریسک‌های متوسط</h3>
<ul>
<li><span class="badge risk">RISK</span> OTP با <code>Math.random()</code> به‌جای CSPRNG (<span class="path">verification.service.ts:188</span>) و race در مصرف OTP (خط ۲۲۵).</li>
<li><span class="badge risk">RISK</span> Swagger بی‌قید در پروداکشن باز است (<span class="path">main.ts:53</span>).</li>
<li><span class="badge risk">RISK</span> secretهای پیش‌فرض ضعیف خارج از پروداکشن (<span class="path">configuration.ts:75</span><code>default-jwt-secret</code>).</li>
</ul>
</div>
<div class="section pagebreak">
<h2>اولویت‌بندی برای پروداکشن</h2>
<div class="summary-box">
<h3 style="margin-top:0;">باید قبل از هر دیپلوی پروداکشن رفع شود (بلاکر)</h3>
<ol>
<li>حذف/گیت کردن <code>POST /billing/wallet/charge</code> پشت درگاه پرداخت واقعی.</li>
<li>گارد بیلینگ روی <code>triggerDeployment</code>، <code>startDeployment</code> و <code>PATCH resources</code>.</li>
<li>ساخت namespace از کل UUID، نه ۸ کاراکتر اول (تداخل بین‌مستأجری).</li>
<li>سیستم migration: جدول ردیابی نسخه یا SQL کاملاً idempotent + هوک <code>pre-upgrade</code> + base schema برای نصب تازه.</li>
<li>اصلاح <code>015</code> (<code>user_id</code><code>userId</code>) و گارد <code>duplicate_object</code> برای <code>CREATE TYPE</code> در <code>001</code>.</li>
<li>commit و deploy اصلاحات uncommit شده workflow (توکن Gitea + auth Harbor کانیکو).</li>
<li>rotate کردن رمزهای هاردکد الستیک‌سرچ.</li>
<li>رفع سینتکس <code>COPY</code> در Dockerfile گو و حذف <code>|| echo</code> از بیلد Node.</li>
</ol>
</div>
<table>
<thead><tr><th style="width:60px;">اولویت</th><th>اقدام</th></tr></thead>
<tbody>
<tr><td class="prio-num">۹</td><td>الگوی <code>lookup</code> برای رمز Redis/RabbitMQ (توقف چرخش رمز).</td></tr>
<tr><td class="prio-num">۱۰</td><td>probe رِدیس/مونگو با احراز هویت.</td></tr>
<tr><td class="prio-num">۱۱</td><td>آینه‌کردن base imageهای بیلد + ایمیج دیتابیس‌ها برای شبکه ایران.</td></tr>
<tr><td class="prio-num">۱۲</td><td>transaction/lock روی عملیات کیف پول.</td></tr>
<tr><td class="prio-num">۱۳</td><td><code>strategy: Recreate</code> روی سرویس‌های stateful و <code>RollingUpdate</code> روی backend.</td></tr>
<tr><td class="prio-num">۱۴</td><td>رفع ImagePullBackOff در fallback بین‌کلاستری.</td></tr>
<tr><td class="prio-num">۱۵</td><td>حذف <code>gitToken</code>/<code>dbPassword</code> از پاسخ‌ها با <code>@Exclude</code>.</td></tr>
<tr><td class="prio-num">۱۶</td><td>اعتبارسنجی و کوت <code>gitBranch</code>، انتقال توکن گیت به Secret.</td></tr>
<tr><td class="prio-num">۱۷</td><td>پشتیبانی MongoDB در snapshot، restore وردپرس از PVC به‌جای Secret.</td></tr>
<tr><td class="prio-num">۱۸</td><td>اجبار MySQL برای رانتایم WordPress.</td></tr>
<tr><td class="prio-num">۱۹</td><td>اجرای تست در مسیر Gitea قبل از دیپلوی.</td></tr>
<tr><td class="prio-num">۲۰</td><td>resource limits و backup پستگرس روی کنترل‌پلین.</td></tr>
</tbody>
</table>
</div>
<footer>
گزارش بررسی فنی CloudHost — تولید خودکار · محرمانه
</footer>
</body>
</html>