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
@@ -0,0 +1,80 @@
1
+ import { developerConfig } from 'studiocms:config';
2
+ import pluginList from 'studiocms:plugins';
3
+ import { settingsEndpoints } from 'studiocms:plugins/endpoints';
4
+ import routeConfig from 'virtual:studiocms/route-config';
5
+ import { HttpApiBuilder } from '@effect/platform';
6
+ import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
7
+ import { AstroAPIContext, CurrentUser } from '@withstudiocms/api-spec/astro-context';
8
+ import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
9
+ import { Effect, pipe } from 'effect';
10
+ import { webHandlerToEffectHttpHandler } from 'effectify/webHandler';
11
+
12
+ /**
13
+ * Check if the Dashboard API is enabled in the route configuration.
14
+ */
15
+ const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
16
+
17
+ /**
18
+ * Plugin Handlers for the Dashboard API
19
+ */
20
+ export const PluginHandlers = HttpApiBuilder.group(
21
+ StudioCMSDashboardApiSpec,
22
+ 'plugins',
23
+ (handlers) =>
24
+ handlers.handleRaw(
25
+ 'savePluginSettings',
26
+ Effect.fn(
27
+ function* ({ path: { plugin } }) {
28
+ if (!dashboardAPIEnabled) {
29
+ return yield* new DashboardAPIError({
30
+ error: 'Dashboard API is disabled',
31
+ });
32
+ }
33
+
34
+ if (developerConfig.demoMode !== false) {
35
+ return yield* new DashboardAPIError({
36
+ error: 'Demo mode is enabled, this action is not allowed.',
37
+ });
38
+ }
39
+
40
+ const [userData, ctx] = yield* Effect.all([CurrentUser, AstroAPIContext]);
41
+
42
+ if (!userData.isLoggedIn || !userData.userPermissionLevel.isAdmin) {
43
+ return yield* new DashboardAPIError({ error: 'Unauthorized' });
44
+ }
45
+
46
+ const settingsPage = yield* Effect.try({
47
+ try: () =>
48
+ pipe(
49
+ pluginList.filter(({ settingsPage }) => !!settingsPage),
50
+ (p) => p.find(({ identifier }) => identifier === plugin),
51
+ (p) => {
52
+ if (!p) {
53
+ throw new DashboardAPIError({ error: 'Plugin not found' });
54
+ }
55
+ const exists = settingsEndpoints.find(({ identifier }) => identifier === plugin);
56
+ if (!exists) {
57
+ throw new DashboardAPIError({ error: 'Plugin does not have a settings page' });
58
+ }
59
+ return exists;
60
+ }
61
+ ),
62
+ catch: (cause) =>
63
+ new DashboardAPIError({ error: (cause as Error).message || 'Internal Server Error' }),
64
+ });
65
+
66
+ if (!settingsPage.onSave) {
67
+ return new DashboardAPIError({ error: 'Plugin does not have a settings page' });
68
+ }
69
+
70
+ return yield* webHandlerToEffectHttpHandler(
71
+ settingsPage.onSave(ctx) as unknown as (request: Request) => Promise<Response>
72
+ );
73
+ },
74
+ Effect.catchTags({
75
+ 'effectify/webHandler.WebHandlerError': (cause) =>
76
+ new DashboardAPIError({ error: cause.message || 'Internal Server Error' }),
77
+ })
78
+ )
79
+ )
80
+ );
@@ -0,0 +1,275 @@
1
+ /** biome-ignore-all lint/style/noNonNullAssertion: This is fine */
2
+ import { Password, User } from 'studiocms:auth/lib';
3
+ import { developerConfig } from 'studiocms:config';
4
+ import { Notifications } from 'studiocms:notifier';
5
+ import { SDKCore } from 'studiocms:sdk';
6
+ import routeConfig from 'virtual:studiocms/route-config';
7
+ import { HttpApiBuilder } from '@effect/platform';
8
+ import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
9
+ import { CurrentUser } from '@withstudiocms/api-spec/astro-context';
10
+ import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
11
+ import { Effect } from 'effect';
12
+ import { isValidEmail } from '#schemas/external-schemas';
13
+ import { sharedDBErrors, sharedNotifierErrors } from './_shared.js';
14
+
15
+ /**
16
+ * Check if the Dashboard API is enabled in the route configuration.
17
+ */
18
+ const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
19
+
20
+ /**
21
+ * Profile Handlers for the Dashboard API
22
+ */
23
+ export const ProfileHandlers = HttpApiBuilder.group(
24
+ StudioCMSDashboardApiSpec,
25
+ 'profile',
26
+ (handlers) =>
27
+ handlers.handle(
28
+ 'updateUserProfile',
29
+ Effect.fn(
30
+ function* ({ payload }) {
31
+ if (!dashboardAPIEnabled) {
32
+ return yield* new DashboardAPIError({
33
+ error: 'Dashboard API is disabled',
34
+ });
35
+ }
36
+
37
+ if (developerConfig.demoMode !== false) {
38
+ return yield* new DashboardAPIError({
39
+ error: 'Demo mode is enabled, this action is not allowed.',
40
+ });
41
+ }
42
+
43
+ const [sdk, userData, notifier, pass, userHelper] = yield* Effect.all([
44
+ SDKCore,
45
+ CurrentUser,
46
+ Notifications,
47
+ Password,
48
+ User.pipe(
49
+ Effect.catchAll(() => new DashboardAPIError({ error: 'Failed to access user data' }))
50
+ ),
51
+ ]);
52
+
53
+ if (!userData.isLoggedIn || !userData.userPermissionLevel.isVisitor) {
54
+ return yield* new DashboardAPIError({ error: 'Unauthorized' });
55
+ }
56
+
57
+ switch (payload.mode) {
58
+ case 'basic': {
59
+ const data = payload.data;
60
+
61
+ if (!data.name || data.name.trim() === '') {
62
+ return yield* new DashboardAPIError({ error: 'Name cannot be empty' });
63
+ }
64
+
65
+ if (!data.email || data.email.trim() === '') {
66
+ return yield* new DashboardAPIError({ error: 'Email cannot be empty' });
67
+ }
68
+
69
+ if (!data.username || data.username.trim() === '') {
70
+ return yield* new DashboardAPIError({ error: 'Username cannot be empty' });
71
+ }
72
+
73
+ const verifyUsernameResponse = yield* userHelper
74
+ .verifyUsernameInput(data.username)
75
+ .pipe(
76
+ Effect.catchAll(
77
+ (err) =>
78
+ new DashboardAPIError({
79
+ error: `Failed to verify username: ${(err as Error).message}`,
80
+ })
81
+ )
82
+ );
83
+
84
+ if (verifyUsernameResponse !== true) {
85
+ return yield* new DashboardAPIError({ error: verifyUsernameResponse });
86
+ }
87
+
88
+ const checkEmail = isValidEmail(data.email);
89
+
90
+ if (!checkEmail.success) {
91
+ return yield* new DashboardAPIError({
92
+ error: `Invalid email address: ${checkEmail.error.message}`,
93
+ });
94
+ }
95
+
96
+ const { usernameSearch, emailSearch } =
97
+ yield* sdk.AUTH.user.searchUsersForUsernameOrEmail(data.username, checkEmail.data);
98
+
99
+ if (userData.user?.username !== data.username && usernameSearch.length > 0) {
100
+ return yield* new DashboardAPIError({ error: 'Username is already taken' });
101
+ }
102
+
103
+ if (userData.user?.email !== data.email && emailSearch.length > 0) {
104
+ return yield* new DashboardAPIError({ error: 'Email is already taken' });
105
+ }
106
+
107
+ yield* sdk.AUTH.user.update({
108
+ userId: userData.user!.id,
109
+ userData: { ...data, id: userData.user!.id },
110
+ });
111
+
112
+ return {
113
+ message: 'Profile updated successfully',
114
+ };
115
+ }
116
+ case 'password': {
117
+ const data = payload.data;
118
+
119
+ const { currentPassword, newPassword, confirmNewPassword } = data;
120
+
121
+ if (!currentPassword && userData.user?.password) {
122
+ return yield* new DashboardAPIError({ error: 'Current password is required' });
123
+ }
124
+
125
+ if (!newPassword) {
126
+ return yield* new DashboardAPIError({ error: 'New password is required' });
127
+ }
128
+
129
+ if (currentPassword && userData.user?.password) {
130
+ const isValid = yield* pass
131
+ .verifyPasswordHash(userData.user.password, currentPassword)
132
+ .pipe(
133
+ Effect.catchAll(
134
+ (err) =>
135
+ new DashboardAPIError({
136
+ error: `Failed to verify current password: ${(err as Error).message}`,
137
+ })
138
+ )
139
+ );
140
+ if (!isValid) {
141
+ return yield* new DashboardAPIError({ error: 'Current password is incorrect' });
142
+ }
143
+ }
144
+
145
+ if (!confirmNewPassword) {
146
+ return yield* new DashboardAPIError({ error: 'Please confirm the new password' });
147
+ }
148
+
149
+ if (newPassword !== confirmNewPassword) {
150
+ return yield* new DashboardAPIError({
151
+ error: 'New password and confirmation do not match',
152
+ });
153
+ }
154
+
155
+ const verifyPasswordResponse = yield* pass.verifyPasswordStrength(newPassword).pipe(
156
+ Effect.catchAll(
157
+ (err) =>
158
+ new DashboardAPIError({
159
+ error: `Failed to verify password strength: ${(err as Error).message}`,
160
+ })
161
+ )
162
+ );
163
+
164
+ if (verifyPasswordResponse !== true) {
165
+ return yield* new DashboardAPIError({ error: verifyPasswordResponse });
166
+ }
167
+
168
+ const userUpdate = {
169
+ password: yield* pass.hashPassword(newPassword).pipe(
170
+ Effect.catchAll(
171
+ (err) =>
172
+ new DashboardAPIError({
173
+ error: `Failed to hash new password: ${(err as Error).message}`,
174
+ })
175
+ )
176
+ ),
177
+ };
178
+
179
+ if (userData.user) {
180
+ yield* sdk.AUTH.user.update({
181
+ userId: userData.user.id,
182
+ userData: {
183
+ id: userData.user.id,
184
+ name: userData.user.name,
185
+ username: userData.user.username,
186
+ updatedAt: new Date().toISOString(),
187
+ createdAt: undefined,
188
+ emailVerified: userData.user.emailVerified,
189
+ ...userUpdate,
190
+ },
191
+ });
192
+
193
+ yield* Effect.all([
194
+ notifier.sendUserNotification('account_updated', userData.user.id),
195
+ notifier.sendAdminNotification('user_updated', userData.user.username),
196
+ ]).pipe(
197
+ Effect.catchAll(
198
+ (err) =>
199
+ new DashboardAPIError({
200
+ error: `Failed to send notifications: ${(err as Error).message}`,
201
+ })
202
+ )
203
+ );
204
+ }
205
+
206
+ return {
207
+ message: 'Password updated successfully',
208
+ };
209
+ }
210
+ case 'avatar': {
211
+ if (!userData.user?.email) {
212
+ return yield* new DashboardAPIError({
213
+ error: 'User email is required to generate avatar',
214
+ });
215
+ }
216
+
217
+ if (userData.user)
218
+ yield* userHelper.createUserAvatar(userData.user.email).pipe(
219
+ Effect.flatMap((newAvatar) =>
220
+ Effect.all([
221
+ sdk.AUTH.user.update({
222
+ userId: userData.user!.id,
223
+ userData: {
224
+ id: userData.user!.id,
225
+ name: userData.user!.name,
226
+ username: userData.user!.username,
227
+ updatedAt: new Date().toISOString(),
228
+ emailVerified: userData.user!.emailVerified,
229
+ createdAt: undefined,
230
+ avatar: newAvatar,
231
+ },
232
+ }),
233
+ notifier.sendUserNotification('account_updated', userData.user!.id).pipe(
234
+ Effect.catchAll(
235
+ (err) =>
236
+ new DashboardAPIError({
237
+ error: `Failed to send user notification: ${(err as Error).message}`,
238
+ })
239
+ )
240
+ ),
241
+ notifier.sendAdminNotification('user_updated', userData.user!.username).pipe(
242
+ Effect.catchAll(
243
+ (err) =>
244
+ new DashboardAPIError({
245
+ error: `Failed to send admin notification: ${(err as Error).message}`,
246
+ })
247
+ )
248
+ ),
249
+ ])
250
+ ),
251
+ Effect.catchAll(
252
+ (err) =>
253
+ new DashboardAPIError({
254
+ error: `Failed to create new avatar: ${(err as Error).message}`,
255
+ })
256
+ )
257
+ );
258
+
259
+ return {
260
+ message: 'Avatar updated successfully',
261
+ };
262
+ }
263
+ default: {
264
+ return yield* new DashboardAPIError({ error: 'Invalid update mode' });
265
+ }
266
+ }
267
+ },
268
+ Notifications.Provide,
269
+ Effect.catchTags({
270
+ ...sharedDBErrors,
271
+ ...sharedNotifierErrors,
272
+ })
273
+ )
274
+ )
275
+ );
@@ -0,0 +1,140 @@
1
+ import { Password } from 'studiocms:auth/lib';
2
+ import { developerConfig } from 'studiocms:config';
3
+ import { Notifications } from 'studiocms:notifier';
4
+ import { SDKCore } from 'studiocms:sdk';
5
+ import routeConfig from 'virtual:studiocms/route-config';
6
+ import { HttpApiBuilder } from '@effect/platform';
7
+ import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
8
+ import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
9
+ import { Effect } from 'effect';
10
+ import { sharedDBErrors, sharedNotifierErrors } from './_shared.js';
11
+
12
+ /**
13
+ * Check if the Dashboard API is enabled in the route configuration.
14
+ */
15
+ const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
16
+
17
+ /**
18
+ * Reset Password Handlers for the Dashboard API
19
+ */
20
+ export const ResetPasswordHandlers = HttpApiBuilder.group(
21
+ StudioCMSDashboardApiSpec,
22
+ 'resetPassword',
23
+ (handlers) =>
24
+ handlers.handle(
25
+ 'resetPassword',
26
+ Effect.fn(
27
+ function* ({ payload }) {
28
+ if (!dashboardAPIEnabled) {
29
+ return yield* new DashboardAPIError({
30
+ error: 'Dashboard API is disabled',
31
+ });
32
+ }
33
+
34
+ if (developerConfig.demoMode !== false) {
35
+ return yield* new DashboardAPIError({
36
+ error: 'Demo mode is enabled, this action is not allowed.',
37
+ });
38
+ }
39
+
40
+ const { token, password, confirm_password } = payload;
41
+
42
+ if (password !== confirm_password) {
43
+ return yield* new DashboardAPIError({
44
+ error: 'Passwords do not match',
45
+ });
46
+ }
47
+
48
+ const [sdk, notifier, pass] = yield* Effect.all([SDKCore, Notifications, Password]);
49
+
50
+ const verifyPasswordResponse = yield* pass.verifyPasswordStrength(password).pipe(
51
+ Effect.catchAll((err) => {
52
+ return new DashboardAPIError({
53
+ error: `Password does not meet strength requirements: ${err.message}`,
54
+ });
55
+ })
56
+ );
57
+
58
+ if (verifyPasswordResponse !== true) {
59
+ return yield* new DashboardAPIError({
60
+ error: verifyPasswordResponse,
61
+ });
62
+ }
63
+
64
+ const hashedPassword = yield* pass.hashPassword(password).pipe(
65
+ Effect.catchAll(() => {
66
+ return new DashboardAPIError({
67
+ error: 'Failed to hash password',
68
+ });
69
+ })
70
+ );
71
+
72
+ const isTokenValid = yield* sdk.resetTokenBucket.check(token);
73
+
74
+ if (!isTokenValid) {
75
+ return yield* new DashboardAPIError({
76
+ error: 'Invalid or expired reset token',
77
+ });
78
+ }
79
+
80
+ const tokenInfo = yield* sdk.UTIL.Generators.testToken(token);
81
+
82
+ if (!tokenInfo || !tokenInfo.userId) {
83
+ return yield* new DashboardAPIError({
84
+ error: 'Invalid or expired reset token',
85
+ });
86
+ }
87
+
88
+ const targetUserId = tokenInfo.userId as string;
89
+
90
+ const userUpdate = {
91
+ password: hashedPassword,
92
+ };
93
+
94
+ const userData = yield* sdk.GET.users.byId(targetUserId);
95
+
96
+ if (!userData) {
97
+ return yield* new DashboardAPIError({
98
+ error: 'User not found',
99
+ });
100
+ }
101
+
102
+ yield* sdk.AUTH.user.update({
103
+ userId: targetUserId,
104
+ userData: {
105
+ id: targetUserId,
106
+ name: userData.name,
107
+ username: userData.username,
108
+ updatedAt: new Date().toISOString(),
109
+ createdAt: undefined,
110
+ emailVerified: userData.emailVerified,
111
+ ...userUpdate,
112
+ },
113
+ });
114
+
115
+ yield* sdk.resetTokenBucket.delete(targetUserId);
116
+
117
+ yield* Effect.all([
118
+ notifier.sendUserNotification('account_updated', targetUserId),
119
+ notifier.sendAdminNotification('user_updated', userData.username),
120
+ ]).pipe(
121
+ Effect.catchAll(() => {
122
+ return new DashboardAPIError({
123
+ error: 'Failed to send notifications',
124
+ });
125
+ })
126
+ );
127
+
128
+ return {
129
+ message: 'Password updated successfully',
130
+ };
131
+ },
132
+ Notifications.Provide,
133
+ Effect.catchTags({
134
+ ...sharedDBErrors,
135
+ ...sharedNotifierErrors,
136
+ GeneratorError: () => new DashboardAPIError({ error: 'Invalid or expired reset token' }),
137
+ })
138
+ )
139
+ )
140
+ );
@@ -0,0 +1,63 @@
1
+ import { SDKCore } from 'studiocms:sdk';
2
+ import routeConfig from 'virtual:studiocms/route-config';
3
+ import { HttpApiBuilder } from '@effect/platform';
4
+ import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
5
+ import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
6
+ import { Effect } from 'effect';
7
+ import { sharedDBErrors, sharedPageCollectionErrors } from './_shared.js';
8
+
9
+ /**
10
+ * Check if the Dashboard API is enabled in the route configuration.
11
+ */
12
+ const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
13
+
14
+ /**
15
+ * Search Item type for the searchList handler response.
16
+ */
17
+ type SearchItem = {
18
+ id: string;
19
+ name: string;
20
+ slug?: string;
21
+ type: 'folder' | 'page';
22
+ isDraft?: boolean | null;
23
+ };
24
+
25
+ /**
26
+ * Search Handlers for the Dashboard API
27
+ */
28
+ export const SearchHandlers = HttpApiBuilder.group(
29
+ StudioCMSDashboardApiSpec,
30
+ 'search',
31
+ (handlers) =>
32
+ handlers.handle('searchList', () =>
33
+ !dashboardAPIEnabled
34
+ ? Effect.fail(new DashboardAPIError({ error: 'Dashboard API is disabled' }))
35
+ : SDKCore.pipe(
36
+ Effect.flatMap((sdk) =>
37
+ Effect.all([
38
+ sdk.GET.folderList().pipe(
39
+ Effect.map((f) => f.map(({ id, name }) => ({ id, name, type: 'folder' })))
40
+ ),
41
+ sdk.GET.pages(true).pipe(
42
+ Effect.map((res) =>
43
+ res.map(({ id, title, slug, draft }) => ({
44
+ id,
45
+ name: title,
46
+ slug,
47
+ isDraft: draft,
48
+ type: 'page',
49
+ }))
50
+ )
51
+ ),
52
+ ])
53
+ ),
54
+ Effect.flatMap(([folders, pages]) =>
55
+ Effect.succeed([...folders, ...pages] as SearchItem[])
56
+ ),
57
+ Effect.catchTags({
58
+ ...sharedDBErrors,
59
+ ...sharedPageCollectionErrors,
60
+ })
61
+ )
62
+ )
63
+ );