@aphexcms/cms-core 0.1.10 → 0.1.11

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 (351) hide show
  1. package/package.json +2 -2
  2. package/src/lib/api/assets.ts +75 -0
  3. package/src/lib/api/client.ts +150 -0
  4. package/src/lib/api/documents.ts +102 -0
  5. package/src/lib/api/index.ts +7 -0
  6. package/src/lib/api/organizations.ts +154 -0
  7. package/src/lib/api/types.ts +34 -0
  8. package/src/lib/auth/auth-errors.ts +23 -0
  9. package/src/lib/auth/auth-hooks.ts +132 -0
  10. package/src/lib/auth/provider.ts +25 -0
  11. package/{dist/client/index.js → src/lib/client/index.ts} +18 -7
  12. package/{dist → src/lib}/components/AdminApp.svelte +43 -11
  13. package/{dist/components/index.js → src/lib/components/index.ts} +5 -1
  14. package/src/lib/config.ts +18 -0
  15. package/{dist/db/adapters/index.js → src/lib/db/adapters/index.ts} +0 -1
  16. package/{dist/db/index.js → src/lib/db/index.ts} +2 -1
  17. package/src/lib/db/interfaces/asset.ts +61 -0
  18. package/src/lib/db/interfaces/document.ts +53 -0
  19. package/src/lib/db/interfaces/index.ts +98 -0
  20. package/src/lib/db/interfaces/organization.ts +51 -0
  21. package/src/lib/db/interfaces/schema.ts +13 -0
  22. package/src/lib/db/interfaces/user.ts +16 -0
  23. package/src/lib/db/utils/reference-resolver.ts +119 -0
  24. package/src/lib/define.ts +7 -0
  25. package/{dist/email/index.js → src/lib/email/index.ts} +2 -1
  26. package/src/lib/email/interfaces/email.ts +45 -0
  27. package/src/lib/engine.ts +85 -0
  28. package/src/lib/field-validation/rule.ts +287 -0
  29. package/src/lib/field-validation/utils.ts +91 -0
  30. package/src/lib/hooks.ts +142 -0
  31. package/{dist/index.js → src/lib/index.ts} +2 -1
  32. package/{dist/is-mobile.svelte.js → src/lib/is-mobile.svelte.ts} +5 -3
  33. package/src/lib/routes/assets-by-id.ts +161 -0
  34. package/src/lib/routes/assets-cdn.ts +185 -0
  35. package/src/lib/routes/assets.ts +116 -0
  36. package/src/lib/routes/documents-by-id.ts +188 -0
  37. package/src/lib/routes/documents-publish.ts +211 -0
  38. package/src/lib/routes/documents.ts +172 -0
  39. package/src/lib/routes/index.ts +13 -0
  40. package/src/lib/routes/organizations-by-id.ts +258 -0
  41. package/src/lib/routes/organizations-invitations.ts +183 -0
  42. package/src/lib/routes/organizations-members.ts +301 -0
  43. package/src/lib/routes/organizations-switch.ts +74 -0
  44. package/src/lib/routes/organizations.ts +147 -0
  45. package/src/lib/routes/schemas-by-type.ts +35 -0
  46. package/src/lib/routes/schemas.ts +19 -0
  47. package/src/lib/routes-exports.ts +42 -0
  48. package/src/lib/schema-context.svelte.ts +24 -0
  49. package/src/lib/schema-utils/cleanup.ts +116 -0
  50. package/src/lib/schema-utils/index.ts +4 -0
  51. package/src/lib/schema-utils/utils.ts +47 -0
  52. package/src/lib/schema-utils/validator.ts +58 -0
  53. package/src/lib/server/index.ts +40 -0
  54. package/src/lib/services/asset-service.ts +256 -0
  55. package/src/lib/services/index.ts +6 -0
  56. package/src/lib/storage/adapters/index.ts +2 -0
  57. package/src/lib/storage/adapters/local-storage-adapter.ts +215 -0
  58. package/{dist/storage/index.js → src/lib/storage/index.ts} +4 -2
  59. package/src/lib/storage/interfaces/index.ts +2 -0
  60. package/src/lib/storage/interfaces/storage.ts +114 -0
  61. package/src/lib/storage/providers/storage.ts +83 -0
  62. package/src/lib/types/asset.ts +81 -0
  63. package/src/lib/types/auth.ts +80 -0
  64. package/src/lib/types/config.ts +45 -0
  65. package/src/lib/types/document.ts +38 -0
  66. package/src/lib/types/index.ts +8 -0
  67. package/src/lib/types/organization.ts +119 -0
  68. package/src/lib/types/schemas.ts +156 -0
  69. package/src/lib/types/sidebar.ts +37 -0
  70. package/src/lib/types/user.ts +17 -0
  71. package/src/lib/utils/content-hash.ts +75 -0
  72. package/src/lib/utils/image-url.ts +204 -0
  73. package/src/lib/utils/index.ts +12 -0
  74. package/src/lib/utils/slug.ts +33 -0
  75. package/src/lib/utils.ts +13 -0
  76. package/dist/api/assets.d.ts +0 -48
  77. package/dist/api/assets.d.ts.map +0 -1
  78. package/dist/api/assets.js +0 -52
  79. package/dist/api/client.d.ts +0 -37
  80. package/dist/api/client.d.ts.map +0 -1
  81. package/dist/api/client.js +0 -125
  82. package/dist/api/documents.d.ts +0 -56
  83. package/dist/api/documents.d.ts.map +0 -1
  84. package/dist/api/documents.js +0 -77
  85. package/dist/api/index.d.ts +0 -7
  86. package/dist/api/index.d.ts.map +0 -1
  87. package/dist/api/index.js +0 -5
  88. package/dist/api/organizations.d.ts +0 -101
  89. package/dist/api/organizations.d.ts.map +0 -1
  90. package/dist/api/organizations.js +0 -92
  91. package/dist/api/types.d.ts +0 -23
  92. package/dist/api/types.d.ts.map +0 -1
  93. package/dist/api/types.js +0 -1
  94. package/dist/auth/auth-errors.d.ts +0 -7
  95. package/dist/auth/auth-errors.d.ts.map +0 -1
  96. package/dist/auth/auth-errors.js +0 -13
  97. package/dist/auth/auth-hooks.d.ts +0 -6
  98. package/dist/auth/auth-hooks.d.ts.map +0 -1
  99. package/dist/auth/auth-hooks.js +0 -108
  100. package/dist/auth/provider.d.ts +0 -17
  101. package/dist/auth/provider.d.ts.map +0 -1
  102. package/dist/auth/provider.js +0 -1
  103. package/dist/client/index.d.ts +0 -24
  104. package/dist/client/index.d.ts.map +0 -1
  105. package/dist/components/AdminApp.svelte.d.ts +0 -24
  106. package/dist/components/AdminApp.svelte.d.ts.map +0 -1
  107. package/dist/components/admin/AdminLayout.svelte.d.ts +0 -15
  108. package/dist/components/admin/AdminLayout.svelte.d.ts.map +0 -1
  109. package/dist/components/admin/DocumentEditor.svelte.d.ts +0 -18
  110. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +0 -1
  111. package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
  112. package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +0 -1
  113. package/dist/components/admin/ObjectModal.svelte.d.ts +0 -15
  114. package/dist/components/admin/ObjectModal.svelte.d.ts.map +0 -1
  115. package/dist/components/admin/SchemaField.svelte.d.ts +0 -19
  116. package/dist/components/admin/SchemaField.svelte.d.ts.map +0 -1
  117. package/dist/components/admin/fields/ArrayField.svelte.d.ts +0 -12
  118. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +0 -1
  119. package/dist/components/admin/fields/BooleanField.svelte.d.ts +0 -13
  120. package/dist/components/admin/fields/BooleanField.svelte.d.ts.map +0 -1
  121. package/dist/components/admin/fields/ImageField.svelte.d.ts +0 -15
  122. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +0 -1
  123. package/dist/components/admin/fields/NumberField.svelte.d.ts +0 -14
  124. package/dist/components/admin/fields/NumberField.svelte.d.ts.map +0 -1
  125. package/dist/components/admin/fields/ReferenceField.svelte.d.ts +0 -12
  126. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +0 -1
  127. package/dist/components/admin/fields/SlugField.svelte.d.ts +0 -15
  128. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +0 -1
  129. package/dist/components/admin/fields/StringField.svelte.d.ts +0 -14
  130. package/dist/components/admin/fields/StringField.svelte.d.ts.map +0 -1
  131. package/dist/components/admin/fields/TextareaField.svelte.d.ts +0 -14
  132. package/dist/components/admin/fields/TextareaField.svelte.d.ts.map +0 -1
  133. package/dist/components/fields/index.d.ts +0 -9
  134. package/dist/components/fields/index.d.ts.map +0 -1
  135. package/dist/components/index.d.ts +0 -7
  136. package/dist/components/index.d.ts.map +0 -1
  137. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts +0 -11
  138. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts.map +0 -1
  139. package/dist/components/layout/Sidebar.svelte.d.ts +0 -14
  140. package/dist/components/layout/Sidebar.svelte.d.ts.map +0 -1
  141. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +0 -4
  142. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +0 -1
  143. package/dist/components/layout/sidebar/NavMain.svelte.d.ts +0 -19
  144. package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +0 -1
  145. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts +0 -9
  146. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts.map +0 -1
  147. package/dist/components/layout/sidebar/NavUser.svelte.d.ts +0 -9
  148. package/dist/components/layout/sidebar/NavUser.svelte.d.ts.map +0 -1
  149. package/dist/config.d.ts +0 -3
  150. package/dist/config.d.ts.map +0 -1
  151. package/dist/config.js +0 -15
  152. package/dist/db/adapters/index.d.ts +0 -1
  153. package/dist/db/adapters/index.d.ts.map +0 -1
  154. package/dist/db/index.d.ts +0 -2
  155. package/dist/db/index.d.ts.map +0 -1
  156. package/dist/db/interfaces/asset.d.ts +0 -51
  157. package/dist/db/interfaces/asset.d.ts.map +0 -1
  158. package/dist/db/interfaces/asset.js +0 -1
  159. package/dist/db/interfaces/document.d.ts +0 -36
  160. package/dist/db/interfaces/document.d.ts.map +0 -1
  161. package/dist/db/interfaces/document.js +0 -1
  162. package/dist/db/interfaces/index.d.ts +0 -73
  163. package/dist/db/interfaces/index.d.ts.map +0 -1
  164. package/dist/db/interfaces/index.js +0 -1
  165. package/dist/db/interfaces/organization.d.ts +0 -27
  166. package/dist/db/interfaces/organization.d.ts.map +0 -1
  167. package/dist/db/interfaces/organization.js +0 -1
  168. package/dist/db/interfaces/schema.d.ts +0 -21
  169. package/dist/db/interfaces/schema.d.ts.map +0 -1
  170. package/dist/db/interfaces/schema.js +0 -1
  171. package/dist/db/interfaces/user.d.ts +0 -15
  172. package/dist/db/interfaces/user.d.ts.map +0 -1
  173. package/dist/db/interfaces/user.js +0 -1
  174. package/dist/db/utils/reference-resolver.d.ts +0 -18
  175. package/dist/db/utils/reference-resolver.d.ts.map +0 -1
  176. package/dist/db/utils/reference-resolver.js +0 -80
  177. package/dist/define.d.ts +0 -3
  178. package/dist/define.d.ts.map +0 -1
  179. package/dist/define.js +0 -4
  180. package/dist/email/index.d.ts +0 -2
  181. package/dist/email/index.d.ts.map +0 -1
  182. package/dist/email/interfaces/email.d.ts +0 -42
  183. package/dist/email/interfaces/email.d.ts.map +0 -1
  184. package/dist/email/interfaces/email.js +0 -1
  185. package/dist/engine.d.ts +0 -26
  186. package/dist/engine.d.ts.map +0 -1
  187. package/dist/engine.js +0 -66
  188. package/dist/field-validation/rule.d.ts +0 -51
  189. package/dist/field-validation/rule.d.ts.map +0 -1
  190. package/dist/field-validation/rule.js +0 -221
  191. package/dist/field-validation/utils.d.ts +0 -21
  192. package/dist/field-validation/utils.d.ts.map +0 -1
  193. package/dist/field-validation/utils.js +0 -66
  194. package/dist/hooks.d.ts +0 -23
  195. package/dist/hooks.d.ts.map +0 -1
  196. package/dist/hooks.js +0 -96
  197. package/dist/index.d.ts +0 -2
  198. package/dist/index.d.ts.map +0 -1
  199. package/dist/is-mobile.svelte.d.ts +0 -5
  200. package/dist/is-mobile.svelte.d.ts.map +0 -1
  201. package/dist/routes/assets-by-id.d.ts +0 -5
  202. package/dist/routes/assets-by-id.d.ts.map +0 -1
  203. package/dist/routes/assets-by-id.js +0 -138
  204. package/dist/routes/assets-cdn.d.ts +0 -3
  205. package/dist/routes/assets-cdn.d.ts.map +0 -1
  206. package/dist/routes/assets-cdn.js +0 -155
  207. package/dist/routes/assets.d.ts +0 -4
  208. package/dist/routes/assets.d.ts.map +0 -1
  209. package/dist/routes/assets.js +0 -94
  210. package/dist/routes/documents-by-id.d.ts +0 -5
  211. package/dist/routes/documents-by-id.d.ts.map +0 -1
  212. package/dist/routes/documents-by-id.js +0 -142
  213. package/dist/routes/documents-publish.d.ts +0 -4
  214. package/dist/routes/documents-publish.d.ts.map +0 -1
  215. package/dist/routes/documents-publish.js +0 -151
  216. package/dist/routes/documents.d.ts +0 -4
  217. package/dist/routes/documents.d.ts.map +0 -1
  218. package/dist/routes/documents.js +0 -131
  219. package/dist/routes/index.d.ts +0 -6
  220. package/dist/routes/index.d.ts.map +0 -1
  221. package/dist/routes/index.js +0 -10
  222. package/dist/routes/organizations-by-id.d.ts +0 -5
  223. package/dist/routes/organizations-by-id.d.ts.map +0 -1
  224. package/dist/routes/organizations-by-id.js +0 -187
  225. package/dist/routes/organizations-invitations.d.ts +0 -4
  226. package/dist/routes/organizations-invitations.d.ts.map +0 -1
  227. package/dist/routes/organizations-invitations.js +0 -125
  228. package/dist/routes/organizations-members.d.ts +0 -5
  229. package/dist/routes/organizations-members.d.ts.map +0 -1
  230. package/dist/routes/organizations-members.js +0 -206
  231. package/dist/routes/organizations-switch.d.ts +0 -3
  232. package/dist/routes/organizations-switch.d.ts.map +0 -1
  233. package/dist/routes/organizations-switch.js +0 -53
  234. package/dist/routes/organizations.d.ts +0 -4
  235. package/dist/routes/organizations.d.ts.map +0 -1
  236. package/dist/routes/organizations.js +0 -109
  237. package/dist/routes/schemas-by-type.d.ts +0 -3
  238. package/dist/routes/schemas-by-type.d.ts.map +0 -1
  239. package/dist/routes/schemas-by-type.js +0 -25
  240. package/dist/routes/schemas.d.ts +0 -3
  241. package/dist/routes/schemas.d.ts.map +0 -1
  242. package/dist/routes/schemas.js +0 -11
  243. package/dist/routes-exports.d.ts +0 -14
  244. package/dist/routes-exports.d.ts.map +0 -1
  245. package/dist/routes-exports.js +0 -19
  246. package/dist/schema-context.svelte.d.ts +0 -10
  247. package/dist/schema-context.svelte.d.ts.map +0 -1
  248. package/dist/schema-context.svelte.js +0 -18
  249. package/dist/schema-utils/cleanup.d.ts +0 -21
  250. package/dist/schema-utils/cleanup.d.ts.map +0 -1
  251. package/dist/schema-utils/cleanup.js +0 -80
  252. package/dist/schema-utils/index.d.ts +0 -4
  253. package/dist/schema-utils/index.d.ts.map +0 -1
  254. package/dist/schema-utils/index.js +0 -4
  255. package/dist/schema-utils/utils.d.ts +0 -30
  256. package/dist/schema-utils/utils.d.ts.map +0 -1
  257. package/dist/schema-utils/utils.js +0 -37
  258. package/dist/schema-utils/validator.d.ts +0 -6
  259. package/dist/schema-utils/validator.d.ts.map +0 -1
  260. package/dist/schema-utils/validator.js +0 -45
  261. package/dist/server/index.d.ts +0 -16
  262. package/dist/server/index.d.ts.map +0 -1
  263. package/dist/server/index.js +0 -28
  264. package/dist/services/asset-service.d.ts +0 -86
  265. package/dist/services/asset-service.d.ts.map +0 -1
  266. package/dist/services/asset-service.js +0 -187
  267. package/dist/services/index.d.ts +0 -3
  268. package/dist/services/index.d.ts.map +0 -1
  269. package/dist/services/index.js +0 -4
  270. package/dist/storage/adapters/index.d.ts +0 -2
  271. package/dist/storage/adapters/index.d.ts.map +0 -1
  272. package/dist/storage/adapters/index.js +0 -2
  273. package/dist/storage/adapters/local-storage-adapter.d.ts +0 -54
  274. package/dist/storage/adapters/local-storage-adapter.d.ts.map +0 -1
  275. package/dist/storage/adapters/local-storage-adapter.js +0 -187
  276. package/dist/storage/index.d.ts +0 -3
  277. package/dist/storage/index.d.ts.map +0 -1
  278. package/dist/storage/interfaces/index.d.ts +0 -2
  279. package/dist/storage/interfaces/index.d.ts.map +0 -1
  280. package/dist/storage/interfaces/index.js +0 -2
  281. package/dist/storage/interfaces/storage.d.ts +0 -91
  282. package/dist/storage/interfaces/storage.d.ts.map +0 -1
  283. package/dist/storage/interfaces/storage.js +0 -1
  284. package/dist/storage/providers/storage.d.ts +0 -43
  285. package/dist/storage/providers/storage.d.ts.map +0 -1
  286. package/dist/storage/providers/storage.js +0 -64
  287. package/dist/types/asset.d.ts +0 -73
  288. package/dist/types/asset.d.ts.map +0 -1
  289. package/dist/types/asset.js +0 -2
  290. package/dist/types/auth.d.ts +0 -50
  291. package/dist/types/auth.d.ts.map +0 -1
  292. package/dist/types/auth.js +0 -41
  293. package/dist/types/config.d.ts +0 -47
  294. package/dist/types/config.d.ts.map +0 -1
  295. package/dist/types/config.js +0 -1
  296. package/dist/types/document.d.ts +0 -34
  297. package/dist/types/document.d.ts.map +0 -1
  298. package/dist/types/document.js +0 -1
  299. package/dist/types/index.d.ts +0 -9
  300. package/dist/types/index.d.ts.map +0 -1
  301. package/dist/types/index.js +0 -8
  302. package/dist/types/organization.d.ts +0 -105
  303. package/dist/types/organization.d.ts.map +0 -1
  304. package/dist/types/organization.js +0 -3
  305. package/dist/types/schemas.d.ts +0 -114
  306. package/dist/types/schemas.d.ts.map +0 -1
  307. package/dist/types/schemas.js +0 -1
  308. package/dist/types/sidebar.d.ts +0 -33
  309. package/dist/types/sidebar.d.ts.map +0 -1
  310. package/dist/types/sidebar.js +0 -1
  311. package/dist/types/user.d.ts +0 -14
  312. package/dist/types/user.d.ts.map +0 -1
  313. package/dist/types/user.js +0 -1
  314. package/dist/utils/content-hash.d.ts +0 -22
  315. package/dist/utils/content-hash.d.ts.map +0 -1
  316. package/dist/utils/content-hash.js +0 -67
  317. package/dist/utils/image-url.d.ts +0 -88
  318. package/dist/utils/image-url.d.ts.map +0 -1
  319. package/dist/utils/image-url.js +0 -165
  320. package/dist/utils/index.d.ts +0 -6
  321. package/dist/utils/index.d.ts.map +0 -1
  322. package/dist/utils/index.js +0 -9
  323. package/dist/utils/slug.d.ts +0 -13
  324. package/dist/utils/slug.d.ts.map +0 -1
  325. package/dist/utils/slug.js +0 -30
  326. package/dist/utils.d.ts +0 -13
  327. package/dist/utils.d.ts.map +0 -1
  328. package/dist/utils.js +0 -5
  329. /package/{dist → src/lib}/app.d.ts +0 -0
  330. /package/{dist → src/lib}/auth/MULTI_TENANCY_PLAN.md +0 -0
  331. /package/{dist → src/lib}/components/admin/AdminLayout.svelte +0 -0
  332. /package/{dist → src/lib}/components/admin/DocumentEditor.svelte +0 -0
  333. /package/{dist → src/lib}/components/admin/DocumentTypesList.svelte +0 -0
  334. /package/{dist → src/lib}/components/admin/ObjectModal.svelte +0 -0
  335. /package/{dist → src/lib}/components/admin/SchemaField.svelte +0 -0
  336. /package/{dist → src/lib}/components/admin/fields/ArrayField.svelte +0 -0
  337. /package/{dist → src/lib}/components/admin/fields/BooleanField.svelte +0 -0
  338. /package/{dist → src/lib}/components/admin/fields/ImageField.svelte +0 -0
  339. /package/{dist → src/lib}/components/admin/fields/NumberField.svelte +0 -0
  340. /package/{dist → src/lib}/components/admin/fields/ReferenceField.svelte +0 -0
  341. /package/{dist → src/lib}/components/admin/fields/SlugField.svelte +0 -0
  342. /package/{dist → src/lib}/components/admin/fields/StringField.svelte +0 -0
  343. /package/{dist → src/lib}/components/admin/fields/TextareaField.svelte +0 -0
  344. /package/{dist/components/fields/index.js → src/lib/components/fields/index.ts} +0 -0
  345. /package/{dist → src/lib}/components/layout/OrganizationSwitcher.svelte +0 -0
  346. /package/{dist → src/lib}/components/layout/Sidebar.svelte +0 -0
  347. /package/{dist → src/lib}/components/layout/sidebar/AppSidebar.svelte +0 -0
  348. /package/{dist → src/lib}/components/layout/sidebar/NavMain.svelte +0 -0
  349. /package/{dist → src/lib}/components/layout/sidebar/NavSecondary.svelte +0 -0
  350. /package/{dist → src/lib}/components/layout/sidebar/NavUser.svelte +0 -0
  351. /package/{dist → src/lib}/plugins/README.md +0 -0
@@ -0,0 +1,215 @@
1
+ // Pure local file system storage adapter - no database operations
2
+ import { writeFile, mkdir, unlink, stat, readdir } from 'fs/promises';
3
+ import { join, dirname } from 'path';
4
+ import type {
5
+ StorageAdapter,
6
+ StorageConfig,
7
+ UploadFileData,
8
+ StorageFile
9
+ } from '../interfaces/storage';
10
+
11
+ const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
12
+ const DEFAULT_ALLOWED_TYPES = [
13
+ 'image/jpeg',
14
+ 'image/png',
15
+ 'image/webp',
16
+ 'image/gif',
17
+ 'image/avif',
18
+ 'application/pdf',
19
+ 'text/plain'
20
+ ];
21
+
22
+ /**
23
+ * Pure local file system storage adapter - only handles files
24
+ */
25
+ export class LocalStorageAdapter implements StorageAdapter {
26
+ readonly name = 'local';
27
+ private config: Required<StorageConfig>;
28
+
29
+ constructor(config: StorageConfig) {
30
+ this.config = {
31
+ basePath: config.basePath,
32
+ baseUrl: config.baseUrl || '',
33
+ maxFileSize: config.maxFileSize || DEFAULT_MAX_FILE_SIZE,
34
+ allowedTypes: config.allowedTypes || DEFAULT_ALLOWED_TYPES,
35
+ options: config.options || {}
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Generate unique filename preserving original name
41
+ */
42
+ private async generateUniqueFilename(originalFilename: string): Promise<string> {
43
+ const { name, ext } = this.parseFilename(originalFilename);
44
+ let filename = originalFilename;
45
+ let counter = 1;
46
+
47
+ // Check if file exists on disk
48
+ while (await this.fileExistsOnDisk(filename)) {
49
+ filename = `${name} (${counter})${ext}`;
50
+ counter++;
51
+ }
52
+
53
+ return filename;
54
+ }
55
+
56
+ /**
57
+ * Parse filename into name and extension
58
+ */
59
+ private parseFilename(filename: string): { name: string; ext: string } {
60
+ const lastDotIndex = filename.lastIndexOf('.');
61
+ if (lastDotIndex === -1) {
62
+ return { name: filename, ext: '' };
63
+ }
64
+ return {
65
+ name: filename.substring(0, lastDotIndex),
66
+ ext: filename.substring(lastDotIndex)
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Check if file exists on disk
72
+ */
73
+ private async fileExistsOnDisk(filename: string): Promise<boolean> {
74
+ try {
75
+ const filePath = join(this.config.basePath, filename);
76
+ await stat(filePath);
77
+ return true;
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Store a file and return storage info
85
+ */
86
+ async store(data: UploadFileData): Promise<StorageFile> {
87
+ // Validate file type
88
+ if (!this.config.allowedTypes.includes(data.mimeType)) {
89
+ throw new Error(
90
+ `Invalid file type: ${data.mimeType}. Allowed types: ${this.config.allowedTypes.join(', ')}`
91
+ );
92
+ }
93
+
94
+ // Validate file size
95
+ if (data.size > this.config.maxFileSize) {
96
+ throw new Error(
97
+ `File too large: ${data.size} bytes. Maximum size: ${this.config.maxFileSize} bytes`
98
+ );
99
+ }
100
+
101
+ // Generate unique filename
102
+ const filename = await this.generateUniqueFilename(data.filename);
103
+ const filePath = join(this.config.basePath, filename);
104
+
105
+ // Store internal path only - URL will be generated by API with asset ID
106
+ // This forces all access through /assets/{id} for proper authorization
107
+ const url = ''; // Will be populated as /assets/{assetId}/{filename} by the API
108
+
109
+ console.log('[LocalStorageAdapter] Storing file:', {
110
+ filename,
111
+ filePath,
112
+ note: 'URL will be generated as /assets/{assetId}/{filename}',
113
+ basePath: this.config.basePath
114
+ });
115
+
116
+ // Ensure storage directory exists
117
+ await mkdir(dirname(filePath), { recursive: true });
118
+
119
+ // Save file to disk
120
+ await writeFile(filePath, data.buffer);
121
+
122
+ return {
123
+ path: filePath,
124
+ url, // Empty - API will generate clean URL with asset ID
125
+ size: data.size
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Read a file from storage
131
+ * Used by API endpoint to serve files
132
+ */
133
+ async getObject(path: string): Promise<Buffer> {
134
+ const { readFile } = await import('fs/promises');
135
+ return await readFile(path);
136
+ }
137
+
138
+ /**
139
+ * Delete a file from storage
140
+ */
141
+ async delete(path: string): Promise<boolean> {
142
+ try {
143
+ await unlink(path);
144
+ return true;
145
+ } catch (error) {
146
+ console.warn('Could not delete file from disk:', error);
147
+ return false;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Check if file exists
153
+ */
154
+ async exists(path: string): Promise<boolean> {
155
+ try {
156
+ await stat(path);
157
+ return true;
158
+ } catch {
159
+ return false;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get public URL for a file path
165
+ */
166
+ getUrl(path: string): string {
167
+ // Extract filename from path and construct URL
168
+ const filename = path.split('/').pop() || '';
169
+ return `${this.config.baseUrl}/${encodeURIComponent(filename)}`;
170
+ }
171
+
172
+ /**
173
+ * Get storage information
174
+ */
175
+ async getStorageInfo(): Promise<{ totalSize: number; availableSpace?: number }> {
176
+ try {
177
+ // Calculate total size of files in storage directory
178
+ const files = await readdir(this.config.basePath);
179
+ let totalSize = 0;
180
+
181
+ for (const file of files) {
182
+ try {
183
+ const filePath = join(this.config.basePath, file);
184
+ const stats = await stat(filePath);
185
+ if (stats.isFile()) {
186
+ totalSize += stats.size;
187
+ }
188
+ } catch {
189
+ // Skip files that can't be read
190
+ }
191
+ }
192
+
193
+ return { totalSize };
194
+ } catch (error) {
195
+ console.error('Error getting storage info:', error);
196
+ return { totalSize: 0 };
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Health check - test if we can write to storage
202
+ */
203
+ async isHealthy(): Promise<boolean> {
204
+ try {
205
+ const testFile = join(this.config.basePath, `health-check-${Date.now()}.tmp`);
206
+ await mkdir(dirname(testFile), { recursive: true });
207
+ await writeFile(testFile, 'health check');
208
+ await unlink(testFile);
209
+ return true;
210
+ } catch (error) {
211
+ console.error('Storage health check failed:', error);
212
+ return false;
213
+ }
214
+ }
215
+ }
@@ -1,6 +1,8 @@
1
1
  // Aphex CMS Storage Layer
2
2
  // Ports and adapters for file storage operations
3
+
3
4
  // Interfaces
4
- export * from './interfaces/index.js';
5
+ export * from './interfaces/index';
6
+
5
7
  // Adapters
6
- export * from './adapters/index.js';
8
+ export * from './adapters/index';
@@ -0,0 +1,2 @@
1
+ // Storage interfaces
2
+ export * from './storage';
@@ -0,0 +1,114 @@
1
+ // Pure storage interface for file operations only
2
+ export interface StorageFile {
3
+ path: string;
4
+ url: string;
5
+ size: number;
6
+ }
7
+
8
+ export interface UploadFileData {
9
+ buffer: Buffer;
10
+ filename: string;
11
+ mimeType: string;
12
+ size: number;
13
+ }
14
+
15
+ export interface StorageConfig {
16
+ basePath: string;
17
+ baseUrl?: string;
18
+ maxFileSize?: number;
19
+ allowedTypes?: string[];
20
+ options?: {
21
+ [key: string]: any;
22
+ };
23
+ }
24
+
25
+ export interface StorageObjectMetadata {
26
+ key: string;
27
+ size: number;
28
+ lastModified: Date;
29
+ contentType?: string;
30
+ etag?: string;
31
+ }
32
+
33
+ export interface ListObjectsOptions {
34
+ prefix?: string;
35
+ maxKeys?: number;
36
+ continuationToken?: string;
37
+ }
38
+
39
+ export interface ListObjectsResult {
40
+ objects: StorageObjectMetadata[];
41
+ isTruncated: boolean;
42
+ continuationToken?: string;
43
+ }
44
+
45
+ /**
46
+ * Pure storage interface - only handles file operations
47
+ * No database operations - completely agnostic
48
+ */
49
+ export interface StorageAdapter {
50
+ // Adapter identifier (used to track which adapter stored each file)
51
+ readonly name: string;
52
+
53
+ // Core file operations (required)
54
+ store(data: UploadFileData): Promise<StorageFile>;
55
+ delete(path: string): Promise<boolean>;
56
+ exists(path: string): Promise<boolean>;
57
+ getUrl(path: string): string;
58
+
59
+ // Storage info
60
+ getStorageInfo(): Promise<{
61
+ totalSize: number;
62
+ availableSpace?: number;
63
+ }>;
64
+
65
+ // Health check
66
+ isHealthy(): Promise<boolean>;
67
+
68
+ // Connection management (optional)
69
+ connect?(): Promise<void>;
70
+ disconnect?(): Promise<void>;
71
+
72
+ // Extended file operations (optional - for advanced storage features)
73
+
74
+ /**
75
+ * Retrieve file contents as Buffer
76
+ * Useful for: image processing, transformations, backups, migrations
77
+ */
78
+ getObject?(path: string): Promise<Buffer>;
79
+
80
+ /**
81
+ * List objects in storage with optional filtering
82
+ * Useful for: admin UI file browser, asset management, cleanup operations
83
+ */
84
+ listObjects?(options?: ListObjectsOptions): Promise<ListObjectsResult>;
85
+
86
+ /**
87
+ * Copy/duplicate an object within storage
88
+ * Useful for: versioning, backups, image variant generation
89
+ */
90
+ copyObject?(sourcePath: string, destPath: string): Promise<boolean>;
91
+
92
+ /**
93
+ * Get object metadata without downloading the file
94
+ * Useful for: verifying file integrity, checking size/type
95
+ */
96
+ getObjectMetadata?(path: string): Promise<StorageObjectMetadata>;
97
+
98
+ /**
99
+ * Generate a signed URL for temporary access to a file
100
+ * Useful for: access control, private files, secure downloads
101
+ * @param path - File path
102
+ * @param expiresIn - Expiration time in seconds (default: 3600)
103
+ * @returns Signed URL that expires after the specified time
104
+ */
105
+ getSignedUrl?(path: string, expiresIn?: number): Promise<string>;
106
+ }
107
+
108
+ /**
109
+ * Storage provider factory interface
110
+ */
111
+ export interface StorageProvider {
112
+ name: string;
113
+ createAdapter(config: StorageConfig): StorageAdapter;
114
+ }
@@ -0,0 +1,83 @@
1
+ // Storage provider factory for creating different storage adapters
2
+ import type { StorageAdapter, StorageProvider, StorageConfig } from '../interfaces/storage';
3
+ import { LocalStorageAdapter } from '../adapters/local-storage-adapter';
4
+
5
+ /**
6
+ * Local file system provider
7
+ */
8
+ export class LocalStorageProvider implements StorageProvider {
9
+ name = 'local';
10
+
11
+ createAdapter(config: StorageConfig): StorageAdapter {
12
+ return new LocalStorageAdapter(config);
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Storage provider registry
18
+ */
19
+ class StorageProviderRegistry {
20
+ private providers = new Map<string, StorageProvider>();
21
+
22
+ constructor() {
23
+ // Register built-in providers
24
+ this.register(new LocalStorageProvider());
25
+ }
26
+
27
+ register(provider: StorageProvider): void {
28
+ this.providers.set(provider.name.toLowerCase(), provider);
29
+ }
30
+
31
+ get(name: string): StorageProvider | undefined {
32
+ return this.providers.get(name.toLowerCase());
33
+ }
34
+
35
+ list(): string[] {
36
+ return Array.from(this.providers.keys());
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Global storage provider registry
42
+ *
43
+ * External packages can register custom storage providers:
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { storageProviders } from '@aphexcms/cms-core/server';
48
+ * import { R2StorageProvider } from '@aphexcms/storage-r2';
49
+ *
50
+ * // Register before creating config
51
+ * storageProviders.register(new R2StorageProvider());
52
+ * ```
53
+ */
54
+ export const storageProviders = new StorageProviderRegistry();
55
+
56
+ /**
57
+ * Factory function to create storage adapters
58
+ */
59
+ export function createStorageAdapter(providerName: string, config: StorageConfig): StorageAdapter {
60
+ const provider = storageProviders.get(providerName);
61
+
62
+ if (!provider) {
63
+ const available = storageProviders.list();
64
+ throw new Error(
65
+ `Unknown storage provider: ${providerName}. Available providers: ${available.join(', ')}`
66
+ );
67
+ }
68
+
69
+ return provider.createAdapter(config);
70
+ }
71
+
72
+ /**
73
+ * Convenience function for local storage
74
+ */
75
+ export function createLocalStorageAdapter(
76
+ basePath: string,
77
+ options?: Partial<StorageConfig>
78
+ ): StorageAdapter {
79
+ return createStorageAdapter('local', {
80
+ basePath,
81
+ ...options
82
+ });
83
+ }
@@ -0,0 +1,81 @@
1
+ // types/asset.ts
2
+
3
+ /**
4
+ * Asset type - represents uploaded files
5
+ */
6
+ export interface Asset {
7
+ id: string;
8
+ organizationId: string;
9
+ assetType: string;
10
+ filename: string;
11
+ originalFilename: string;
12
+ mimeType: string;
13
+ size: number;
14
+ url: string;
15
+ path: string;
16
+ storageAdapter: string;
17
+ width: number | null;
18
+ height: number | null;
19
+ metadata: any;
20
+ title: string | null;
21
+ description: string | null;
22
+ alt: string | null;
23
+ creditLine: string | null;
24
+ createdBy: string | null;
25
+ createdAt: Date | null;
26
+ updatedAt: Date | null;
27
+ }
28
+
29
+ /**
30
+ * New asset input type
31
+ */
32
+ export interface NewAsset {
33
+ id?: string;
34
+ assetType: string;
35
+ filename: string;
36
+ originalFilename: string;
37
+ mimeType: string;
38
+ size: number;
39
+ url: string;
40
+ path: string;
41
+ storageAdapter: string;
42
+ width?: number | null;
43
+ height?: number | null;
44
+ metadata?: any;
45
+ title?: string | null;
46
+ description?: string | null;
47
+ alt?: string | null;
48
+ creditLine?: string | null;
49
+ createdBy?: string | null;
50
+ createdAt?: Date | null;
51
+ updatedAt?: Date | null;
52
+ }
53
+
54
+ // Sanity-style image data structure
55
+ export interface ImageAsset {
56
+ _type: 'reference';
57
+ _ref: string; // Asset ID
58
+ }
59
+
60
+ export interface ImageCrop {
61
+ top: number;
62
+ bottom: number;
63
+ left: number;
64
+ right: number;
65
+ }
66
+
67
+ export interface ImageHotspot {
68
+ x: number;
69
+ y: number;
70
+ height: number;
71
+ width: number;
72
+ }
73
+
74
+ export interface ImageValue {
75
+ _type: 'image';
76
+ asset: ImageAsset;
77
+ crop?: ImageCrop;
78
+ hotspot?: ImageHotspot;
79
+ // Additional custom fields can be added here
80
+ [key: string]: any;
81
+ }
@@ -0,0 +1,80 @@
1
+ // types/auth.ts
2
+ import type { CMSUser } from './user';
3
+ import type { OrganizationRole } from './organization';
4
+
5
+ export interface SessionAuth {
6
+ type: 'session';
7
+ user: CMSUser;
8
+ session: {
9
+ id: string;
10
+ expiresAt: Date;
11
+ };
12
+ organizationId: string;
13
+ organizationRole: OrganizationRole;
14
+ organizations?: Array<{
15
+ id: string;
16
+ name: string;
17
+ slug: string;
18
+ role: OrganizationRole;
19
+ isActive: boolean;
20
+ metadata?: any;
21
+ }>;
22
+ }
23
+
24
+ export interface ApiKeyAuth {
25
+ type: 'api_key';
26
+ keyId: string;
27
+ name: string;
28
+ permissions: ('read' | 'write')[];
29
+ organizationId: string;
30
+ environment?: string;
31
+ lastUsedAt?: Date;
32
+ }
33
+
34
+ export type Auth = SessionAuth | ApiKeyAuth;
35
+
36
+ // Access control utilities
37
+
38
+ /**
39
+ * Check if a user has write permissions based on their organization role
40
+ * Viewers have read-only access
41
+ */
42
+ export function canWrite(auth: Auth): boolean {
43
+ if (auth.type === 'api_key') {
44
+ return auth.permissions.includes('write');
45
+ }
46
+ // Viewers are read-only
47
+ return auth.organizationRole !== 'viewer';
48
+ }
49
+
50
+ /**
51
+ * Check if a user can manage organization members
52
+ * Only owners and admins can manage members
53
+ */
54
+ export function canManageMembers(auth: Auth): boolean {
55
+ if (auth.type === 'api_key') {
56
+ return false;
57
+ }
58
+ return auth.organizationRole === 'owner' || auth.organizationRole === 'admin';
59
+ }
60
+
61
+ /**
62
+ * Check if a user can manage API keys
63
+ * Only owners and admins can manage API keys
64
+ */
65
+ export function canManageApiKeys(auth: Auth): boolean {
66
+ if (auth.type === 'api_key') {
67
+ return false;
68
+ }
69
+ return auth.organizationRole === 'owner' || auth.organizationRole === 'admin';
70
+ }
71
+
72
+ /**
73
+ * Check if a user is a viewer (read-only access)
74
+ */
75
+ export function isViewer(auth: Auth): boolean {
76
+ if (auth.type === 'api_key') {
77
+ return !auth.permissions.includes('write');
78
+ }
79
+ return auth.organizationRole === 'viewer';
80
+ }
@@ -0,0 +1,45 @@
1
+ // types/config.ts
2
+ import type { AuthProvider } from '../auth/provider';
3
+ import type { DatabaseAdapter } from '../db/index';
4
+ import type { StorageAdapter } from '../storage/interfaces/index';
5
+ import type { EmailAdapter } from '../email/index';
6
+ import { SchemaType } from './schemas';
7
+
8
+ export interface CMSPlugin {
9
+ name: string;
10
+ version: string;
11
+ routes?: { [path: string]: (event: any) => Promise<Response> | Response };
12
+ install: (cms: any) => Promise<void>;
13
+ config?: { [key: string]: any };
14
+ }
15
+
16
+ export interface CMSConfig {
17
+ schemaTypes: SchemaType[];
18
+ database: DatabaseAdapter;
19
+ storage?: StorageAdapter | null;
20
+ email?: EmailAdapter | null;
21
+ customization?: {
22
+ branding?: {
23
+ title?: string;
24
+ logo?: string;
25
+ favicon?: string;
26
+ };
27
+ theme?: {
28
+ colors?: Record<string, string>;
29
+ fonts?: Record<string, string>;
30
+ };
31
+ };
32
+ plugins?: CMSPlugin[];
33
+ auth?: {
34
+ provider: AuthProvider;
35
+ loginUrl?: string;
36
+ };
37
+ security?: {
38
+ /**
39
+ * Secret key for signing asset URLs (enables multi-tenant access without exposing API keys)
40
+ * Should be a long, random string (e.g., 32+ characters)
41
+ * Required for signed asset URLs to work
42
+ */
43
+ assetSigningSecret?: string;
44
+ };
45
+ }
@@ -0,0 +1,38 @@
1
+ // types/document.ts
2
+ //
3
+ //
4
+ import { AuthUser } from '../types/user';
5
+
6
+ /**
7
+ * Document type - represents a CMS document
8
+ */
9
+ export interface Document {
10
+ id: string;
11
+ type: string;
12
+ status: 'draft' | 'published' | null;
13
+ draftData: any;
14
+ publishedData: any;
15
+ publishedHash: string | null;
16
+ createdBy: string | AuthUser | null;
17
+ updatedBy: string | null;
18
+ publishedAt: Date | null;
19
+ createdAt: Date | null;
20
+ updatedAt: Date | null;
21
+ }
22
+
23
+ /**
24
+ * New document input type
25
+ */
26
+ export interface NewDocument {
27
+ id?: string;
28
+ type: string;
29
+ status?: 'draft' | 'published' | null;
30
+ draftData?: any;
31
+ publishedData?: any;
32
+ publishedHash?: string | null;
33
+ createdBy?: string | null;
34
+ updatedBy?: string | null;
35
+ publishedAt?: Date | null;
36
+ createdAt?: Date | null;
37
+ updatedAt?: Date | null;
38
+ }
@@ -0,0 +1,8 @@
1
+ export * from './asset';
2
+ export * from './auth';
3
+ export * from './document';
4
+ export * from './schemas';
5
+ export * from './config';
6
+ export * from './user';
7
+ export * from './sidebar';
8
+ export * from './organization';