studiocms 0.2.0 → 0.4.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 (293) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/cli/add/index.d.ts +2 -2
  3. package/dist/cli/add/index.js +4 -3
  4. package/dist/cli/add/npm-utils.d.ts +6 -6
  5. package/dist/cli/add/tryToInstallPlugins.d.ts +1 -1
  6. package/dist/cli/add/tryToInstallPlugins.js +6 -5
  7. package/dist/cli/add/updateStudioCMSConfig.d.ts +1 -1
  8. package/dist/cli/add/updateStudioCMSConfig.js +3 -4
  9. package/dist/cli/add/validatePlugins.d.ts +1 -2
  10. package/dist/cli/add/validatePlugins.js +15 -9
  11. package/dist/cli/crypto/genJWT/index.d.ts +1 -1
  12. package/dist/cli/crypto/genJWT/index.js +8 -9
  13. package/dist/cli/crypto/index.d.ts +1 -1
  14. package/dist/cli/init/steps/env.js +14 -4
  15. package/dist/cli/init/steps/next.d.ts +1 -1
  16. package/dist/cli/init/steps/next.js +6 -5
  17. package/dist/cli/migrator/index.d.ts +1 -1
  18. package/dist/cli/migrator/index.js +2 -2
  19. package/dist/cli/users/index.d.ts +1 -1
  20. package/dist/cli/users/shared.js +2 -2
  21. package/dist/cli/users/steps/createUsers.js +7 -7
  22. package/dist/cli/users/steps/modifyUsers.js +2 -2
  23. package/dist/cli/users/steps/next.d.ts +1 -1
  24. package/dist/cli/utils/checkRequiredEnvVars.js +2 -2
  25. package/dist/cli/utils/context.d.ts +2 -4
  26. package/dist/cli/utils/context.js +1 -3
  27. package/dist/cli/utils/getCliDbClient.d.ts +1 -1
  28. package/dist/cli/utils/intro.d.ts +1 -1
  29. package/dist/cli/utils/loadConfig.d.ts +54 -49
  30. package/dist/cli/utils/loadConfig.js +5 -8
  31. package/dist/cli/utils/logger.js +3 -3
  32. package/dist/cli/utils/user-utils.d.ts +1 -1
  33. package/dist/cli/utils/user-utils.js +4 -3
  34. package/dist/client/apiClient.d.ts +4923 -0
  35. package/dist/client/apiClient.js +72 -0
  36. package/dist/config.d.ts +1734 -1
  37. package/dist/consts.d.ts +5 -5
  38. package/dist/consts.js +3 -2
  39. package/dist/db/plugins.d.ts +1 -1
  40. package/dist/db/plugins.js +5 -8
  41. package/dist/handlers/frontend/routes.d.ts +4 -18
  42. package/dist/handlers/frontend/routes.js +13 -152
  43. package/dist/handlers/frontend/types.d.ts +1 -1
  44. package/dist/handlers/frontend/utils.js +0 -18
  45. package/dist/handlers/pluginHandler.d.ts +34 -257
  46. package/dist/handlers/pluginHandler.js +92 -46
  47. package/dist/handlers/routeHandler.js +32 -11
  48. package/dist/handlers/setupDbStudio.d.ts +3 -1
  49. package/dist/handlers/setupDbStudio.js +19 -10
  50. package/dist/handlers/storage-manager/core/effectify-astro-context.d.ts +25 -0
  51. package/dist/handlers/storage-manager/core/effectify-astro-context.js +78 -0
  52. package/dist/handlers/storage-manager/no-op.d.ts +2 -2
  53. package/dist/handlers/storage-manager/no-op.js +2 -3
  54. package/dist/index.d.ts +0 -1
  55. package/dist/index.js +10 -20
  56. package/dist/integrations/robots/index.d.ts +2 -2
  57. package/dist/integrations/robots/index.js +1 -3
  58. package/dist/integrations/robots/schema.d.ts +102 -273
  59. package/dist/integrations/robots/schema.js +220 -209
  60. package/dist/plugins/analytics/assets/schemas.d.ts +14 -9
  61. package/dist/plugins/analytics/assets/schemas.js +25 -17
  62. package/dist/plugins/analytics/db-client.d.ts +1 -1
  63. package/dist/plugins/analytics/index.d.ts +823 -3
  64. package/dist/plugins/analytics/index.js +4 -5
  65. package/dist/plugins/analytics/schemas.d.ts +54 -62
  66. package/dist/plugins/analytics/schemas.js +64 -13
  67. package/dist/plugins/analytics/table.d.ts +1 -1
  68. package/dist/plugins.d.ts +0 -1
  69. package/dist/schemas/config/api.d.ts +17 -0
  70. package/dist/schemas/config/api.js +14 -0
  71. package/dist/schemas/config/auth.d.ts +55 -59
  72. package/dist/schemas/config/auth.js +34 -11
  73. package/dist/schemas/config/dashboard.d.ts +43 -79
  74. package/dist/schemas/config/dashboard.js +43 -12
  75. package/dist/schemas/config/db.d.ts +15 -17
  76. package/dist/schemas/config/db.js +13 -5
  77. package/dist/schemas/config/developer.d.ts +33 -45
  78. package/dist/schemas/config/developer.js +22 -5
  79. package/dist/schemas/config/index.d.ts +398 -521
  80. package/dist/schemas/config/index.js +115 -57
  81. package/dist/schemas/config/sdk.d.ts +50 -196
  82. package/dist/schemas/config/sdk.js +61 -73
  83. package/dist/schemas/custom.d.ts +40 -0
  84. package/dist/schemas/custom.js +41 -0
  85. package/dist/schemas/external-schemas.d.ts +171 -0
  86. package/dist/schemas/external-schemas.js +179 -0
  87. package/dist/schemas/index.d.ts +2 -0
  88. package/dist/schemas/index.js +2 -0
  89. package/dist/schemas/plugins/i18n.d.ts +59 -39
  90. package/dist/schemas/plugins/i18n.js +42 -5
  91. package/dist/schemas/plugins/index.d.ts +7126 -10296
  92. package/dist/schemas/plugins/index.js +260 -276
  93. package/dist/schemas/plugins/shared.d.ts +1293 -3718
  94. package/dist/schemas/plugins/shared.js +320 -329
  95. package/dist/test-utils.d.ts +15 -4
  96. package/dist/test-utils.js +27 -11
  97. package/dist/toolbar/db-viewer/db-shared-types.d.ts +6 -6
  98. package/dist/toolbar/db-viewer/studio/connection.d.ts +8 -4
  99. package/dist/toolbar/db-viewer/studio/connection.js +2 -28
  100. package/dist/toolbar/db-viewer/studio/env/libsql.d.ts +7 -0
  101. package/dist/toolbar/db-viewer/studio/env/libsql.js +17 -0
  102. package/dist/toolbar/db-viewer/studio/env/mysql.d.ts +7 -0
  103. package/dist/toolbar/db-viewer/studio/env/mysql.js +23 -0
  104. package/dist/toolbar/db-viewer/studio/env/postgres.d.ts +7 -0
  105. package/dist/toolbar/db-viewer/studio/env/postgres.js +23 -0
  106. package/dist/toolbar/db-viewer/studio/index.js +20 -56
  107. package/dist/toolbar/db-viewer/studio/type.d.ts +1 -2
  108. package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.d.ts +3 -0
  109. package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.js +24 -0
  110. package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.d.ts +3 -0
  111. package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.js +9 -0
  112. package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.d.ts +3 -0
  113. package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.js +9 -0
  114. package/dist/toolbar/db-viewer/viewer.js +20 -21
  115. package/dist/types.d.ts +30 -0
  116. package/dist/utils/effects/smtp.d.ts +1 -1
  117. package/dist/utils/lang-helper.d.ts +10 -2
  118. package/dist/virtual.d.ts +35 -28
  119. package/dist/virtuals/auth/core.d.ts +5 -5
  120. package/dist/virtuals/auth/verify-email.d.ts +6 -6
  121. package/dist/virtuals/components/Generator.astro +2 -2
  122. package/dist/virtuals/components/Renderer.astro +9 -1
  123. package/dist/virtuals/components/renderFn.d.ts +3 -1
  124. package/dist/virtuals/components/renderFn.js +18 -0
  125. package/dist/virtuals/lib/headDefaults.d.ts +4 -2
  126. package/dist/virtuals/lib/headDefaults.js +0 -2
  127. package/dist/virtuals/lib/routeMap.d.ts +0 -12
  128. package/dist/virtuals/lib/routeMap.js +2 -14
  129. package/dist/virtuals/mailer/index.d.ts +3 -3
  130. package/dist/virtuals/notifier/index.d.ts +5 -5
  131. package/dist/virtuals/plugins/dashboard-pages.d.ts +2 -64
  132. package/dist/virtuals/scripts/StorageFileBrowser.d.ts +1 -172
  133. package/dist/virtuals/scripts/StorageFileBrowser.js +216 -119
  134. package/dist/virtuals/template-engine/index.d.ts +4 -4
  135. package/frontend/components/dashboard/configuration/ConfigForm.astro +218 -110
  136. package/frontend/components/dashboard/content-mgmt/ContentSearch.astro +21 -22
  137. package/frontend/components/dashboard/content-mgmt/CreateFolder.astro +66 -54
  138. package/frontend/components/dashboard/content-mgmt/CreatePage.astro +58 -104
  139. package/frontend/components/dashboard/content-mgmt/EditFolder.astro +65 -67
  140. package/frontend/components/dashboard/content-mgmt/EditPage.astro +86 -134
  141. package/frontend/components/dashboard/content-mgmt/InnerSidebarElement.astro +0 -1
  142. package/frontend/components/dashboard/content-mgmt/PageHeader.astro +33 -52
  143. package/frontend/components/dashboard/content-mgmt/PageTypeHandler.astro +2 -2
  144. package/frontend/components/dashboard/profile/APITokens.astro +219 -158
  145. package/frontend/components/dashboard/profile/BasicInfo.astro +165 -106
  146. package/frontend/components/dashboard/profile/Notifications.astro +27 -18
  147. package/frontend/components/dashboard/profile/UpdatePassword.astro +134 -94
  148. package/frontend/components/dashboard/sidebar/VersionCheck.astro +31 -16
  149. package/frontend/components/dashboard/sidebar/VersionCheckChangelog.astro +18 -11
  150. package/frontend/components/dashboard/sidebar-modals/VersionModal.astro +2 -2
  151. package/frontend/components/dashboard/smtp-config/TemplateEditor.astro +14 -14
  152. package/frontend/components/dashboard/taxonomy/InnerSidebarElement.astro +0 -1
  153. package/frontend/components/dashboard/taxonomy/MetaContainer.astro +0 -2
  154. package/frontend/components/dashboard/taxonomy/PageHeader.astro +16 -24
  155. package/frontend/components/dashboard/taxonomy/TaxonomySearch.astro +23 -27
  156. package/frontend/components/dashboard/user-mgmt/InnerSidebarElement.astro +111 -104
  157. package/frontend/components/dashboard/user-mgmt/UserListItem.astro +9 -22
  158. package/frontend/components/dashboard/user-mgmt/UserListItems.astro +18 -0
  159. package/frontend/components/first-time-setup/snippets/{opt2-studiocms.config.diff → studiocms.config.diff} +1 -0
  160. package/frontend/components/shared/Code.astro +1 -4
  161. package/frontend/components/shared/DynamicSettingsRenderer.astro +1 -1
  162. package/frontend/components/shared/SSRUser.astro +2 -4
  163. package/frontend/components/shared/foldertree/FolderTreeNode.astro +0 -6
  164. package/frontend/components/shared/storage-manager/StorageCopyOutput.astro +0 -1
  165. package/frontend/components/shared/taxonomy/TaxonomyTreeNode.astro +0 -6
  166. package/frontend/layouts/DashboardLayout.astro +1 -10
  167. package/frontend/layouts/TaxonomyLayout.astro +0 -1
  168. package/frontend/middleware/index.ts +102 -61
  169. package/frontend/pages/404.astro +5 -9
  170. package/frontend/pages/[dashboard]/[...pluginPage].astro +10 -1
  171. package/frontend/pages/[dashboard]/configuration.astro +10 -1
  172. package/frontend/pages/[dashboard]/content-management/createfolder.astro +10 -1
  173. package/frontend/pages/[dashboard]/content-management/createpage.astro +10 -1
  174. package/frontend/pages/[dashboard]/content-management/diff.astro +39 -14
  175. package/frontend/pages/[dashboard]/content-management/editfolder.astro +10 -1
  176. package/frontend/pages/[dashboard]/content-management/editpage.astro +10 -1
  177. package/frontend/pages/[dashboard]/content-management/index.astro +10 -1
  178. package/frontend/pages/[dashboard]/index.astro +10 -1
  179. package/frontend/pages/[dashboard]/login.astro +86 -25
  180. package/frontend/pages/[dashboard]/password-reset.astro +22 -16
  181. package/frontend/pages/[dashboard]/plugins/[plugin].astro +10 -1
  182. package/frontend/pages/[dashboard]/profile.astro +10 -1
  183. package/frontend/pages/[dashboard]/signup.astro +153 -52
  184. package/frontend/pages/[dashboard]/smtp-configuration.astro +77 -75
  185. package/frontend/pages/[dashboard]/system-management.astro +10 -1
  186. package/frontend/pages/[dashboard]/taxonomy/categories.astro +30 -41
  187. package/frontend/pages/[dashboard]/taxonomy/index.astro +10 -0
  188. package/frontend/pages/[dashboard]/taxonomy/tags.astro +33 -43
  189. package/frontend/pages/[dashboard]/unverified-email.astro +29 -21
  190. package/frontend/pages/[dashboard]/user-management/edit.astro +170 -90
  191. package/frontend/pages/[dashboard]/user-management/index.astro +10 -1
  192. package/frontend/pages/studiocms_api/[...all].ts +106 -0
  193. package/frontend/pages/studiocms_api/_handlers/_utils/auth.ts +26 -0
  194. package/frontend/pages/studiocms_api/_handlers/_utils/changelog.ts +147 -0
  195. package/frontend/pages/studiocms_api/_handlers/_utils/db-studio-driver.ts +46 -0
  196. package/frontend/pages/studiocms_api/_handlers/_utils/parseLogLevel.ts +27 -0
  197. package/frontend/pages/studiocms_api/_handlers/auth/auth.ts +459 -0
  198. package/frontend/pages/studiocms_api/_handlers/auth/index.ts +17 -0
  199. package/frontend/pages/studiocms_api/_handlers/auth/oauth.ts +91 -0
  200. package/frontend/pages/studiocms_api/_handlers/dashboard/_shared.ts +57 -0
  201. package/frontend/pages/studiocms_api/_handlers/dashboard/apiTokens.ts +134 -0
  202. package/frontend/pages/studiocms_api/_handlers/dashboard/config.ts +64 -0
  203. package/frontend/pages/studiocms_api/_handlers/dashboard/content.ts +741 -0
  204. package/frontend/pages/studiocms_api/_handlers/dashboard/create.ts +480 -0
  205. package/frontend/pages/studiocms_api/_handlers/dashboard/emailNotifications.ts +49 -0
  206. package/frontend/pages/studiocms_api/_handlers/dashboard/index.ts +45 -0
  207. package/frontend/pages/studiocms_api/_handlers/dashboard/mailer.ts +136 -0
  208. package/frontend/pages/studiocms_api/_handlers/dashboard/plugins.ts +80 -0
  209. package/frontend/pages/studiocms_api/_handlers/dashboard/profile.ts +275 -0
  210. package/frontend/pages/studiocms_api/_handlers/dashboard/resetPassword.ts +140 -0
  211. package/frontend/pages/studiocms_api/_handlers/dashboard/search.ts +63 -0
  212. package/frontend/pages/studiocms_api/_handlers/dashboard/taxonomy.ts +285 -0
  213. package/frontend/pages/studiocms_api/_handlers/dashboard/templates.ts +75 -0
  214. package/frontend/pages/studiocms_api/_handlers/dashboard/users.ts +312 -0
  215. package/frontend/pages/studiocms_api/_handlers/dashboard/verifyEndpoints.ts +307 -0
  216. package/frontend/pages/studiocms_api/_handlers/integration/dbStudio.ts +98 -0
  217. package/frontend/pages/studiocms_api/_handlers/integration/index.ts +17 -0
  218. package/frontend/pages/studiocms_api/_handlers/integration/storageManager.ts +107 -0
  219. package/frontend/pages/studiocms_api/_handlers/rest-api/index.ts +8 -0
  220. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/_shared.ts +41 -0
  221. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/index.ts +17 -0
  222. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/public.ts +195 -0
  223. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts +1726 -0
  224. package/frontend/pages/studiocms_api/_handlers/sdk.ts +129 -0
  225. package/frontend/pages/studiocms_api/_middleware/astroLocals.ts +36 -0
  226. package/frontend/pages/studiocms_api/_middleware/restApi.ts +56 -0
  227. package/frontend/pages/studiocms_api/integrations/[...all].ts +8 -0
  228. package/frontend/scripts/formdata.ts +219 -0
  229. package/frontend/setup-pages/3-done.astro +4 -20
  230. package/frontend/setup-pages/studiocms_api/dashboard/step-2.ts +3 -6
  231. package/frontend/styles/dashboard-base.css +0 -1
  232. package/frontend/web-vitals/endpoint.ts +2 -1
  233. package/package.json +35 -31
  234. package/dist/global.d.ts +0 -9
  235. package/frontend/components/dashboard/LoginChecker.astro +0 -68
  236. package/frontend/components/dashboard/user-mgmt/RankCheck.astro +0 -57
  237. package/frontend/components/first-time-setup/snippets/opt1-astro.config.diff +0 -14
  238. package/frontend/components/first-time-setup/snippets/opt2-astro.config.diff +0 -9
  239. package/frontend/middleware/_authmap.ts +0 -63
  240. package/frontend/pages/studiocms_api/auth/[path].ts +0 -390
  241. package/frontend/pages/studiocms_api/auth/[provider]/[...id].ts +0 -64
  242. package/frontend/pages/studiocms_api/auth/[provider]/_effects/index.ts +0 -101
  243. package/frontend/pages/studiocms_api/auth/_shared.ts +0 -52
  244. package/frontend/pages/studiocms_api/dashboard/api-tokens.ts +0 -115
  245. package/frontend/pages/studiocms_api/dashboard/config.ts +0 -74
  246. package/frontend/pages/studiocms_api/dashboard/content/diff.ts +0 -73
  247. package/frontend/pages/studiocms_api/dashboard/content/folder.ts +0 -220
  248. package/frontend/pages/studiocms_api/dashboard/content/page.ts +0 -359
  249. package/frontend/pages/studiocms_api/dashboard/create-reset-link.ts +0 -77
  250. package/frontend/pages/studiocms_api/dashboard/create-user-invite.ts +0 -231
  251. package/frontend/pages/studiocms_api/dashboard/create-user.ts +0 -186
  252. package/frontend/pages/studiocms_api/dashboard/email-notification-settings-site.ts +0 -74
  253. package/frontend/pages/studiocms_api/dashboard/mailer/check-email.ts +0 -75
  254. package/frontend/pages/studiocms_api/dashboard/mailer/config.ts +0 -136
  255. package/frontend/pages/studiocms_api/dashboard/plugins/[plugin].ts +0 -80
  256. package/frontend/pages/studiocms_api/dashboard/profile.ts +0 -245
  257. package/frontend/pages/studiocms_api/dashboard/resend-verify-email.ts +0 -77
  258. package/frontend/pages/studiocms_api/dashboard/reset-password.ts +0 -124
  259. package/frontend/pages/studiocms_api/dashboard/search-list.ts +0 -59
  260. package/frontend/pages/studiocms_api/dashboard/taxonomy-search.ts +0 -47
  261. package/frontend/pages/studiocms_api/dashboard/taxonomy.ts +0 -230
  262. package/frontend/pages/studiocms_api/dashboard/templates.ts +0 -74
  263. package/frontend/pages/studiocms_api/dashboard/update-user-notifications.ts +0 -86
  264. package/frontend/pages/studiocms_api/dashboard/users.ts +0 -236
  265. package/frontend/pages/studiocms_api/dashboard/verify-email.ts +0 -83
  266. package/frontend/pages/studiocms_api/dashboard/verify-session.ts +0 -187
  267. package/frontend/pages/studiocms_api/integrations/[type]/[...id].ts +0 -15
  268. package/frontend/pages/studiocms_api/integrations/[type]/_routes/db-studio.ts +0 -173
  269. package/frontend/pages/studiocms_api/integrations/[type]/_routes/storage.ts +0 -88
  270. package/frontend/pages/studiocms_api/partials/editor.astro +0 -74
  271. package/frontend/pages/studiocms_api/partials/render.astro +0 -39
  272. package/frontend/pages/studiocms_api/partials/user-list-items.astro +0 -43
  273. package/frontend/pages/studiocms_api/rest/utils/auth-token.ts +0 -59
  274. package/frontend/pages/studiocms_api/rest/v1/[type]/[...id].ts +0 -23
  275. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/categories.ts +0 -267
  276. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/folders.ts +0 -283
  277. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/pages.ts +0 -505
  278. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/settings.ts +0 -100
  279. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/tags.ts +0 -229
  280. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/users.ts +0 -553
  281. package/frontend/pages/studiocms_api/rest/v1/public/[type]/[...id].ts +0 -19
  282. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/categories.ts +0 -74
  283. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/folders.ts +0 -85
  284. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/pages.ts +0 -103
  285. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/tags.ts +0 -67
  286. package/frontend/pages/studiocms_api/sdk/[...path].ts +0 -97
  287. package/frontend/pages/studiocms_api/sdk/utils/changelog.ts +0 -119
  288. package/frontend/scripts/auth/formListener.ts +0 -81
  289. package/frontend/scripts/formdata-utils.ts +0 -116
  290. package/frontend/utils/build-partial-schema.ts +0 -46
  291. package/frontend/utils/errors.ts +0 -6
  292. package/frontend/utils/param-extractor.ts +0 -83
  293. package/frontend/utils/rest-router.ts +0 -444
@@ -1,553 +0,0 @@
1
- import { Password, User } from 'studiocms:auth/lib';
2
- import { apiResponseLogger } from 'studiocms:logger';
3
- import { Notifications } from 'studiocms:notifier';
4
- import { SDKCore } from 'studiocms:sdk';
5
- import { UserPermissionLevel } from '@withstudiocms/auth-kit/types';
6
- import {
7
- AllResponse,
8
- createEffectAPIRoutes,
9
- createJsonResponse,
10
- genLogger,
11
- OptionsResponse,
12
- parseAPIContextJson,
13
- } from '@withstudiocms/effect';
14
- import { z } from 'astro/zod';
15
- import { Effect, Schema } from 'effect';
16
- import type { EndpointRoute } from './../../../../../../utils/rest-router.js';
17
- import { verifyAuthTokenFromHeader } from '../../../utils/auth-token.js';
18
-
19
- export class IndexJSONData extends Schema.Class<IndexJSONData>('IndexJSONData')({
20
- username: Schema.Union(Schema.String, Schema.Undefined),
21
- password: Schema.Union(Schema.String, Schema.Undefined),
22
- email: Schema.Union(Schema.String, Schema.Undefined),
23
- displayname: Schema.Union(Schema.String, Schema.Undefined),
24
- rank: Schema.Union(
25
- Schema.Literal('owner'),
26
- Schema.Literal('admin'),
27
- Schema.Literal('editor'),
28
- Schema.Literal('visitor'),
29
- Schema.Undefined
30
- ),
31
- }) {}
32
-
33
- type PermissionRank = 'visitor' | 'editor' | 'admin' | 'owner' | 'unknown';
34
-
35
- export class IdJSONData extends Schema.Class<IdJSONData>('IdJSONData')({
36
- rank: Schema.Union(
37
- Schema.Literal('owner'),
38
- Schema.Literal('admin'),
39
- Schema.Literal('editor'),
40
- Schema.Literal('visitor'),
41
- Schema.Literal('unknown')
42
- ),
43
- }) {}
44
-
45
- export const usersRouter: EndpointRoute = {
46
- __idType: 'string',
47
- __index: createEffectAPIRoutes(
48
- {
49
- GET: (ctx) =>
50
- genLogger('studioCMS:rest:v1:users:GET')(function* () {
51
- const [sdk, user] = yield* Effect.all([SDKCore, verifyAuthTokenFromHeader(ctx)]);
52
-
53
- if (user instanceof Response) {
54
- return user;
55
- }
56
-
57
- const { rank } = user;
58
-
59
- if (rank !== 'owner' && rank !== 'admin') {
60
- return apiResponseLogger(401, 'Unauthorized');
61
- }
62
-
63
- const users = yield* sdk.GET.users.all();
64
-
65
- let data = users.map(
66
- ({
67
- avatar,
68
- createdAt,
69
- email,
70
- id,
71
- name,
72
- permissionsData,
73
- updatedAt,
74
- url,
75
- username,
76
- }) => ({
77
- avatar,
78
- createdAt,
79
- email,
80
- id,
81
- name,
82
- rank: permissionsData?.rank ?? 'unknown',
83
- updatedAt,
84
- url,
85
- username,
86
- })
87
- );
88
-
89
- if (rank !== 'owner') {
90
- data = data.filter((user) => user.rank !== 'owner');
91
- }
92
-
93
- const searchParams = ctx.url.searchParams;
94
-
95
- const rankFilter = searchParams.get('rank');
96
- const usernameFilter = searchParams.get('username');
97
- const nameFilter = searchParams.get('name');
98
- const usernameFilterLower = usernameFilter?.toLowerCase();
99
- const nameFilterLower = nameFilter?.toLowerCase();
100
-
101
- let filteredData = data;
102
-
103
- if (rankFilter) {
104
- filteredData = filteredData.filter((u) => u.rank === rankFilter);
105
- }
106
-
107
- if (usernameFilterLower) {
108
- filteredData = filteredData.filter((u) =>
109
- (u.username ?? '').toLowerCase().includes(usernameFilterLower)
110
- );
111
- }
112
-
113
- if (nameFilterLower) {
114
- filteredData = filteredData.filter((u) =>
115
- (u.name ?? '').toLowerCase().includes(nameFilterLower)
116
- );
117
- }
118
-
119
- return createJsonResponse(filteredData);
120
- }),
121
- POST: (ctx) =>
122
- genLogger('studioCMS:rest:v1:users:POST')(function* () {
123
- const [sdk, user, userUtils, passwordUtils, notifier] = yield* Effect.all([
124
- SDKCore,
125
- verifyAuthTokenFromHeader(ctx),
126
- User,
127
- Password,
128
- Notifications,
129
- ]);
130
-
131
- if (user instanceof Response) {
132
- return user;
133
- }
134
-
135
- const { rank } = user;
136
-
137
- if (rank !== 'owner' && rank !== 'admin') {
138
- return apiResponseLogger(401, 'Unauthorized');
139
- }
140
-
141
- let {
142
- username,
143
- password,
144
- email,
145
- displayname,
146
- rank: newUserRank,
147
- } = yield* parseAPIContextJson(ctx, IndexJSONData);
148
-
149
- if (!username) {
150
- return apiResponseLogger(400, 'Missing field: Username is required');
151
- }
152
-
153
- // Only owners can assign the 'owner' rank
154
- if (newUserRank === 'owner' && rank !== 'owner') {
155
- return apiResponseLogger(401, 'Unauthorized');
156
- }
157
-
158
- if (!password) {
159
- password = yield* sdk.UTIL.Generators.generateRandomPassword(12);
160
- }
161
-
162
- if (!email) {
163
- return apiResponseLogger(400, 'Missing field: Email is required');
164
- }
165
-
166
- if (!displayname) {
167
- return apiResponseLogger(400, 'Missing field: Display name is required');
168
- }
169
-
170
- if (!newUserRank) {
171
- return apiResponseLogger(400, 'Missing field: Rank is required');
172
- }
173
- // Prevent privilege escalation: admins cannot assign owner rank
174
- if (rank === 'admin' && newUserRank === 'owner') {
175
- return apiResponseLogger(
176
- 403,
177
- 'Forbidden: insufficient permission to assign owner rank'
178
- );
179
- }
180
-
181
- // If the email is invalid, return an error
182
- const checkEmail = z.coerce
183
- .string()
184
- .email({ message: 'Email address is invalid' })
185
- .safeParse(email);
186
- if (!checkEmail.success) {
187
- return apiResponseLogger(400, `Invalid email: ${checkEmail.error.message}`);
188
- }
189
-
190
- const [verifyUsernameResponse, verifyPasswordResponse, { usernameSearch, emailSearch }] =
191
- yield* Effect.all([
192
- userUtils.verifyUsernameInput(username),
193
- passwordUtils.verifyPasswordStrength(password),
194
- sdk.AUTH.user.searchUsersForUsernameOrEmail(username, checkEmail.data),
195
- ]);
196
-
197
- // If the username is invalid, return an error
198
- if (verifyUsernameResponse !== true) {
199
- return apiResponseLogger(400, verifyUsernameResponse);
200
- }
201
-
202
- // If the password is invalid, return an error(password);
203
- if (verifyPasswordResponse !== true) {
204
- return apiResponseLogger(400, verifyPasswordResponse);
205
- }
206
-
207
- if (usernameSearch.length > 0) {
208
- return apiResponseLogger(400, 'Invalid username: Username is already in use');
209
- }
210
- if (emailSearch.length > 0) {
211
- return apiResponseLogger(400, 'Invalid email: Email is already in use');
212
- }
213
-
214
- // Create a new user
215
- const newUser = yield* userUtils.createLocalUser(
216
- displayname,
217
- username,
218
- checkEmail.data,
219
- password
220
- );
221
- const updateRank = yield* sdk.UPDATE.permissions({
222
- user: newUser.id,
223
- rank: newUserRank,
224
- });
225
- yield* notifier.sendAdminNotification('new_user', newUser.username);
226
- return apiResponseLogger(
227
- 200,
228
- JSON.stringify({
229
- username,
230
- email: checkEmail.data,
231
- displayname,
232
- rank: updateRank.rank,
233
- password,
234
- })
235
- );
236
- }).pipe(Notifications.Provide),
237
- OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['GET', 'POST'] })),
238
- ALL: () => Effect.try(() => AllResponse()),
239
- },
240
- {
241
- cors: { methods: ['GET', 'POST', 'OPTIONS'] },
242
- onError: (error) => {
243
- console.error('API Error:', error);
244
- return createJsonResponse({ error: 'Something went wrong' }, { status: 500 });
245
- },
246
- }
247
- ),
248
- id: (id: string) =>
249
- createEffectAPIRoutes(
250
- {
251
- GET: (ctx) =>
252
- genLogger('studioCMS:rest:v1:users:[id]:GET')(function* () {
253
- const [sdk, user, userUtils] = yield* Effect.all([
254
- SDKCore,
255
- verifyAuthTokenFromHeader(ctx),
256
- User,
257
- ]);
258
-
259
- if (user instanceof Response) {
260
- return user;
261
- }
262
-
263
- const { rank } = user;
264
-
265
- if (rank !== 'owner' && rank !== 'admin') {
266
- return apiResponseLogger(401, 'Unauthorized');
267
- }
268
-
269
- const existingUser = yield* sdk.GET.users.byId(id);
270
-
271
- if (!existingUser) {
272
- return apiResponseLogger(404, 'User not found');
273
- }
274
-
275
- const { avatar, createdAt, email, name, permissionsData, updatedAt, url, username } =
276
- existingUser;
277
-
278
- const existingUserRank = (permissionsData?.rank ?? 'visitor') as PermissionRank;
279
-
280
- const data = {
281
- avatar,
282
- createdAt,
283
- email,
284
- id,
285
- name,
286
- rank: existingUserRank,
287
- updatedAt,
288
- url,
289
- username,
290
- };
291
-
292
- const loggedInUser = yield* sdk.GET.users.byId(user.userId);
293
-
294
- if (!loggedInUser || loggedInUser === undefined) {
295
- return apiResponseLogger(400, 'User Error');
296
- }
297
-
298
- const permissionLevelInput = {
299
- isLoggedIn: true,
300
- user: loggedInUser,
301
- permissionLevel: (loggedInUser.permissionsData?.rank ?? 'visitor') as PermissionRank,
302
- };
303
-
304
- const userPermissionLevel =
305
- yield* userUtils.getUserPermissionLevel(permissionLevelInput);
306
-
307
- const requiredPerms = () => {
308
- switch (existingUserRank) {
309
- case 'owner':
310
- return UserPermissionLevel.owner;
311
- case 'admin':
312
- return UserPermissionLevel.admin;
313
- case 'editor':
314
- return UserPermissionLevel.editor;
315
- case 'visitor':
316
- return UserPermissionLevel.visitor;
317
- default:
318
- return UserPermissionLevel.unknown;
319
- }
320
- };
321
-
322
- const isAllowed = userPermissionLevel > requiredPerms();
323
-
324
- if (!isAllowed) {
325
- return apiResponseLogger(401, 'Unauthorized');
326
- }
327
-
328
- return createJsonResponse(data);
329
- }),
330
- PATCH: (ctx) =>
331
- genLogger('studioCMS:rest:v1:users:[id]:PATCH')(function* () {
332
- const [sdk, user, userUtils, notifier] = yield* Effect.all([
333
- SDKCore,
334
- verifyAuthTokenFromHeader(ctx),
335
- User,
336
- Notifications,
337
- ]);
338
-
339
- if (user instanceof Response) {
340
- return user;
341
- }
342
-
343
- const { rank } = user;
344
-
345
- if (rank !== 'owner' && rank !== 'admin') {
346
- return apiResponseLogger(401, 'Unauthorized');
347
- }
348
-
349
- const existingUser = yield* sdk.GET.users.byId(id);
350
-
351
- if (!existingUser) {
352
- return apiResponseLogger(400, 'User not found');
353
- }
354
-
355
- const { permissionsData } = existingUser;
356
-
357
- const existingUserRank = (permissionsData?.rank ?? 'visitor') as PermissionRank;
358
-
359
- const loggedInUser = yield* sdk.GET.users.byId(user.userId);
360
-
361
- if (!loggedInUser || loggedInUser === undefined) {
362
- return apiResponseLogger(400, 'User Error');
363
- }
364
-
365
- const permissionLevelInput = {
366
- isLoggedIn: true,
367
- user: loggedInUser,
368
- permissionLevel: (loggedInUser.permissionsData?.rank ?? 'visitor') as PermissionRank,
369
- };
370
-
371
- const userPermissionLevel =
372
- yield* userUtils.getUserPermissionLevel(permissionLevelInput);
373
-
374
- const requiredPerms = () => {
375
- switch (existingUserRank) {
376
- case 'owner':
377
- return UserPermissionLevel.owner;
378
- case 'admin':
379
- return UserPermissionLevel.admin;
380
- case 'editor':
381
- return UserPermissionLevel.editor;
382
- case 'visitor':
383
- return UserPermissionLevel.visitor;
384
- default:
385
- return UserPermissionLevel.unknown;
386
- }
387
- };
388
-
389
- const isAllowed = userPermissionLevel > requiredPerms();
390
-
391
- if (!isAllowed) {
392
- return apiResponseLogger(401, 'Unauthorized');
393
- }
394
-
395
- const { rank: newRank } = yield* parseAPIContextJson(ctx, IdJSONData);
396
-
397
- if (!newRank) {
398
- return apiResponseLogger(400, 'Missing field: Rank is required');
399
- }
400
-
401
- // Prevent privilege escalation: actor cannot assign a rank >= their own
402
- const requiredPermsForNewRank = (() => {
403
- switch (newRank) {
404
- case 'owner':
405
- return UserPermissionLevel.owner;
406
- case 'admin':
407
- return UserPermissionLevel.admin;
408
- case 'editor':
409
- return UserPermissionLevel.editor;
410
- case 'visitor':
411
- return UserPermissionLevel.visitor;
412
- default:
413
- return UserPermissionLevel.unknown;
414
- }
415
- })();
416
- if (userPermissionLevel <= requiredPermsForNewRank) {
417
- return apiResponseLogger(403, 'Forbidden');
418
- }
419
-
420
- const updateRank = yield* sdk.UPDATE.permissions({
421
- user: id,
422
- rank: newRank,
423
- });
424
- if (!updateRank) {
425
- return apiResponseLogger(400, 'Failed to update rank');
426
- }
427
- const updatedUser = yield* sdk.GET.users.byId(id);
428
- if (!updatedUser) {
429
- return apiResponseLogger(400, 'Failed to get updated user');
430
- }
431
- const {
432
- avatar,
433
- createdAt,
434
- email,
435
- name,
436
- permissionsData: newPermissionsData,
437
- updatedAt,
438
- url,
439
- username,
440
- } = updatedUser;
441
- const updatedUserRank = (newPermissionsData?.rank ?? 'unknown') as PermissionRank;
442
- const data = {
443
- avatar,
444
- createdAt,
445
- email,
446
- id,
447
- name,
448
- rank: updatedUserRank,
449
- updatedAt,
450
- url,
451
- username,
452
- };
453
- yield* notifier.sendUserNotification('account_updated', id);
454
- yield* notifier.sendAdminNotification('user_updated', username);
455
-
456
- return createJsonResponse(data);
457
- }).pipe(Notifications.Provide),
458
- DELETE: (ctx) =>
459
- genLogger('studioCMS:rest:v1:users:[id]:DELETE')(function* () {
460
- const [sdk, user, userUtils, notifier] = yield* Effect.all([
461
- SDKCore,
462
- verifyAuthTokenFromHeader(ctx),
463
- User,
464
- Notifications,
465
- ]);
466
-
467
- if (user instanceof Response) {
468
- return user;
469
- }
470
-
471
- const { rank } = user;
472
-
473
- if (rank !== 'owner' && rank !== 'admin') {
474
- return apiResponseLogger(401, 'Unauthorized');
475
- }
476
-
477
- if (id === user.userId) {
478
- return apiResponseLogger(400, 'Cannot delete your own account');
479
- }
480
-
481
- const existingUser = yield* sdk.GET.users.byId(id);
482
-
483
- if (!existingUser) {
484
- return apiResponseLogger(400, 'User not found');
485
- }
486
-
487
- const { permissionsData } = existingUser;
488
-
489
- const existingUserRank = (permissionsData?.rank ?? 'visitor') as PermissionRank;
490
-
491
- const loggedInUser = yield* sdk.GET.users.byId(user.userId);
492
-
493
- if (!loggedInUser || loggedInUser === undefined) {
494
- return apiResponseLogger(400, 'User Error');
495
- }
496
-
497
- const permissionLevelInput = {
498
- isLoggedIn: true,
499
- user: loggedInUser,
500
- permissionLevel: (loggedInUser.permissionsData?.rank ?? 'visitor') as PermissionRank,
501
- };
502
-
503
- const userPermissionLevel =
504
- yield* userUtils.getUserPermissionLevel(permissionLevelInput);
505
-
506
- const requiredPerms = () => {
507
- switch (existingUserRank) {
508
- case 'owner':
509
- return UserPermissionLevel.owner;
510
- case 'admin':
511
- return UserPermissionLevel.admin;
512
- case 'editor':
513
- return UserPermissionLevel.editor;
514
- case 'visitor':
515
- return UserPermissionLevel.visitor;
516
- default:
517
- return UserPermissionLevel.unknown;
518
- }
519
- };
520
-
521
- const isAllowed = userPermissionLevel > requiredPerms();
522
-
523
- if (!isAllowed) {
524
- return apiResponseLogger(401, 'Unauthorized');
525
- }
526
-
527
- const response = yield* sdk.DELETE.user(id);
528
-
529
- if (!response) {
530
- return apiResponseLogger(400, 'Failed to delete user');
531
- }
532
-
533
- if (response.status === 'error') {
534
- return apiResponseLogger(400, response.message);
535
- }
536
-
537
- yield* notifier.sendAdminNotification('user_deleted', existingUser.username);
538
-
539
- return apiResponseLogger(200, response.message);
540
- }).pipe(Notifications.Provide),
541
- OPTIONS: () =>
542
- Effect.try(() => OptionsResponse({ allowedMethods: ['GET', 'PATCH', 'DELETE'] })),
543
- ALL: () => Effect.try(() => AllResponse()),
544
- },
545
- {
546
- cors: { methods: ['GET', 'PATCH', 'DELETE', 'OPTIONS'] },
547
- onError: (error) => {
548
- console.error('API Error:', error);
549
- return createJsonResponse({ error: 'Something went wrong' }, { status: 500 });
550
- },
551
- }
552
- ),
553
- };
@@ -1,19 +0,0 @@
1
- import { Schema } from 'effect';
2
- import { createRestRouter, type RouteRegistry } from '../../../../../../utils/rest-router.js';
3
- import { categoriesRouter } from './_routes/categories.js';
4
- import { foldersRouter } from './_routes/folders.js';
5
- import { pagesRouter } from './_routes/pages.js';
6
- import { tagsRouter } from './_routes/tags.js';
7
-
8
- const registry: RouteRegistry = {
9
- categories: categoriesRouter,
10
- tags: tagsRouter,
11
- folders: foldersRouter,
12
- pages: pagesRouter,
13
- };
14
-
15
- export const ALL = createRestRouter(
16
- 'studiocms:rest:v1:public',
17
- Schema.Literal('categories', 'tags', 'folders', 'pages'),
18
- registry
19
- );
@@ -1,74 +0,0 @@
1
- import { SDKCore } from 'studiocms:sdk';
2
- import {
3
- AllResponse,
4
- createEffectAPIRoutes,
5
- createJsonResponse,
6
- Effect,
7
- genLogger,
8
- OptionsResponse,
9
- } from '@withstudiocms/effect';
10
- import type { EndpointRoute } from '../../../../../../../utils/rest-router.js';
11
-
12
- export const categoriesRouter: EndpointRoute = {
13
- __idType: 'number',
14
- __index: createEffectAPIRoutes(
15
- {
16
- GET: (ctx) =>
17
- genLogger('studiocms:rest:v1:public:categories:GET')(function* () {
18
- const sdk = yield* SDKCore;
19
-
20
- const searchParams = ctx.url.searchParams;
21
- const folderNameFilter = searchParams.get('name');
22
- const folderParentFilter = searchParams.get('parent');
23
-
24
- let categories = yield* sdk.GET.categories.getAll();
25
-
26
- if (folderNameFilter) {
27
- categories = categories.filter((category) => category.name.includes(folderNameFilter));
28
- }
29
-
30
- if (folderParentFilter) {
31
- categories = categories.filter(
32
- (category) => category.parent === Number.parseInt(folderParentFilter, 10)
33
- );
34
- }
35
-
36
- return createJsonResponse(categories);
37
- }),
38
- OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['GET'] })),
39
- ALL: () => Effect.try(() => AllResponse()),
40
- },
41
- {
42
- cors: { methods: ['GET', 'OPTIONS'] },
43
- onError: (error) => {
44
- console.error('API Error:', error);
45
- return createJsonResponse({ error: 'Something went wrong' }, { status: 500 });
46
- },
47
- }
48
- ),
49
- id: (id: number) =>
50
- createEffectAPIRoutes(
51
- {
52
- GET: () =>
53
- genLogger(`studiocms:rest:v1:public:categories:${id}:GET`)(function* () {
54
- const sdk = yield* SDKCore;
55
- const category = yield* sdk.GET.categories.byId(id);
56
-
57
- if (!category) {
58
- return createJsonResponse({ error: 'Category not found' }, { status: 404 });
59
- }
60
-
61
- return createJsonResponse(category);
62
- }),
63
- OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['GET'] })),
64
- ALL: () => Effect.try(() => AllResponse()),
65
- },
66
- {
67
- cors: { methods: ['GET', 'OPTIONS'] },
68
- onError: (error) => {
69
- console.error('API Error:', error);
70
- return createJsonResponse({ error: 'Something went wrong' }, { status: 500 });
71
- },
72
- }
73
- ),
74
- };