@qwickapps/server 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +12 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  6. package/dist/plugins/bans/bans-plugin.js +12 -3
  7. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  8. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  9. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  10. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  11. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  12. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  13. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  14. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  15. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  16. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  17. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  18. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  19. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  20. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  21. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  22. package/dist/plugins/devices/adapters/index.js +10 -0
  23. package/dist/plugins/devices/adapters/index.js.map +1 -0
  24. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  25. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  26. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  27. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  28. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  29. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  30. package/dist/plugins/devices/devices-plugin.js +453 -0
  31. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  32. package/dist/plugins/devices/index.d.ts +18 -0
  33. package/dist/plugins/devices/index.d.ts.map +1 -0
  34. package/dist/plugins/devices/index.js +18 -0
  35. package/dist/plugins/devices/index.js.map +1 -0
  36. package/dist/plugins/devices/stores/index.d.ts +9 -0
  37. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  38. package/dist/plugins/devices/stores/index.js +9 -0
  39. package/dist/plugins/devices/stores/index.js.map +1 -0
  40. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  41. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  42. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  43. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  44. package/dist/plugins/devices/token-utils.d.ts +100 -0
  45. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  46. package/dist/plugins/devices/token-utils.js +162 -0
  47. package/dist/plugins/devices/token-utils.js.map +1 -0
  48. package/dist/plugins/devices/types.d.ts +307 -0
  49. package/dist/plugins/devices/types.d.ts.map +1 -0
  50. package/dist/plugins/devices/types.js +10 -0
  51. package/dist/plugins/devices/types.js.map +1 -0
  52. package/dist/plugins/index.d.ts +14 -2
  53. package/dist/plugins/index.d.ts.map +1 -1
  54. package/dist/plugins/index.js +13 -1
  55. package/dist/plugins/index.js.map +1 -1
  56. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  57. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  58. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  59. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  60. package/dist/plugins/notifications/index.d.ts +71 -0
  61. package/dist/plugins/notifications/index.d.ts.map +1 -0
  62. package/dist/plugins/notifications/index.js +72 -0
  63. package/dist/plugins/notifications/index.js.map +1 -0
  64. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  65. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  66. package/dist/plugins/notifications/notifications-manager.js +610 -0
  67. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  68. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  69. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  70. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  71. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  72. package/dist/plugins/notifications/types.d.ts +164 -0
  73. package/dist/plugins/notifications/types.d.ts.map +1 -0
  74. package/dist/plugins/notifications/types.js +9 -0
  75. package/dist/plugins/notifications/types.js.map +1 -0
  76. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  77. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  78. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  79. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  80. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  81. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  82. package/dist/plugins/parental/adapters/index.js +7 -0
  83. package/dist/plugins/parental/adapters/index.js.map +1 -0
  84. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  85. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  86. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  87. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  88. package/dist/plugins/parental/index.d.ts +14 -0
  89. package/dist/plugins/parental/index.d.ts.map +1 -0
  90. package/dist/plugins/parental/index.js +15 -0
  91. package/dist/plugins/parental/index.js.map +1 -0
  92. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  93. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  94. package/dist/plugins/parental/parental-plugin.js +666 -0
  95. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  96. package/dist/plugins/parental/stores/index.d.ts +7 -0
  97. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  98. package/dist/plugins/parental/stores/index.js +7 -0
  99. package/dist/plugins/parental/stores/index.js.map +1 -0
  100. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  101. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  102. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  103. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  104. package/dist/plugins/parental/types.d.ts +154 -0
  105. package/dist/plugins/parental/types.d.ts.map +1 -0
  106. package/dist/plugins/parental/types.js +10 -0
  107. package/dist/plugins/parental/types.js.map +1 -0
  108. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  109. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  110. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  111. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  112. package/dist/plugins/profiles/index.d.ts +12 -0
  113. package/dist/plugins/profiles/index.d.ts.map +1 -0
  114. package/dist/plugins/profiles/index.js +13 -0
  115. package/dist/plugins/profiles/index.js.map +1 -0
  116. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  117. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  118. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  119. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  120. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  121. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  122. package/dist/plugins/profiles/stores/index.js +9 -0
  123. package/dist/plugins/profiles/stores/index.js.map +1 -0
  124. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  125. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  126. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  127. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  128. package/dist/plugins/profiles/types.d.ts +289 -0
  129. package/dist/plugins/profiles/types.d.ts.map +1 -0
  130. package/dist/plugins/profiles/types.js +10 -0
  131. package/dist/plugins/profiles/types.js.map +1 -0
  132. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  133. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  134. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  135. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  136. package/dist/plugins/subscriptions/index.d.ts +12 -0
  137. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  138. package/dist/plugins/subscriptions/index.js +13 -0
  139. package/dist/plugins/subscriptions/index.js.map +1 -0
  140. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  141. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  142. package/dist/plugins/subscriptions/stores/index.js +9 -0
  143. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  144. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  145. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  146. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  147. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  148. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  149. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  150. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  151. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  152. package/dist/plugins/subscriptions/types.d.ts +308 -0
  153. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  154. package/dist/plugins/subscriptions/types.js +10 -0
  155. package/dist/plugins/subscriptions/types.js.map +1 -0
  156. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  157. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  158. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  159. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  160. package/dist/plugins/usage/index.d.ts +12 -0
  161. package/dist/plugins/usage/index.d.ts.map +1 -0
  162. package/dist/plugins/usage/index.js +13 -0
  163. package/dist/plugins/usage/index.js.map +1 -0
  164. package/dist/plugins/usage/stores/index.d.ts +9 -0
  165. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  166. package/dist/plugins/usage/stores/index.js +9 -0
  167. package/dist/plugins/usage/stores/index.js.map +1 -0
  168. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  169. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  170. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  171. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  172. package/dist/plugins/usage/types.d.ts +195 -0
  173. package/dist/plugins/usage/types.d.ts.map +1 -0
  174. package/dist/plugins/usage/types.js +10 -0
  175. package/dist/plugins/usage/types.js.map +1 -0
  176. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  177. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  178. package/dist/plugins/usage/usage-plugin.js +412 -0
  179. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  180. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  181. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  182. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  183. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  184. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  185. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  186. package/dist/plugins/users/index.d.ts +2 -2
  187. package/dist/plugins/users/index.d.ts.map +1 -1
  188. package/dist/plugins/users/index.js +1 -1
  189. package/dist/plugins/users/index.js.map +1 -1
  190. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  191. package/dist/plugins/users/stores/postgres-store.js +76 -0
  192. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  193. package/dist/plugins/users/types.d.ts +74 -6
  194. package/dist/plugins/users/types.d.ts.map +1 -1
  195. package/dist/plugins/users/users-plugin.d.ts +15 -1
  196. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  197. package/dist/plugins/users/users-plugin.js +29 -0
  198. package/dist/plugins/users/users-plugin.js.map +1 -1
  199. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  200. package/dist-ui/assets/index-CynOqPkb.js.map +1 -0
  201. package/dist-ui/index.html +1 -1
  202. package/dist-ui-lib/api/controlPanelApi.d.ts +46 -0
  203. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  204. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  205. package/dist-ui-lib/dashboard/widgets/index.d.ts +1 -0
  206. package/dist-ui-lib/index.js +1822 -1611
  207. package/dist-ui-lib/index.js.map +1 -1
  208. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  209. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  210. package/package.json +1 -1
  211. package/src/index.ts +178 -0
  212. package/src/plugins/bans/bans-plugin.ts +15 -3
  213. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  214. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  215. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  216. package/src/plugins/devices/adapters/index.ts +13 -0
  217. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  218. package/src/plugins/devices/devices-plugin.ts +538 -0
  219. package/src/plugins/devices/index.ts +69 -0
  220. package/src/plugins/devices/stores/index.ts +9 -0
  221. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  222. package/src/plugins/devices/token-utils.ts +213 -0
  223. package/src/plugins/devices/types.ts +351 -0
  224. package/src/plugins/index.ts +218 -0
  225. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  226. package/src/plugins/notifications/index.ts +91 -0
  227. package/src/plugins/notifications/notifications-manager.ts +773 -0
  228. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  229. package/src/plugins/notifications/types.ts +207 -0
  230. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  231. package/src/plugins/parental/adapters/index.ts +8 -0
  232. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  233. package/src/plugins/parental/index.ts +55 -0
  234. package/src/plugins/parental/parental-plugin.ts +759 -0
  235. package/src/plugins/parental/stores/index.ts +7 -0
  236. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  237. package/src/plugins/parental/types.ts +180 -0
  238. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  239. package/src/plugins/profiles/index.ts +49 -0
  240. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  241. package/src/plugins/profiles/stores/index.ts +9 -0
  242. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  243. package/src/plugins/profiles/types.ts +338 -0
  244. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  245. package/src/plugins/subscriptions/index.ts +51 -0
  246. package/src/plugins/subscriptions/stores/index.ts +9 -0
  247. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  248. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  249. package/src/plugins/subscriptions/types.ts +355 -0
  250. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  251. package/src/plugins/usage/index.ts +39 -0
  252. package/src/plugins/usage/stores/index.ts +9 -0
  253. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  254. package/src/plugins/usage/types.ts +222 -0
  255. package/src/plugins/usage/usage-plugin.ts +484 -0
  256. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  257. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  258. package/src/plugins/users/index.ts +6 -0
  259. package/src/plugins/users/stores/postgres-store.ts +104 -0
  260. package/src/plugins/users/types.ts +82 -6
  261. package/src/plugins/users/users-plugin.ts +37 -0
  262. package/ui/src/App.tsx +5 -1
  263. package/ui/src/api/controlPanelApi.ts +103 -6
  264. package/ui/src/components/StatCard.tsx +58 -0
  265. package/ui/src/dashboard/builtInWidgets.tsx +3 -1
  266. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  267. package/ui/src/dashboard/widgets/index.ts +1 -0
  268. package/ui/src/pages/NotificationsPage.tsx +417 -0
  269. package/ui/src/utils/formatters.ts +33 -0
  270. package/dist-ui/assets/index-D7DoZ9rL.js +0 -478
  271. package/dist-ui/assets/index-D7DoZ9rL.js.map +0 -1
@@ -0,0 +1,213 @@
1
+ /**
2
+ * PostgreSQL Usage Store
3
+ *
4
+ * Usage tracking storage implementation using PostgreSQL.
5
+ * Tracks daily and monthly usage counters.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import type {
11
+ UsageStore,
12
+ DailyUsage,
13
+ MonthlyUsage,
14
+ PostgresUsageStoreConfig,
15
+ } from '../types.js';
16
+
17
+ // Pool interface (from pg package)
18
+ interface PgPool {
19
+ query(text: string, values?: unknown[]): Promise<{ rows: unknown[]; rowCount: number | null }>;
20
+ }
21
+
22
+ /**
23
+ * Get current date in YYYY-MM-DD format
24
+ */
25
+ function getCurrentDate(): string {
26
+ return new Date().toISOString().split('T')[0];
27
+ }
28
+
29
+ /**
30
+ * Get current year-month in YYYY-MM format
31
+ */
32
+ function getCurrentYearMonth(): string {
33
+ const now = new Date();
34
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
35
+ }
36
+
37
+ /**
38
+ * Create a PostgreSQL usage store
39
+ */
40
+ export function postgresUsageStore(config: PostgresUsageStoreConfig): UsageStore {
41
+ const {
42
+ pool: poolOrFn,
43
+ dailyTable = 'usage_daily',
44
+ monthlyTable = 'usage_monthly',
45
+ schema = 'public',
46
+ autoCreateTables = true,
47
+ } = config;
48
+
49
+ // Helper to get pool (supports lazy initialization via function)
50
+ const getPool = (): PgPool => {
51
+ const pool = typeof poolOrFn === 'function' ? poolOrFn() : poolOrFn;
52
+ return pool as PgPool;
53
+ };
54
+
55
+ const dailyTableFull = `"${schema}"."${dailyTable}"`;
56
+ const monthlyTableFull = `"${schema}"."${monthlyTable}"`;
57
+
58
+ return {
59
+ name: 'postgres',
60
+
61
+ async initialize(): Promise<void> {
62
+ if (!autoCreateTables) return;
63
+
64
+ // Create usage_daily table
65
+ await getPool().query(`
66
+ CREATE TABLE IF NOT EXISTS ${dailyTableFull} (
67
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
68
+ user_id UUID NOT NULL,
69
+ feature_code VARCHAR(100) NOT NULL,
70
+ date DATE NOT NULL DEFAULT CURRENT_DATE,
71
+ count INTEGER DEFAULT 0,
72
+ metadata JSONB DEFAULT '{}',
73
+ created_at TIMESTAMPTZ DEFAULT NOW(),
74
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
75
+ UNIQUE(user_id, feature_code, date)
76
+ );
77
+
78
+ CREATE INDEX IF NOT EXISTS idx_${dailyTable}_user_date ON ${dailyTableFull}(user_id, date);
79
+ CREATE INDEX IF NOT EXISTS idx_${dailyTable}_feature ON ${dailyTableFull}(feature_code);
80
+ CREATE INDEX IF NOT EXISTS idx_${dailyTable}_date ON ${dailyTableFull}(date);
81
+ `);
82
+
83
+ // Create usage_monthly table
84
+ await getPool().query(`
85
+ CREATE TABLE IF NOT EXISTS ${monthlyTableFull} (
86
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
87
+ user_id UUID NOT NULL,
88
+ feature_code VARCHAR(100) NOT NULL,
89
+ year_month VARCHAR(7) NOT NULL,
90
+ count INTEGER DEFAULT 0,
91
+ metadata JSONB DEFAULT '{}',
92
+ created_at TIMESTAMPTZ DEFAULT NOW(),
93
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
94
+ UNIQUE(user_id, feature_code, year_month)
95
+ );
96
+
97
+ CREATE INDEX IF NOT EXISTS idx_${monthlyTable}_user ON ${monthlyTableFull}(user_id, year_month);
98
+ CREATE INDEX IF NOT EXISTS idx_${monthlyTable}_feature ON ${monthlyTableFull}(feature_code);
99
+ `);
100
+ },
101
+
102
+ async getDailyUsage(userId: string, featureCode: string, date?: string): Promise<DailyUsage | null> {
103
+ const targetDate = date || getCurrentDate();
104
+ const result = await getPool().query(
105
+ `SELECT * FROM ${dailyTableFull}
106
+ WHERE user_id = $1 AND feature_code = $2 AND date = $3`,
107
+ [userId, featureCode, targetDate]
108
+ );
109
+ return (result.rows[0] as DailyUsage) || null;
110
+ },
111
+
112
+ async getMonthlyUsage(userId: string, featureCode: string, yearMonth?: string): Promise<MonthlyUsage | null> {
113
+ const targetMonth = yearMonth || getCurrentYearMonth();
114
+ const result = await getPool().query(
115
+ `SELECT * FROM ${monthlyTableFull}
116
+ WHERE user_id = $1 AND feature_code = $2 AND year_month = $3`,
117
+ [userId, featureCode, targetMonth]
118
+ );
119
+ return (result.rows[0] as MonthlyUsage) || null;
120
+ },
121
+
122
+ async incrementDaily(userId: string, featureCode: string, amount = 1, date?: string): Promise<number> {
123
+ const targetDate = date || getCurrentDate();
124
+ const yearMonth = targetDate.substring(0, 7); // Extract YYYY-MM from YYYY-MM-DD
125
+
126
+ // Increment daily usage (upsert)
127
+ const dailyResult = await getPool().query(
128
+ `INSERT INTO ${dailyTableFull} (user_id, feature_code, date, count)
129
+ VALUES ($1, $2, $3, $4)
130
+ ON CONFLICT (user_id, feature_code, date)
131
+ DO UPDATE SET count = ${dailyTableFull}.count + $4, updated_at = NOW()
132
+ RETURNING count`,
133
+ [userId, featureCode, targetDate, amount]
134
+ );
135
+
136
+ const newCount = (dailyResult.rows[0] as { count: number }).count;
137
+
138
+ // Also increment monthly usage
139
+ await getPool().query(
140
+ `INSERT INTO ${monthlyTableFull} (user_id, feature_code, year_month, count)
141
+ VALUES ($1, $2, $3, $4)
142
+ ON CONFLICT (user_id, feature_code, year_month)
143
+ DO UPDATE SET count = ${monthlyTableFull}.count + $4, updated_at = NOW()`,
144
+ [userId, featureCode, yearMonth, amount]
145
+ );
146
+
147
+ return newCount;
148
+ },
149
+
150
+ async getAllDailyUsage(userId: string, date?: string): Promise<DailyUsage[]> {
151
+ const targetDate = date || getCurrentDate();
152
+ const result = await getPool().query(
153
+ `SELECT * FROM ${dailyTableFull}
154
+ WHERE user_id = $1 AND date = $2
155
+ ORDER BY feature_code`,
156
+ [userId, targetDate]
157
+ );
158
+ return result.rows as DailyUsage[];
159
+ },
160
+
161
+ async getAllMonthlyUsage(userId: string, yearMonth?: string): Promise<MonthlyUsage[]> {
162
+ const targetMonth = yearMonth || getCurrentYearMonth();
163
+ const result = await getPool().query(
164
+ `SELECT * FROM ${monthlyTableFull}
165
+ WHERE user_id = $1 AND year_month = $2
166
+ ORDER BY feature_code`,
167
+ [userId, targetMonth]
168
+ );
169
+ return result.rows as MonthlyUsage[];
170
+ },
171
+
172
+ async resetDailyUsage(userId: string, featureCode: string, date?: string): Promise<void> {
173
+ const targetDate = date || getCurrentDate();
174
+ await getPool().query(
175
+ `UPDATE ${dailyTableFull}
176
+ SET count = 0, updated_at = NOW()
177
+ WHERE user_id = $1 AND feature_code = $2 AND date = $3`,
178
+ [userId, featureCode, targetDate]
179
+ );
180
+ },
181
+
182
+ async cleanupOldDaily(daysToKeep: number): Promise<number> {
183
+ // Calculate cutoff date to avoid SQL interpolation
184
+ const cutoffDate = new Date();
185
+ cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
186
+ const cutoffDateStr = cutoffDate.toISOString().split('T')[0];
187
+
188
+ const result = await getPool().query(
189
+ `DELETE FROM ${dailyTableFull} WHERE date < $1`,
190
+ [cutoffDateStr]
191
+ );
192
+ return result.rowCount ?? 0;
193
+ },
194
+
195
+ async cleanupOldMonthly(monthsToKeep: number): Promise<number> {
196
+ // Calculate the cutoff year-month
197
+ const now = new Date();
198
+ now.setMonth(now.getMonth() - monthsToKeep);
199
+ const cutoffMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
200
+
201
+ const result = await getPool().query(
202
+ `DELETE FROM ${monthlyTableFull}
203
+ WHERE year_month < $1`,
204
+ [cutoffMonth]
205
+ );
206
+ return result.rowCount ?? 0;
207
+ },
208
+
209
+ async shutdown(): Promise<void> {
210
+ // Pool is managed externally, nothing to do here
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Usage Plugin Types
3
+ *
4
+ * Type definitions for usage tracking with daily/monthly counters.
5
+ * Integrates with subscriptions-plugin for limit enforcement.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ /**
11
+ * Daily usage record
12
+ */
13
+ export interface DailyUsage {
14
+ /** Primary key - UUID */
15
+ id: string;
16
+ /** User ID */
17
+ user_id: string;
18
+ /** Feature code (e.g., 'ai_messages', 'vision_calls') */
19
+ feature_code: string;
20
+ /** Date (YYYY-MM-DD) */
21
+ date: string;
22
+ /** Usage count */
23
+ count: number;
24
+ /** Additional metadata */
25
+ metadata: Record<string, unknown>;
26
+ /** When the record was created */
27
+ created_at: Date;
28
+ /** When the record was last updated */
29
+ updated_at: Date;
30
+ }
31
+
32
+ /**
33
+ * Monthly usage record
34
+ */
35
+ export interface MonthlyUsage {
36
+ /** Primary key - UUID */
37
+ id: string;
38
+ /** User ID */
39
+ user_id: string;
40
+ /** Feature code */
41
+ feature_code: string;
42
+ /** Year-month (YYYY-MM) */
43
+ year_month: string;
44
+ /** Usage count */
45
+ count: number;
46
+ /** Additional metadata */
47
+ metadata: Record<string, unknown>;
48
+ /** When the record was created */
49
+ created_at: Date;
50
+ /** When the record was last updated */
51
+ updated_at: Date;
52
+ }
53
+
54
+ /**
55
+ * Usage increment result
56
+ */
57
+ export interface UsageIncrementResult {
58
+ /** Whether the increment was allowed */
59
+ allowed: boolean;
60
+ /** Current count after increment (if allowed) */
61
+ current_count: number;
62
+ /** Limit value (-1 = unlimited) */
63
+ limit: number | null;
64
+ /** Remaining quota */
65
+ remaining: number | null;
66
+ /** When the counter resets */
67
+ resets_at?: Date;
68
+ /** Reason if not allowed */
69
+ reason?: string;
70
+ }
71
+
72
+ /**
73
+ * Usage status for a feature
74
+ */
75
+ export interface UsageStatus {
76
+ /** Feature code */
77
+ feature_code: string;
78
+ /** Current usage count */
79
+ current: number;
80
+ /** Limit value (-1 = unlimited, null = no subscription) */
81
+ limit: number | null;
82
+ /** Remaining quota (-1 = unlimited) */
83
+ remaining: number | null;
84
+ /** When the counter resets */
85
+ resets_at: Date;
86
+ /** Percentage used (0-100, null if unlimited) */
87
+ percentage_used: number | null;
88
+ }
89
+
90
+ /**
91
+ * Usage summary for a user
92
+ */
93
+ export interface UsageSummary {
94
+ /** User ID */
95
+ user_id: string;
96
+ /** Period (daily or monthly) */
97
+ period: 'daily' | 'monthly';
98
+ /** Period date/month */
99
+ period_value: string;
100
+ /** Usage by feature */
101
+ features: UsageStatus[];
102
+ }
103
+
104
+ // ═══════════════════════════════════════════════════════════════════════════
105
+ // Store Interface
106
+ // ═══════════════════════════════════════════════════════════════════════════
107
+
108
+ /**
109
+ * Usage store interface
110
+ */
111
+ export interface UsageStore {
112
+ /** Store name (e.g., 'postgres') */
113
+ name: string;
114
+
115
+ /**
116
+ * Initialize the store (create tables, etc.)
117
+ */
118
+ initialize(): Promise<void>;
119
+
120
+ /**
121
+ * Get daily usage for a user and feature
122
+ */
123
+ getDailyUsage(userId: string, featureCode: string, date?: string): Promise<DailyUsage | null>;
124
+
125
+ /**
126
+ * Get monthly usage for a user and feature
127
+ */
128
+ getMonthlyUsage(userId: string, featureCode: string, yearMonth?: string): Promise<MonthlyUsage | null>;
129
+
130
+ /**
131
+ * Increment daily usage (also updates monthly)
132
+ */
133
+ incrementDaily(userId: string, featureCode: string, amount?: number, date?: string): Promise<number>;
134
+
135
+ /**
136
+ * Get all daily usage for a user on a date
137
+ */
138
+ getAllDailyUsage(userId: string, date?: string): Promise<DailyUsage[]>;
139
+
140
+ /**
141
+ * Get all monthly usage for a user
142
+ */
143
+ getAllMonthlyUsage(userId: string, yearMonth?: string): Promise<MonthlyUsage[]>;
144
+
145
+ /**
146
+ * Reset daily usage for a user and feature
147
+ */
148
+ resetDailyUsage(userId: string, featureCode: string, date?: string): Promise<void>;
149
+
150
+ /**
151
+ * Cleanup old daily records
152
+ */
153
+ cleanupOldDaily(daysToKeep: number): Promise<number>;
154
+
155
+ /**
156
+ * Cleanup old monthly records
157
+ */
158
+ cleanupOldMonthly(monthsToKeep: number): Promise<number>;
159
+
160
+ /**
161
+ * Shutdown the store
162
+ */
163
+ shutdown(): Promise<void>;
164
+ }
165
+
166
+ // ═══════════════════════════════════════════════════════════════════════════
167
+ // Configuration Types
168
+ // ═══════════════════════════════════════════════════════════════════════════
169
+
170
+ /**
171
+ * PostgreSQL usage store configuration
172
+ */
173
+ export interface PostgresUsageStoreConfig {
174
+ /** PostgreSQL pool instance or a function that returns one */
175
+ pool: unknown | (() => unknown);
176
+ /** Daily usage table name (default: 'usage_daily') */
177
+ dailyTable?: string;
178
+ /** Monthly usage table name (default: 'usage_monthly') */
179
+ monthlyTable?: string;
180
+ /** Schema name (default: 'public') */
181
+ schema?: string;
182
+ /** Auto-create tables on init (default: true) */
183
+ autoCreateTables?: boolean;
184
+ }
185
+
186
+ /**
187
+ * API configuration
188
+ */
189
+ export interface UsageApiConfig {
190
+ /** API route prefix (default: '/usage') */
191
+ prefix?: string;
192
+ /** Enable usage endpoints */
193
+ enabled?: boolean;
194
+ }
195
+
196
+ /**
197
+ * Cleanup configuration
198
+ */
199
+ export interface UsageCleanupConfig {
200
+ /** Days to keep daily records (default: 90) */
201
+ dailyRetentionDays?: number;
202
+ /** Months to keep monthly records (default: 24) */
203
+ monthlyRetentionMonths?: number;
204
+ /** Run cleanup on startup */
205
+ runOnStartup?: boolean;
206
+ /** Cleanup interval in hours (0 = disabled) */
207
+ cleanupIntervalHours?: number;
208
+ }
209
+
210
+ /**
211
+ * Usage plugin configuration
212
+ */
213
+ export interface UsagePluginConfig {
214
+ /** Usage storage backend */
215
+ store: UsageStore;
216
+ /** API configuration */
217
+ api?: UsageApiConfig;
218
+ /** Cleanup configuration */
219
+ cleanup?: UsageCleanupConfig;
220
+ /** Enable debug logging */
221
+ debug?: boolean;
222
+ }