رفتن به محتوای اصلی

Taghvimam

«تقویمم» یک تقویمِ آفلاین‌اول و چند‌دستگاهه است. کلاینت‌های اندروید و وب هر کدام داده‌هایشان را محلی نگه می‌دارند و یک بک‌اندِ همگام‌سازی، تغییراتشان را دو‌طرفه با هم آشتی می‌کند. سه اصل طراحی‌اش: کار کردنِ قابل‌اتکا در حالت آفلاین، احراز هویتی که هر دستگاه را جدا می‌شناسد، و حل تعارض بدون این‌که داده‌ای گم شود.

اگر تازه به این مستندات رسیده‌اید، بهترین مسیر این است: اول شروع به کار را بخوانید تا پروژه را روی سیستم خودتان بالا بیاورید، بعد سری به معماری سیستم بزنید تا تصویر کلی دستتان بیاید. برای مرجع دقیقِ همهٔ endpoint‌ها هم دو گزینه دارید: مرجع API (یک صفحهٔ Redoc که مستقیم از روی Zod schema‌ها تولید می‌شود) یا Swagger UI اگر می‌خواهید همان‌جا درخواست‌ها را زنده امتحان کنید.

اجزای پروژه

پروژه از سه بخش اصلی تشکیل شده:

بخشتکنولوژی
بک‌اند (apps/api)Express.js + TypeScript، Prisma + PostgreSQL، احراز هویت JWT دو‌توکنی، OTP پیامکی (کاوه‌نگار)، Firebase FCM
وب (apps/calendar-web)React 18 + Vite + Tailwind، TanStack Query، Dexie (IndexedDB)، معماری feature-sliced
contracts (packages/contracts)Zod schema‌ها — منبع واحدِ تمام DTO‌ها و specِ OpenAPI
وضعیت پیاده‌سازی

سرورِ همگام‌سازی ✅ کامل است؛ کلاینت وب ✅ آفلاین‌اولِ کامل. کلاینت اندروید (KMP) ❌ فعلاً فقط تقویمِ محلی — موتورِ همگام‌سازی‌اش هنوز ساخته نشده (جزئیات: کلاینت اندروید).

همگام‌سازی هم یک محور جداگانه است: آفلاین، مبتنی بر یک version سراسری، cursor امضا‌شده، قاعدهٔ آخرین‌نوشته‌برنده (LWW) و ذخیرهٔ تعارض‌ها برای بازیابی بعدی.

همگام‌سازی، در یک نگاه

دستگاه و سرور تغییراتشان را از طریق endpoint‌های /api/sync/* با هم هماهنگ می‌کنند. هر چهار موجودیت — تقویم، رویداد، وظیفه و یادآور — از همین مسیر عبور می‌کنند. هر رکورد یک uid دارد که کلاینت خودش می‌سازد، و یک version که فقط سرور آن را می‌نویسد — یک دنبالهٔ سراسری و تک‌رشته‌ای.

وقتی کلاینت می‌خواهد تغییرات سرور را بگیرد (pull)، این کار با یک cursor امضا‌شده (black-box) انجام می‌شود که برای هر موجودیت، آخرین نسخه‌ای را که کلاینت دیده نگه می‌دارد. سرور پشتِ صحنه برای هر کاربر یک advisory lock می‌گیرد تا تضمین کند هیچ رکوردی وسط یک pull از قلم نیفتد.

وقتی کلاینت می‌خواهد تغییرات محلی‌اش را بفرستد (push)، هر آیتم در یک transaction جداگانه و با قاعدهٔ آخرین‌نوشته‌برنده اعمال می‌شود؛ اگر تعارضی پیش بیاید، نسخهٔ بازنده در جدول SyncConflict ذخیره می‌شود تا بعداً بتوانید آن را بازیابی کنید.

برای مدل کامل و جزئیاتِ ریزتر، spec طراحی همگام‌سازی را ببینید.