Files
hamkadr/src/JobsMedical.Web/Pages/Admin/Settings.cshtml
T
soroush.asadi 17d38431bf
CI/CD / CI · dotnet build (push) Successful in 31s
CI/CD / Deploy · hamkadr (push) Successful in 56s
Add SEO sitemap/robots + real SMS OTP (Kavenegar, admin-configured)
- /sitemap.xml (static pages + open shifts + fresh jobs, respecting expiry) + /robots.txt (blocks /Admin,/Employer); base URL from forwarded request → https://hamkadr.ir in prod
- ISmsSender + KavenegarSmsSender (verify/lookup template, sms/send fallback); SMS settings (enabled/apikey/template/sender) in /Admin/Settings; OtpService.IssueAsync sends SMS and stops revealing the code when enabled (dev still shows it); migration

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:27:21 +03:30

140 lines
9.2 KiB
Plaintext

@page
@model JobsMedical.Web.Pages.Admin.SettingsModel
@{
ViewData["Title"] = "تنظیمات جمع‌آوری و هوش مصنوعی";
}
<div class="page-head">
<div class="container">
<h1>تنظیمات جمع‌آوری و هوش مصنوعی</h1>
<p class="muted"><a asp-page="/Admin/Index">← بازگشت به صف</a></p>
</div>
</div>
<div class="container section" style="max-width:680px;">
@if (Model.Saved is not null)
{
<div class="alert alert-success">✓ @Model.Saved</div>
}
<form method="post" class="card card-pad">
<h3 style="margin-top:0;">حالت انتشار</h3>
<div class="filter-group">
<label>نحوه افزودن آگهی‌ها به سایت</label>
<select name="Mode">
<option value="0" selected="@(Model.Mode == JobsMedical.Web.Models.IngestionMode.Manual)">دستی — همه به صف بررسی می‌روند</option>
<option value="1" selected="@(Model.Mode == JobsMedical.Web.Models.IngestionMode.Automatic)">خودکار — موارد تأییدشده مستقیم منتشر می‌شوند</option>
</select>
</div>
<div class="filter-group">
<label>حداقل درصد اطمینان برای انتشار خودکار (بدون هوش مصنوعی)</label>
<input type="number" name="AutoPublishMinConfidence" min="0" max="100" value="@Model.AutoPublishMinConfidence" dir="ltr" />
<p class="muted" style="font-size:12px; margin:4px 0 0;">در حالت خودکار و بدون AI، آگهی‌هایی با اطمینان بالاتر از این مقدار خودکار منتشر می‌شوند.</p>
</div>
<hr style="border:none; border-top:1px solid var(--line); margin:18px 0;" />
<h3 style="margin-top:0;">لایه هوش مصنوعی (اختیاری)</h3>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="AiEnabled" value="true" style="width:auto;" checked="@Model.AiEnabled" />
فعال‌سازی بررسی با هوش مصنوعی قبل از انتشار
</label>
<p class="muted" style="font-size:12px; margin:4px 0 0;">در صورت فعال بودن، هر آگهی پیش از انتشار توسط مدل بررسی و تأیید/رد/ساختارمند می‌شود.</p>
</div>
<div class="filter-group">
<label>آدرس سرویس (سازگار با OpenAI)</label>
<input type="text" name="AiEndpoint" value="@Model.AiEndpoint" placeholder="https://host/v1/chat/completions" dir="ltr" />
<p class="muted" style="font-size:12px; margin:4px 0 0;">می‌تواند یک مدل self-hosted یا سرویس داخلی باشد (OpenAI/Anthropic در ایران مسدودند).</p>
</div>
<div class="filter-group" style="display:flex; gap:8px;">
<div style="flex:1;"><label>کلید API</label><input type="password" name="AiApiKey" value="@Model.AiApiKey" dir="ltr" /></div>
<div style="flex:1;"><label>نام مدل</label><input type="text" name="AiModel" value="@Model.AiModel" dir="ltr" /></div>
</div>
<div class="filter-group">
<label>دستور و چارچوب هوش مصنوعی (Prompt / Framework)</label>
<textarea name="AiSystemPrompt" rows="10" dir="rtl">@Model.AiSystemPrompt</textarea>
<p class="muted" style="font-size:12px; margin:4px 0 0;">به مدل بگو چطور تأیید/رد کند و چه فیلدهایی را استخراج کند. خروجی باید JSON باشد.</p>
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="AiAutoApprove" value="true" style="width:auto;" checked="@Model.AiAutoApprove" />
در حالت خودکار، آگهی‌هایی که AI تأیید می‌کند مستقیم منتشر شوند
</label>
</div>
<hr style="border:none; border-top:1px solid var(--line); margin:18px 0;" />
<h3 style="margin-top:0;">منابع جمع‌آوری (اسکرپ کانال‌ها)</h3>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="AutoIngestEnabled" value="true" style="width:auto;" checked="@Model.AutoIngestEnabled" />
اجرای خودکار جمع‌آوری روی زمان‌بند
</label>
</div>
<div class="filter-group">
<label>فاصله اجرای خودکار (دقیقه)</label>
<input type="number" name="IngestIntervalMinutes" min="1" value="@Model.IngestIntervalMinutes" dir="ltr" />
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="TelegramEnabled" value="true" style="width:auto;" checked="@Model.TelegramEnabled" />
تلگرام (کانال‌های عمومی — بدون توکن)
</label>
<label style="margin-top:6px;">یوزرنیم کانال‌ها (هر خط یک کانال)</label>
<textarea name="TelegramChannels" rows="3" dir="ltr" placeholder="shift_channel&#10;another_channel">@Model.TelegramChannels</textarea>
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="BaleEnabled" value="true" style="width:auto;" checked="@Model.BaleEnabled" />
بله (بات باید عضو کانال باشد)
</label>
<label style="margin-top:6px;">توکن بات بله</label>
<input type="password" name="BaleBotToken" value="@Model.BaleBotToken" dir="ltr" />
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="DivarEnabled" value="true" style="width:auto;" checked="@Model.DivarEnabled" />
دیوار
</label>
<div style="display:flex; gap:8px; margin-top:6px;">
<div style="flex:0 0 120px;"><label>شهر (slug)</label><input type="text" name="DivarCity" value="@Model.DivarCity" dir="ltr" placeholder="tehran" /></div>
<div style="flex:1;"><label>عبارت‌های جستجو (هر خط یکی)</label><textarea name="DivarQueries" rows="3">@Model.DivarQueries</textarea></div>
</div>
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="MedjobsEnabled" value="true" style="width:auto;" checked="@Model.MedjobsEnabled" />
مدجابز (medjobs.ir) — خواندن کامل آگهی‌ها از سایت‌مپ
</label>
<label style="margin-top:6px;">حداکثر آگهی در هر اجرا</label>
<input type="number" name="MedjobsMaxAds" min="1" max="500" value="@Model.MedjobsMaxAds" dir="ltr" />
<p class="muted" style="font-size:12px; margin:4px 0 0;">آگهی‌های تکراری به‌صورت خودکار رد می‌شوند؛ هر اجرا فقط آگهی‌های جدید را می‌آورد.</p>
</div>
<hr style="border:none; border-top:1px solid var(--line); margin:18px 0;" />
<h3 style="margin-top:0;">پیامک ورود (OTP) — کاوه‌نگار</h3>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:700;">
<input type="checkbox" name="SmsEnabled" value="true" style="width:auto;" checked="@Model.SmsEnabled" />
ارسال کد ورود با پیامک (در صورت خاموش بودن، کد روی صفحه نمایش داده می‌شود)
</label>
</div>
<div class="filter-group">
<label>کلید API کاوه‌نگار</label>
<input type="password" name="SmsApiKey" value="@Model.SmsApiKey" dir="ltr" />
</div>
<div class="filter-group" style="display:flex; gap:8px;">
<div style="flex:1;"><label>نام تمپلیت verify (ترجیحی)</label><input type="text" name="SmsTemplate" value="@Model.SmsTemplate" dir="ltr" placeholder="otp" /></div>
<div style="flex:1;"><label>خط ارسال (در نبود تمپلیت)</label><input type="text" name="SmsSender" value="@Model.SmsSender" dir="ltr" placeholder="10008..." /></div>
</div>
<p class="muted" style="font-size:12px;">روش پیشنهادی: تمپلیت verify/lookup با متغیر %token. اگر تمپلیت خالی باشد، پیامک ساده با خط ارسال فرستاده می‌شود.</p>
<button type="submit" class="btn btn-accent btn-block btn-lg">ذخیره تنظیمات</button>
</form>
</div>