db: Appwrite schema (isletmem db, 11 tables, indexes) + SDK helpers
- Created database 'isletmem' with 11 tables via Appwrite MCP: tenant_settings, customers, services, software, customer_software, calendar_events, tasks, finance_entries, invoices, invoice_items, audit_logs - All tables use rowSecurity=true; row-level perms scoped to Appwrite Team (tenant) - 18 indexes (composite on tenantId + queried columns; unique for invoice numbers, tenant_settings) - src/lib/appwrite/schema.ts as TS source of truth (DATABASE_ID, TABLES, row types) - src/lib/appwrite/client.ts (browser SDK) - src/lib/appwrite/server.ts (node-appwrite admin + session clients) - .env.example template, .env.local for dev (gitignored) - typecheck script added
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# Appwrite (self-hosted, v1.9.0)
|
||||
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://db.kovaksoft.com/v1
|
||||
NEXT_PUBLIC_APPWRITE_PROJECT_ID=
|
||||
NEXT_PUBLIC_APPWRITE_DATABASE_ID=isletmem
|
||||
|
||||
# Server-only — Appwrite API key with sufficient scopes
|
||||
# (databases.read/write, tables.read/write, users.read/write, teams.read/write)
|
||||
APPWRITE_API_KEY=
|
||||
|
||||
# App
|
||||
APP_URL=http://localhost:3000
|
||||
@@ -32,6 +32,7 @@ yarn-error.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
+4
-1
@@ -6,7 +6,8 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
@@ -37,6 +38,7 @@
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"appwrite": "^25.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
@@ -44,6 +46,7 @@
|
||||
"lucide-react": "^0.562.0",
|
||||
"next": "16.1.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"node-appwrite": "^24.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "19.2.3",
|
||||
"react-day-picker": "^9.13.0",
|
||||
|
||||
Generated
+75
@@ -92,6 +92,9 @@ importers:
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.21.3
|
||||
version: 8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
appwrite:
|
||||
specifier: ^25.0.0
|
||||
version: 25.0.0
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -113,6 +116,9 @@ importers:
|
||||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
node-appwrite:
|
||||
specifier: ^24.0.0
|
||||
version: 24.0.0
|
||||
postcss:
|
||||
specifier: ^8.5.6
|
||||
version: 8.5.6
|
||||
@@ -395,89 +401,105 @@ packages:
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-ppc64@0.34.5':
|
||||
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-riscv64@0.34.5':
|
||||
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-wasm32@0.34.5':
|
||||
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||
@@ -544,24 +566,28 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-musl@16.1.1':
|
||||
resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-gnu@16.1.1':
|
||||
resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-musl@16.1.1':
|
||||
resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@16.1.1':
|
||||
resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==}
|
||||
@@ -1230,24 +1256,28 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
|
||||
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
|
||||
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
|
||||
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
|
||||
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
|
||||
@@ -1442,41 +1472,49 @@ packages:
|
||||
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
|
||||
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
|
||||
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
|
||||
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
|
||||
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
|
||||
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
|
||||
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
|
||||
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
|
||||
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
|
||||
@@ -1515,6 +1553,10 @@ packages:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
appwrite@25.0.0:
|
||||
resolution: {integrity: sha512-ZoNhS02x4Bfcur8MQCnZvskrSciSKwKlL+FZOmGtLr6g+1FyEyZufEgOaynStOsqHSlmyUywvluWYH5eLda9Pw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
@@ -1584,6 +1626,9 @@ packages:
|
||||
resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
|
||||
hasBin: true
|
||||
|
||||
bignumber.js@9.3.1:
|
||||
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
|
||||
@@ -2245,6 +2290,9 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
json-bigint@1.0.0:
|
||||
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
||||
|
||||
json-buffer@3.0.1:
|
||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||
|
||||
@@ -2316,24 +2364,28 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.2:
|
||||
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.2:
|
||||
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.2:
|
||||
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.2:
|
||||
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
|
||||
@@ -2438,6 +2490,12 @@ packages:
|
||||
sass:
|
||||
optional: true
|
||||
|
||||
node-appwrite@24.0.0:
|
||||
resolution: {integrity: sha512-6oPNGkNHMcBAcu2GmaOTl4uCrabEcin0NzHRlE8lBfsduG+o4MtYjdLoXlA0IdGtlkFFa+qA4Hmkb7Z7By0ySQ==}
|
||||
|
||||
node-fetch-native-with-agent@1.7.2:
|
||||
resolution: {integrity: sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==}
|
||||
|
||||
node-releases@2.0.27:
|
||||
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
||||
|
||||
@@ -4295,6 +4353,10 @@ snapshots:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
appwrite@25.0.0:
|
||||
dependencies:
|
||||
json-bigint: 1.0.0
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
@@ -4386,6 +4448,8 @@ snapshots:
|
||||
|
||||
baseline-browser-mapping@2.9.11: {}
|
||||
|
||||
bignumber.js@9.3.1: {}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
@@ -5192,6 +5256,10 @@ snapshots:
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json-bigint@1.0.0:
|
||||
dependencies:
|
||||
bignumber.js: 9.3.1
|
||||
|
||||
json-buffer@3.0.1: {}
|
||||
|
||||
json-schema-traverse@0.4.1: {}
|
||||
@@ -5353,6 +5421,13 @@ snapshots:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
node-appwrite@24.0.0:
|
||||
dependencies:
|
||||
json-bigint: 1.0.0
|
||||
node-fetch-native-with-agent: 1.7.2
|
||||
|
||||
node-fetch-native-with-agent@1.7.2: {}
|
||||
|
||||
node-releases@2.0.27: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Account, Avatars, Client, Databases, Storage, TablesDB, Teams } from "appwrite";
|
||||
|
||||
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
|
||||
const projectId = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
|
||||
|
||||
if (!endpoint || !projectId) {
|
||||
throw new Error(
|
||||
"Missing NEXT_PUBLIC_APPWRITE_ENDPOINT or NEXT_PUBLIC_APPWRITE_PROJECT_ID. Check .env.local.",
|
||||
);
|
||||
}
|
||||
|
||||
export const client = new Client().setEndpoint(endpoint).setProject(projectId);
|
||||
|
||||
export const account = new Account(client);
|
||||
export const teams = new Teams(client);
|
||||
export const databases = new Databases(client);
|
||||
export const tablesDB = new TablesDB(client);
|
||||
export const storage = new Storage(client);
|
||||
export const avatars = new Avatars(client);
|
||||
@@ -0,0 +1,167 @@
|
||||
import type { Models } from "appwrite";
|
||||
|
||||
export const DATABASE_ID = "isletmem";
|
||||
|
||||
export const TABLES = {
|
||||
tenantSettings: "tenant_settings",
|
||||
customers: "customers",
|
||||
services: "services",
|
||||
software: "software",
|
||||
customerSoftware: "customer_software",
|
||||
calendarEvents: "calendar_events",
|
||||
tasks: "tasks",
|
||||
financeEntries: "finance_entries",
|
||||
invoices: "invoices",
|
||||
invoiceItems: "invoice_items",
|
||||
auditLogs: "audit_logs",
|
||||
} as const;
|
||||
|
||||
export type TableId = (typeof TABLES)[keyof typeof TABLES];
|
||||
|
||||
type Row = Models.Document;
|
||||
|
||||
export type TenantRole = "owner" | "admin" | "member";
|
||||
|
||||
export interface TenantSettings extends Row {
|
||||
tenantId: string;
|
||||
companyName: string;
|
||||
companyTaxId?: string;
|
||||
companyAddress?: string;
|
||||
companyEmail?: string;
|
||||
companyPhone?: string;
|
||||
logo?: string;
|
||||
defaultVatRate?: number;
|
||||
invoicePrefix?: string;
|
||||
invoiceCounter?: number;
|
||||
}
|
||||
|
||||
export type CustomerStatus = "active" | "passive";
|
||||
|
||||
export interface Customer extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
name: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
taxId?: string;
|
||||
address?: string;
|
||||
notes?: string;
|
||||
status?: CustomerStatus;
|
||||
}
|
||||
|
||||
export type BillingPeriod = "monthly" | "yearly" | "onetime";
|
||||
|
||||
export interface Service extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
customerId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
unitPrice: number;
|
||||
recurring?: boolean;
|
||||
billingPeriod?: BillingPeriod;
|
||||
}
|
||||
|
||||
export interface Software extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
name: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
defaultFee?: number;
|
||||
}
|
||||
|
||||
export interface CustomerSoftware extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
customerId: string;
|
||||
softwareId: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
fee?: number;
|
||||
billingPeriod?: BillingPeriod;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface CalendarEvent extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
start: string;
|
||||
end: string;
|
||||
allDay?: boolean;
|
||||
customerId?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export type TaskStatus = "backlog" | "todo" | "in_progress" | "done";
|
||||
export type TaskPriority = "low" | "medium" | "high" | "urgent";
|
||||
|
||||
export interface Task extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
status?: TaskStatus;
|
||||
priority?: TaskPriority;
|
||||
dueDate?: string;
|
||||
assigneeId?: string;
|
||||
customerId?: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
export type FinanceType = "income" | "expense" | "debt" | "receivable";
|
||||
export type PaymentMethod = "cash" | "transfer" | "card" | "check" | "other";
|
||||
|
||||
export interface FinanceEntry extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
type: FinanceType;
|
||||
amount: number;
|
||||
date: string;
|
||||
description?: string;
|
||||
customerId?: string;
|
||||
invoiceId?: string;
|
||||
paymentMethod?: PaymentMethod;
|
||||
}
|
||||
|
||||
export type InvoiceStatus = "draft" | "sent" | "paid" | "overdue" | "cancelled";
|
||||
|
||||
export interface Invoice extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
number: string;
|
||||
customerId: string;
|
||||
issueDate: string;
|
||||
dueDate: string;
|
||||
status?: InvoiceStatus;
|
||||
subtotal?: number;
|
||||
vatTotal?: number;
|
||||
total?: number;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface InvoiceItem extends Row {
|
||||
tenantId: string;
|
||||
createdBy: string;
|
||||
invoiceId: string;
|
||||
description: string;
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
vatRate?: number;
|
||||
lineTotal: number;
|
||||
}
|
||||
|
||||
export type AuditAction = "create" | "update" | "delete";
|
||||
|
||||
export interface AuditLog extends Row {
|
||||
tenantId: string;
|
||||
userId: string;
|
||||
action: AuditAction;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
changes?: string;
|
||||
ipAddress?: string;
|
||||
userAgent?: string;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import "server-only";
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
import {
|
||||
Account,
|
||||
Client,
|
||||
Databases,
|
||||
Storage,
|
||||
TablesDB,
|
||||
Teams,
|
||||
Users,
|
||||
} from "node-appwrite";
|
||||
|
||||
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
|
||||
const projectId = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
|
||||
const apiKey = process.env.APPWRITE_API_KEY;
|
||||
|
||||
if (!endpoint || !projectId) {
|
||||
throw new Error(
|
||||
"Missing NEXT_PUBLIC_APPWRITE_ENDPOINT or NEXT_PUBLIC_APPWRITE_PROJECT_ID. Check .env.local.",
|
||||
);
|
||||
}
|
||||
|
||||
export const APPWRITE_SESSION_COOKIE = "isletmem-session";
|
||||
|
||||
function baseClient() {
|
||||
return new Client().setEndpoint(endpoint!).setProject(projectId!);
|
||||
}
|
||||
|
||||
export function createAdminClient() {
|
||||
if (!apiKey) {
|
||||
throw new Error("Missing APPWRITE_API_KEY. Required for admin operations.");
|
||||
}
|
||||
const client = baseClient().setKey(apiKey);
|
||||
return {
|
||||
client,
|
||||
account: new Account(client),
|
||||
teams: new Teams(client),
|
||||
users: new Users(client),
|
||||
databases: new Databases(client),
|
||||
tablesDB: new TablesDB(client),
|
||||
storage: new Storage(client),
|
||||
};
|
||||
}
|
||||
|
||||
export async function createSessionClient() {
|
||||
const session = (await cookies()).get(APPWRITE_SESSION_COOKIE);
|
||||
if (!session?.value) {
|
||||
throw new Error("No active session.");
|
||||
}
|
||||
const client = baseClient().setSession(session.value);
|
||||
return {
|
||||
client,
|
||||
account: new Account(client),
|
||||
teams: new Teams(client),
|
||||
databases: new Databases(client),
|
||||
tablesDB: new TablesDB(client),
|
||||
storage: new Storage(client),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCurrentUser() {
|
||||
try {
|
||||
const { account } = await createSessionClient();
|
||||
return await account.get();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user