العنوان الحالي للمختبر
http://174.138.29.61:18080 http://174.138.29.61:18080/v1 GET http://174.138.29.61:18080/healthz
كل مسارات العمل تحت /v1. الصحة بدون /v1. التطبيقان يُبنيان بـ --dart-define=USE_HTTP=true فيقرآن العنوان من packages/core/lib/src/lab_url.dart.
1. قواعد عامة
- الجسم JSON والترميز UTF-8.
- الرأس:
Content-Type: application/json - بعد الدخول:
Authorization: Bearer <accessToken> - رمز الوصول يعيش 15 دقيقة. جدّده بـ
POST /v1/auth/refreshقبل انتهائه. - المعرّفات أرقام كبيرة تُرسل نصوصاً:
"id": "1" - اعرض
messageArللمستخدم عند الخطأ.
{ "statusCode": 401, "message": "...", "messageAr": "..." }
| الرمز | المعنى |
|---|---|
| 400 | جسم ناقص أو مكرر (بريد/هاتف) |
| 401 | بيانات دخول أو رمز وصول أو OTP |
| 403 | الدور لا يملك المسار |
| 404 | الصف غير موجود |
| 409 | تعارض |
2. الأدوار والتطبيقات
| تطبيق | من يدخل | ماذا يفعل |
|---|---|---|
farmer_app | مزارع فقط | تسجيل، دخول، قراءة، بنك، رمز |
admin_app | مشرف أو مالك | كتابة أسعار/إرشاد/محاصيل، بحث مزارعين، صرف |
إن دخل مزارع لتطبيق الإدارة أو مشرف لتطبيق المزارع، ارفض محلياً برسالة «الدور غير مدعوم»، حتى لو الخادم قبل الرمز.
3. المصادقة
3.1 إنشاء حساب مستخدم (المزارع فقط)
POST /v1/auth/register
{
"fullName": "أحمد علي",
"email": "ahmed@example.com",
"password": "at-least-8",
"phone": "0120000099"
}
لا تُصدر رموزاً فوراً. الاستجابة:
{
"status": "otp_required",
"challengeId": "a1b2...",
"phoneHint": "012***0099",
"expiresIn": 300,
"otpEcho": "482193"
}
otpEcho يظهر في المختبر فقط (OTP_ECHO=true). في الإنتاج لن يُعاد؛ الرمز يُرسل برسالة لاحقاً.
POST /v1/auth/verify-otp
{ "challengeId": "a1b2...", "code": "482193" }
نجاح:
{
"status": "ok",
"accessToken": "...",
"refreshToken": "...",
"expiresIn": 900,
"user": {
"id": "4",
"fullName": "أحمد علي",
"email": "ahmed@example.com",
"phone": "0120000099",
"phoneVerified": true,
"role": "farmer",
"farmerId": "2",
"supportEligible": false
}
}
احفظ accessToken وrefreshToken على الجهاز. لا تُظهرهما في السجلات.
3.2 الدخول بالبريد وكلمة المرور
POST /v1/auth/login
{ "email": "farmer.lab@nidaa.local", "password": "LabBridge#2026" }
إن كان للحساب هاتف (حسابات المختبر كلها كذلك) يعيد otp_required. اعرض شاشة OTP. بعد التأكيد يُعلَّم phoneVerified. إن لم يكن هاتف: status: "ok" مع الرموز مباشرة.
| الدور | البريد | كلمة المرور |
|---|---|---|
| مزارع | farmer.lab@nidaa.local | LabBridge#2026 |
| مشرف | admin.lab@nidaa.local | LabBridge#2026 |
| مالك | owner.lab@nidaa.local | LabBridge#2026 |
3.3 إعادة إرسال الرمز
POST /v1/auth/request-otp
{ "challengeId": "..." }
يعيد challengeId جديداً. أبطل القديم في الواجهة.
3.4 قوقل (الخدمة الرسمية)
زر «الدخول بقوقل» يفتح شاشة قوقل الرسمية عبر google_sign_in ثم يرسل معرّف الهوية:
POST /v1/auth/google
{ "idToken": "<Google ID token>" }
ينشئ مزارعاً إن لم يوجد، أو يربط الحساب إن وُجد نفس البريد. ضع GOOGLE_CLIENT_ID (عميل الويب) في .env وابنِ التطبيق بـ --dart-define=GOOGLE_SERVER_CLIENT_ID=....
حزمة أندرويد: org.nidaa.user — SHA-1: DF:47:F3:4B:56:20:29:9D:A0:11:92:9F:F7:E3:5B:2C:97:3F:78:AD
3.5 فيسبوك (الخدمة الرسمية)
زر «الدخول بفيسبوك» يفتح تسجيل فيسبوك الرسمي:
POST /v1/auth/facebook
{ "accessToken": "<Facebook access token>" }
ضع FACEBOOK_APP_ID وFACEBOOK_APP_SECRET وFACEBOOK_CLIENT_TOKEN في .env وفي farmer_app/android/oauth.properties. إن كان للحساب هاتف غير موثّق بعد الدخول الاجتماعي، الخادم يعيد otp_required.
3.6 التجديد والخروج والجهاز الحالي
POST /v1/auth/refresh { "refreshToken": "..." }
POST /v1/auth/logout { "refreshToken": "..." }
GET /v1/auth/me
عند 401 على أي مسار محمي: امسح الجلسة وأعد شاشة الدخول.
4. تسلسل الشاشات المقترح
[دخول]
بريد + كلمة مرور
أو قوقل / فيسبوك ← تطبيق المزارع فقط
أو «تسجيل مستخدم جديد»
↓
[OTP] رمز من 6 أرقام يؤكد الهاتف
↓
[هيكل التطبيق]
لا تتخطّ OTP إن كانت الاستجابة otp_required. لا تدخل المزارع قبل verify-otp.
5. مسارات المزارع بعد الدخول
رأس Bearer إلزامي.
| الغرض | الطلب |
|---|---|
| الأسعار | GET /v1/crop-prices |
| الإرشاد المنشور | GET /v1/content |
| بحث محتوى | GET /v1/content?q=&type=article&stage=land_prep |
| القوائم | GET /v1/lookups |
| دفعاتي | GET /v1/payments/me |
| حسابي البنكي | GET /v1/bank-accounts/me |
| إرسال بنك | POST /v1/bank-accounts |
| رمزي | GET /v1/qr/my-code |
| إشعارات | GET /v1/notifications |
POST /v1/bank-accounts
{
"bankId": "1",
"accountName": "أحمد علي",
"accountNumber": "1234567890",
"bankakNumber": "2499..."
}
lookups.banks[].id هو bankId. مراحل الزراعة: land_prep planting irrigation pest_control harvest. أنواع المحتوى: article image audio video. المزارع لا يستدعي POST /crops ولا /crop-prices ولا /content ولا GET /farmers — الخادم يعيد 403.
6. مسارات المشرف / المالك
نفس الرأس Bearer بعد OTP.
محاصيل
POST /v1/crops
{ "name": "سمسم", "categoryId": "1" }
PATCH /v1/crops/:id
{ "name": "...", "isActive": true }
categoryId من GET /v1/lookups → categories.
أسعار
POST /v1/crop-prices
{
"cropId": "1",
"marketId": "1",
"unitId": "1",
"price": 1300,
"priceDate": "2026-08-15"
}
PATCH /v1/crop-prices/:id
{ "price": 1320, "notes": "تحديث يومي" }
GET /v1/crop-prices يعيد السعر الحالي والتغير المحسوب (previousPrice, change, changePct). لا ترسل تصنيف عربي في الاستعلام؛ صفِّ في الواجهة أو استخدم رمز التصنيف grains.
إرشاد / معلومات
POST /v1/content
{
"title": "ري القمح",
"body": "...",
"type": "article",
"farmingStageId": "3",
"cropIds": ["1"]
}
PATCH /v1/content/:id/publish
GET /v1/content?includeDrafts=1
PATCH /v1/content/:id
{ "title": "...", "body": "..." }
يُنشأ مسودة ثم يُنشر.
المزارعون وبيانات المستخدم
GET /v1/farmers
GET /v1/farmers?q=أحمد
GET /v1/farmers/:id
POST /v1/farmers
{ "fullName": "...", "email": "...", "phone": "...", "password": "..." }
PATCH /v1/farmers/:id
{ "supportEligible": true, "locationText": "ود مدني" }
GET /v1/farmers/:id/payments
PATCH /v1/payments/:id/status
{ "status": "paid", "receiptNumber": "REC-1" }
PATCH /v1/bank-accounts/:id/review
{ "status": "verified", "notes": "مطابق" }
POST /v1/qr/verify
{ "token": "..." }
تفصيل GET /farmers/:id يتضمن المستخدم والموقع ونوع الزراعة والحسابات والدفعات. تفعيل الأهلية لمزارع بلا دفعات ينشئ خمس دفعات تلقائياً. المشرف والمالك ينشئان مستخدماً من شاشة المزارعين.
المشرفون — المالك فقط
GET /v1/staff
POST /v1/staff
{ "fullName": "...", "email": "...", "password": "...", "phone": "..." }
PATCH /v1/staff/:id
{ "status": "inactive" }
المالك ينشئ مشرفاً من لوحة المالك. المشرف لا ينشئ مشرفاً. الحساب غير النشط يُرفض عند الدخول.
المالك فقط
GET /v1/owner/dashboard GET /v1/reports/overview
المشرف على هذين المسارين يُمنع.
7. مثال تدفق كامل (مزارع جديد)
BASE=http://174.138.29.61:18080/v1
curl -s -X POST $BASE/auth/register -H 'Content-Type: application/json' \
-d '{"fullName":"مزارع جديد","email":"n1@nidaa.local","password":"password1","phone":"0120000091"}'
curl -s -X POST $BASE/auth/verify-otp -H 'Content-Type: application/json' \
-d '{"challengeId":"...","code":"..."}'
TOKEN=...
curl -s $BASE/auth/me -H "Authorization: Bearer $TOKEN"
curl -s $BASE/crop-prices -H "Authorization: Bearer $TOKEN"
curl -s $BASE/content -H "Authorization: Bearer $TOKEN"
مثال مشرف يضيف سعراً بعد OTP:
curl -s -X POST $BASE/auth/login -H 'Content-Type: application/json' \
-d '{"email":"admin.lab@nidaa.local","password":"LabBridge#2026"}'
# ثم verify-otp
curl -s -X POST $BASE/crop-prices -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"cropId":"1","marketId":"1","unitId":"1","price":1400,"priceDate":"2026-08-15"}'
8. ربط فلاتر
الملف: packages/core/lib/src/http_repository.dart
| دالة المستودع | المسار |
|---|---|
login | POST /auth/login |
register | POST /auth/register |
verifyOtp | POST /auth/verify-otp |
resendOtp | POST /auth/request-otp |
loginGoogle | POST /auth/google |
loginFacebook | POST /auth/facebook |
cropPrices | GET /crop-prices |
createCropPrice | POST /crop-prices |
content | GET /content |
createContent | POST /content + نشر |
searchFarmers / getFarmer | GET /farmers و GET /farmers/:id |
createFarmer | POST /farmers — مشرف أو مالك |
listStaff / createAdmin | /staff — مالك فقط |
لا تبنِ عميل HTTP ثانياً في الشاشات. أضف الدالة في BridgeRepository ثم نفّذها في HttpBridgeRepository وFakeBridgeRepository.
cd admin_app # أو farmer_app flutter build apk --release --dart-define=USE_HTTP=true
بدون USE_HTTP=true يعمل الوهم المحلي ولا يصل الخادم.
9. إعداد قوقل وفيسبوك الرسميين
- Google Cloud: عميل أندرويد لحزمة
org.nidaa.userمع SHA-1 أعلاه، وعميل ويب. ضع معرّف عميل الويب فيGOOGLE_CLIENT_IDوابنِ التطبيق بـGOOGLE_SERVER_CLIENT_ID. - Facebook Developers: تطبيق أندرويد بنفس الحزمة. ضع
FACEBOOK_APP_IDوFACEBOOK_CLIENT_TOKENفي.envوoauth.properties. AUTH_OAUTH_DEV=falseبعد التأكد من الرموز الحقيقية.- التطبيق يرسل رمز قوقل/فيسبوك الحقيقي؛ لا حوار بريد محلي.
- إيقاف
OTP_ECHOوربط بوابة SMS لاحقاً دون تغيير شكل المسارات.