يستخدم SnapOtter قاعدة بيانات PostgreSQL 17 مع [Drizzle ORM](https://orm.drizzle.team/) (pg-core / node-postgres) لاستمرارية البيانات. المخطط معرَّف في `apps/api/src/db/schema.ts`.
يُضبَط الاتصال عبر متغير البيئة `DATABASE_URL` (القيمة الافتراضية `postgres://snapotter:snapotter@postgres:5432/snapotter`). في Docker Compose، تخزّن حاوية Postgres بياناتها في وحدة التخزين المسماة `SnapOtter-pgdata`.
## الجداول {#tables}
### users {#users}
يخزّن حسابات المستخدمين. يُنشأ تلقائيًا عند التشغيل الأول من `DEFAULT_USERNAME` و `DEFAULT_PASSWORD`.
| العمود | النوع | ملاحظات |
|---|---|---|
| `id` | uuid | المفتاح الأساسي |
| `username` | varchar | فريد، مطلوب |
| `passwordHash` | varchar | تجزئة scrypt |
| `role` | varchar | `admin`، أو `editor`، أو `user` |
| `mustChangePassword` | boolean | علامة إعادة تعيين كلمة المرور القسرية |
| `createdAt` | timestamp | وقت الإنشاء |
| `updatedAt` | timestamp | وقت آخر تحديث |
### sessions {#sessions}
جلسات تسجيل الدخول النشطة. يربط كل صف رمز جلسة بمستخدم.
| العمود | النوع | ملاحظات |
|---|---|---|
| `id` | varchar | المفتاح الأساسي (رمز الجلسة) |
| `userId` | uuid | مفتاح خارجي إلى `users.id` |
| `expiresAt` | timestamp | وقت انتهاء الصلاحية |
| `createdAt` | timestamp | وقت الإنشاء |
### teams {#teams}
مجموعات لتنظيم المستخدمين. يمكن للمسؤولين إسناد المستخدمين إلى فرق.
| العمود | النوع | الوصف |
|--------|------|-------------|
| `id` | uuid | المفتاح الأساسي |
| `name` | varchar (فريد، بحد أقصى 50 حرفًا) | اسم الفريق |
| `createdAt` | timestamp | وقت الإنشاء |
### api_keys {#api-keys}
مفاتيح API للوصول البرمجي. يُعرَض المفتاح الخام مرة واحدة عند الإنشاء؛ ولا يُخزَّن سوى التجزئة (hash).
| العمود | النوع | ملاحظات |
|---|---|---|
| `id` | uuid | المفتاح الأساسي |
| `userId` | uuid | مفتاح خارجي إلى `users.id` |
| `keyHash` | varchar | تجزئة scrypt للمفتاح |
| `name` | varchar | تسمية يوفّرها المستخدم |
| `createdAt` | timestamp | وقت الإنشاء |
| `lastUsedAt` | timestamp | يُحدَّث عند كل طلب مصادَق عليه |
تُسبَق المفاتيح بـ `si_` متبوعًا بـ 96 حرفًا ست عشريًا (48 بايتًا عشوائيًا).
### pipelines {#pipelines}
سلاسل الأدوات المحفوظة التي ينشئها المستخدمون في الواجهة.
مكتبة ملفات دائمة. يُدرَج التعديل المحفوظ افتراضيًا كصف جذري مستقل ("الحفظ كملف جديد": `version` يساوي 1، و`parentId` يساوي null، بحيث يظل الأصل مدرجًا)، أو كإصدار مرتبط بالأب عند الكتابة فوق الأصل (يُضبَط `parentId`، ويُزاد `version`، ليَحُلّ محل الأصل). يسجّل العمود `toolChain` الأدوات المطبَّقة.
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
```
### الترحيل من 1.x (SQLite) {#migrating-from-1-x-sqlite}
للترقية من SnapOtter 1.x دليل خاص بها: انظر [الترقية من 1.x إلى 2.0](./upgrading). باختصار، أعد استخدام وحدة تخزين `/data` الموجودة لديك، وسيكتشف الإصدار 2.0 تلقائيًا `/data/snapotter.db` ويستوردها عند التشغيل الأول (أو اضبط `SQLITE_MIGRATE_PATH` للإشارة إليها بشكل صريح). انسخ وحدة تخزين `/data` بأكملها احتياطيًا أولًا، وليس `snapotter.db` فقط: يستخدم الإصدار 1.x وضع SQLite WAL، لذا كثيرًا ما تترك الحاوية المتوقفة معظم بياناتها في `snapotter.db-wal` بجوار `snapotter.db` شبه الفارغ.