studiocms 0.3.0 → 0.4.1

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 (263) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/cli/add/index.d.ts +2 -2
  3. package/dist/cli/add/npm-utils.d.ts +6 -6
  4. package/dist/cli/add/tryToInstallPlugins.d.ts +1 -1
  5. package/dist/cli/add/updateStudioCMSConfig.d.ts +1 -1
  6. package/dist/cli/add/validatePlugins.d.ts +1 -1
  7. package/dist/cli/crypto/genJWT/index.d.ts +1 -1
  8. package/dist/cli/crypto/index.d.ts +1 -1
  9. package/dist/cli/init/steps/env.js +12 -2
  10. package/dist/cli/init/steps/next.d.ts +1 -1
  11. package/dist/cli/migrator/index.d.ts +1 -1
  12. package/dist/cli/users/index.d.ts +1 -1
  13. package/dist/cli/users/steps/createUsers.js +3 -3
  14. package/dist/cli/users/steps/next.d.ts +1 -1
  15. package/dist/cli/utils/context.d.ts +1 -1
  16. package/dist/cli/utils/getCliDbClient.d.ts +1 -1
  17. package/dist/cli/utils/intro.d.ts +1 -1
  18. package/dist/cli/utils/loadConfig.d.ts +54 -49
  19. package/dist/cli/utils/loadConfig.js +5 -8
  20. package/dist/cli/utils/user-utils.d.ts +1 -1
  21. package/dist/client/apiClient.d.ts +4923 -0
  22. package/dist/client/apiClient.js +72 -0
  23. package/dist/config.d.ts +1734 -1
  24. package/dist/consts.d.ts +5 -5
  25. package/dist/consts.js +3 -2
  26. package/dist/db/plugins.d.ts +1 -1
  27. package/dist/db/plugins.js +5 -8
  28. package/dist/handlers/frontend/routes.d.ts +4 -18
  29. package/dist/handlers/frontend/routes.js +13 -152
  30. package/dist/handlers/frontend/types.d.ts +1 -1
  31. package/dist/handlers/frontend/utils.js +0 -18
  32. package/dist/handlers/pluginHandler.d.ts +34 -257
  33. package/dist/handlers/pluginHandler.js +92 -46
  34. package/dist/handlers/routeHandler.js +32 -11
  35. package/dist/handlers/setupDbStudio.d.ts +1 -1
  36. package/dist/handlers/storage-manager/core/effectify-astro-context.d.ts +25 -0
  37. package/dist/handlers/storage-manager/core/effectify-astro-context.js +78 -0
  38. package/dist/handlers/storage-manager/no-op.d.ts +2 -2
  39. package/dist/handlers/storage-manager/no-op.js +2 -3
  40. package/dist/index.d.ts +0 -1
  41. package/dist/index.js +9 -5
  42. package/dist/integrations/robots/index.d.ts +2 -2
  43. package/dist/integrations/robots/index.js +1 -3
  44. package/dist/integrations/robots/schema.d.ts +102 -273
  45. package/dist/integrations/robots/schema.js +220 -209
  46. package/dist/plugins/analytics/assets/schemas.d.ts +14 -9
  47. package/dist/plugins/analytics/assets/schemas.js +25 -17
  48. package/dist/plugins/analytics/db-client.d.ts +1 -1
  49. package/dist/plugins/analytics/index.d.ts +823 -3
  50. package/dist/plugins/analytics/index.js +4 -5
  51. package/dist/plugins/analytics/schemas.d.ts +54 -62
  52. package/dist/plugins/analytics/schemas.js +64 -13
  53. package/dist/plugins/analytics/table.d.ts +1 -1
  54. package/dist/plugins.d.ts +0 -1
  55. package/dist/schemas/config/api.d.ts +17 -0
  56. package/dist/schemas/config/api.js +14 -0
  57. package/dist/schemas/config/auth.d.ts +55 -59
  58. package/dist/schemas/config/auth.js +34 -11
  59. package/dist/schemas/config/dashboard.d.ts +43 -79
  60. package/dist/schemas/config/dashboard.js +43 -12
  61. package/dist/schemas/config/db.d.ts +15 -17
  62. package/dist/schemas/config/db.js +13 -5
  63. package/dist/schemas/config/developer.d.ts +33 -45
  64. package/dist/schemas/config/developer.js +22 -5
  65. package/dist/schemas/config/index.d.ts +398 -521
  66. package/dist/schemas/config/index.js +115 -57
  67. package/dist/schemas/config/sdk.d.ts +50 -196
  68. package/dist/schemas/config/sdk.js +61 -73
  69. package/dist/schemas/custom.d.ts +40 -0
  70. package/dist/schemas/custom.js +41 -0
  71. package/dist/schemas/external-schemas.d.ts +171 -0
  72. package/dist/schemas/external-schemas.js +179 -0
  73. package/dist/schemas/index.d.ts +2 -0
  74. package/dist/schemas/index.js +2 -0
  75. package/dist/schemas/plugins/i18n.d.ts +59 -39
  76. package/dist/schemas/plugins/i18n.js +42 -5
  77. package/dist/schemas/plugins/index.d.ts +7126 -10296
  78. package/dist/schemas/plugins/index.js +260 -276
  79. package/dist/schemas/plugins/shared.d.ts +1293 -3718
  80. package/dist/schemas/plugins/shared.js +320 -329
  81. package/dist/test-utils.d.ts +15 -4
  82. package/dist/test-utils.js +27 -11
  83. package/dist/toolbar/db-viewer/db-shared-types.d.ts +6 -6
  84. package/dist/toolbar/db-viewer/viewer.js +20 -21
  85. package/dist/types.d.ts +30 -0
  86. package/dist/utils/effects/smtp.d.ts +1 -1
  87. package/dist/utils/lang-helper.d.ts +10 -2
  88. package/dist/virtual.d.ts +23 -0
  89. package/dist/virtuals/auth/core.d.ts +5 -5
  90. package/dist/virtuals/auth/verify-email.d.ts +6 -6
  91. package/dist/virtuals/components/Generator.astro +2 -2
  92. package/dist/virtuals/components/Renderer.astro +9 -1
  93. package/dist/virtuals/components/renderFn.d.ts +3 -1
  94. package/dist/virtuals/components/renderFn.js +18 -0
  95. package/dist/virtuals/lib/headDefaults.d.ts +4 -2
  96. package/dist/virtuals/lib/headDefaults.js +0 -2
  97. package/dist/virtuals/lib/routeMap.d.ts +0 -12
  98. package/dist/virtuals/lib/routeMap.js +2 -14
  99. package/dist/virtuals/mailer/index.d.ts +3 -3
  100. package/dist/virtuals/notifier/index.d.ts +5 -5
  101. package/dist/virtuals/plugins/dashboard-pages.d.ts +2 -64
  102. package/dist/virtuals/scripts/StorageFileBrowser.d.ts +1 -172
  103. package/dist/virtuals/scripts/StorageFileBrowser.js +216 -119
  104. package/dist/virtuals/template-engine/index.d.ts +4 -4
  105. package/frontend/components/dashboard/configuration/ConfigForm.astro +218 -110
  106. package/frontend/components/dashboard/content-mgmt/ContentSearch.astro +21 -22
  107. package/frontend/components/dashboard/content-mgmt/CreateFolder.astro +66 -54
  108. package/frontend/components/dashboard/content-mgmt/CreatePage.astro +58 -104
  109. package/frontend/components/dashboard/content-mgmt/EditFolder.astro +65 -67
  110. package/frontend/components/dashboard/content-mgmt/EditPage.astro +86 -134
  111. package/frontend/components/dashboard/content-mgmt/InnerSidebarElement.astro +0 -1
  112. package/frontend/components/dashboard/content-mgmt/PageHeader.astro +33 -52
  113. package/frontend/components/dashboard/content-mgmt/PageTypeHandler.astro +2 -2
  114. package/frontend/components/dashboard/profile/APITokens.astro +219 -158
  115. package/frontend/components/dashboard/profile/BasicInfo.astro +165 -106
  116. package/frontend/components/dashboard/profile/Notifications.astro +27 -18
  117. package/frontend/components/dashboard/profile/UpdatePassword.astro +134 -94
  118. package/frontend/components/dashboard/sidebar/VersionCheck.astro +31 -16
  119. package/frontend/components/dashboard/sidebar/VersionCheckChangelog.astro +18 -11
  120. package/frontend/components/dashboard/sidebar-modals/VersionModal.astro +2 -2
  121. package/frontend/components/dashboard/smtp-config/TemplateEditor.astro +14 -14
  122. package/frontend/components/dashboard/taxonomy/InnerSidebarElement.astro +0 -1
  123. package/frontend/components/dashboard/taxonomy/MetaContainer.astro +0 -2
  124. package/frontend/components/dashboard/taxonomy/PageHeader.astro +16 -24
  125. package/frontend/components/dashboard/taxonomy/TaxonomySearch.astro +23 -27
  126. package/frontend/components/dashboard/user-mgmt/InnerSidebarElement.astro +111 -104
  127. package/frontend/components/dashboard/user-mgmt/UserListItem.astro +9 -22
  128. package/frontend/components/dashboard/user-mgmt/UserListItems.astro +18 -0
  129. package/frontend/components/first-time-setup/snippets/{opt2-studiocms.config.diff → studiocms.config.diff} +1 -0
  130. package/frontend/components/shared/Code.astro +1 -4
  131. package/frontend/components/shared/DynamicSettingsRenderer.astro +1 -1
  132. package/frontend/components/shared/SSRUser.astro +2 -4
  133. package/frontend/components/shared/foldertree/FolderTreeNode.astro +0 -6
  134. package/frontend/components/shared/storage-manager/StorageCopyOutput.astro +0 -1
  135. package/frontend/components/shared/taxonomy/TaxonomyTreeNode.astro +0 -6
  136. package/frontend/layouts/DashboardLayout.astro +0 -9
  137. package/frontend/layouts/TaxonomyLayout.astro +0 -1
  138. package/frontend/middleware/index.ts +102 -61
  139. package/frontend/pages/404.astro +0 -1
  140. package/frontend/pages/[dashboard]/[...pluginPage].astro +10 -1
  141. package/frontend/pages/[dashboard]/configuration.astro +10 -1
  142. package/frontend/pages/[dashboard]/content-management/createfolder.astro +10 -1
  143. package/frontend/pages/[dashboard]/content-management/createpage.astro +10 -1
  144. package/frontend/pages/[dashboard]/content-management/diff.astro +39 -14
  145. package/frontend/pages/[dashboard]/content-management/editfolder.astro +10 -1
  146. package/frontend/pages/[dashboard]/content-management/editpage.astro +10 -1
  147. package/frontend/pages/[dashboard]/content-management/index.astro +10 -1
  148. package/frontend/pages/[dashboard]/index.astro +10 -1
  149. package/frontend/pages/[dashboard]/login.astro +86 -25
  150. package/frontend/pages/[dashboard]/password-reset.astro +22 -16
  151. package/frontend/pages/[dashboard]/plugins/[plugin].astro +10 -1
  152. package/frontend/pages/[dashboard]/profile.astro +10 -1
  153. package/frontend/pages/[dashboard]/signup.astro +153 -52
  154. package/frontend/pages/[dashboard]/smtp-configuration.astro +77 -75
  155. package/frontend/pages/[dashboard]/system-management.astro +10 -1
  156. package/frontend/pages/[dashboard]/taxonomy/categories.astro +30 -41
  157. package/frontend/pages/[dashboard]/taxonomy/index.astro +10 -0
  158. package/frontend/pages/[dashboard]/taxonomy/tags.astro +33 -43
  159. package/frontend/pages/[dashboard]/unverified-email.astro +29 -21
  160. package/frontend/pages/[dashboard]/user-management/edit.astro +170 -90
  161. package/frontend/pages/[dashboard]/user-management/index.astro +10 -1
  162. package/frontend/pages/studiocms_api/[...all].ts +106 -0
  163. package/frontend/pages/studiocms_api/_handlers/_utils/auth.ts +26 -0
  164. package/frontend/pages/studiocms_api/_handlers/_utils/changelog.ts +147 -0
  165. package/frontend/pages/studiocms_api/_handlers/_utils/db-studio-driver.ts +46 -0
  166. package/frontend/pages/studiocms_api/_handlers/_utils/parseLogLevel.ts +27 -0
  167. package/frontend/pages/studiocms_api/_handlers/auth/auth.ts +459 -0
  168. package/frontend/pages/studiocms_api/_handlers/auth/index.ts +17 -0
  169. package/frontend/pages/studiocms_api/_handlers/auth/oauth.ts +91 -0
  170. package/frontend/pages/studiocms_api/_handlers/dashboard/_shared.ts +57 -0
  171. package/frontend/pages/studiocms_api/_handlers/dashboard/apiTokens.ts +134 -0
  172. package/frontend/pages/studiocms_api/_handlers/dashboard/config.ts +64 -0
  173. package/frontend/pages/studiocms_api/_handlers/dashboard/content.ts +741 -0
  174. package/frontend/pages/studiocms_api/_handlers/dashboard/create.ts +480 -0
  175. package/frontend/pages/studiocms_api/_handlers/dashboard/emailNotifications.ts +49 -0
  176. package/frontend/pages/studiocms_api/_handlers/dashboard/index.ts +45 -0
  177. package/frontend/pages/studiocms_api/_handlers/dashboard/mailer.ts +136 -0
  178. package/frontend/pages/studiocms_api/_handlers/dashboard/plugins.ts +80 -0
  179. package/frontend/pages/studiocms_api/_handlers/dashboard/profile.ts +275 -0
  180. package/frontend/pages/studiocms_api/_handlers/dashboard/resetPassword.ts +140 -0
  181. package/frontend/pages/studiocms_api/_handlers/dashboard/search.ts +63 -0
  182. package/frontend/pages/studiocms_api/_handlers/dashboard/taxonomy.ts +285 -0
  183. package/frontend/pages/studiocms_api/_handlers/dashboard/templates.ts +75 -0
  184. package/frontend/pages/studiocms_api/_handlers/dashboard/users.ts +312 -0
  185. package/frontend/pages/studiocms_api/_handlers/dashboard/verifyEndpoints.ts +307 -0
  186. package/frontend/pages/studiocms_api/_handlers/integration/dbStudio.ts +98 -0
  187. package/frontend/pages/studiocms_api/_handlers/integration/index.ts +17 -0
  188. package/frontend/pages/studiocms_api/_handlers/integration/storageManager.ts +107 -0
  189. package/frontend/pages/studiocms_api/_handlers/rest-api/index.ts +8 -0
  190. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/_shared.ts +41 -0
  191. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/index.ts +17 -0
  192. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/public.ts +195 -0
  193. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts +1726 -0
  194. package/frontend/pages/studiocms_api/_handlers/sdk.ts +129 -0
  195. package/frontend/pages/studiocms_api/_middleware/astroLocals.ts +36 -0
  196. package/frontend/pages/studiocms_api/_middleware/restApi.ts +56 -0
  197. package/frontend/pages/studiocms_api/integrations/[...all].ts +8 -0
  198. package/frontend/scripts/formdata.ts +219 -0
  199. package/frontend/setup-pages/3-done.astro +4 -20
  200. package/frontend/setup-pages/studiocms_api/dashboard/step-2.ts +3 -6
  201. package/frontend/styles/dashboard-base.css +0 -1
  202. package/frontend/web-vitals/endpoint.ts +2 -1
  203. package/package.json +34 -29
  204. package/dist/global.d.ts +0 -9
  205. package/frontend/components/dashboard/LoginChecker.astro +0 -68
  206. package/frontend/components/dashboard/user-mgmt/RankCheck.astro +0 -57
  207. package/frontend/components/first-time-setup/snippets/opt1-astro.config.diff +0 -14
  208. package/frontend/components/first-time-setup/snippets/opt2-astro.config.diff +0 -9
  209. package/frontend/middleware/_authmap.ts +0 -63
  210. package/frontend/pages/studiocms_api/auth/[path].ts +0 -384
  211. package/frontend/pages/studiocms_api/auth/[provider]/[...id].ts +0 -64
  212. package/frontend/pages/studiocms_api/auth/[provider]/_effects/index.ts +0 -101
  213. package/frontend/pages/studiocms_api/auth/_shared.ts +0 -52
  214. package/frontend/pages/studiocms_api/dashboard/api-tokens.ts +0 -115
  215. package/frontend/pages/studiocms_api/dashboard/config.ts +0 -74
  216. package/frontend/pages/studiocms_api/dashboard/content/diff.ts +0 -73
  217. package/frontend/pages/studiocms_api/dashboard/content/folder.ts +0 -220
  218. package/frontend/pages/studiocms_api/dashboard/content/page.ts +0 -359
  219. package/frontend/pages/studiocms_api/dashboard/create-reset-link.ts +0 -77
  220. package/frontend/pages/studiocms_api/dashboard/create-user-invite.ts +0 -231
  221. package/frontend/pages/studiocms_api/dashboard/create-user.ts +0 -186
  222. package/frontend/pages/studiocms_api/dashboard/email-notification-settings-site.ts +0 -74
  223. package/frontend/pages/studiocms_api/dashboard/mailer/check-email.ts +0 -75
  224. package/frontend/pages/studiocms_api/dashboard/mailer/config.ts +0 -136
  225. package/frontend/pages/studiocms_api/dashboard/plugins/[plugin].ts +0 -80
  226. package/frontend/pages/studiocms_api/dashboard/profile.ts +0 -245
  227. package/frontend/pages/studiocms_api/dashboard/resend-verify-email.ts +0 -77
  228. package/frontend/pages/studiocms_api/dashboard/reset-password.ts +0 -124
  229. package/frontend/pages/studiocms_api/dashboard/search-list.ts +0 -59
  230. package/frontend/pages/studiocms_api/dashboard/taxonomy-search.ts +0 -47
  231. package/frontend/pages/studiocms_api/dashboard/taxonomy.ts +0 -230
  232. package/frontend/pages/studiocms_api/dashboard/templates.ts +0 -74
  233. package/frontend/pages/studiocms_api/dashboard/update-user-notifications.ts +0 -86
  234. package/frontend/pages/studiocms_api/dashboard/users.ts +0 -236
  235. package/frontend/pages/studiocms_api/dashboard/verify-email.ts +0 -83
  236. package/frontend/pages/studiocms_api/dashboard/verify-session.ts +0 -187
  237. package/frontend/pages/studiocms_api/integrations/[type]/[...id].ts +0 -15
  238. package/frontend/pages/studiocms_api/integrations/[type]/_routes/db-studio.ts +0 -173
  239. package/frontend/pages/studiocms_api/integrations/[type]/_routes/storage.ts +0 -88
  240. package/frontend/pages/studiocms_api/partials/editor.astro +0 -74
  241. package/frontend/pages/studiocms_api/partials/render.astro +0 -39
  242. package/frontend/pages/studiocms_api/partials/user-list-items.astro +0 -43
  243. package/frontend/pages/studiocms_api/rest/utils/auth-token.ts +0 -59
  244. package/frontend/pages/studiocms_api/rest/v1/[type]/[...id].ts +0 -23
  245. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/categories.ts +0 -267
  246. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/folders.ts +0 -283
  247. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/pages.ts +0 -505
  248. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/settings.ts +0 -100
  249. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/tags.ts +0 -229
  250. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/users.ts +0 -553
  251. package/frontend/pages/studiocms_api/rest/v1/public/[type]/[...id].ts +0 -19
  252. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/categories.ts +0 -74
  253. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/folders.ts +0 -85
  254. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/pages.ts +0 -103
  255. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/tags.ts +0 -67
  256. package/frontend/pages/studiocms_api/sdk/[...path].ts +0 -97
  257. package/frontend/pages/studiocms_api/sdk/utils/changelog.ts +0 -119
  258. package/frontend/scripts/auth/formListener.ts +0 -81
  259. package/frontend/scripts/formdata-utils.ts +0 -116
  260. package/frontend/utils/build-partial-schema.ts +0 -46
  261. package/frontend/utils/errors.ts +0 -6
  262. package/frontend/utils/param-extractor.ts +0 -83
  263. package/frontend/utils/rest-router.ts +0 -444
@@ -1,359 +0,0 @@
1
- import { apiResponseLogger } from 'studiocms:logger';
2
- import { Notifications } from 'studiocms:notifier';
3
- import plugins from 'studiocms:plugins';
4
- import { apiEndpoints } from 'studiocms:plugins/endpoints';
5
- import { SDKCore } from 'studiocms:sdk';
6
- import type {
7
- CombinedInsertContent,
8
- tsPageContentSelect,
9
- tsPageData,
10
- tsPageDataSelect,
11
- } from 'studiocms:sdk/types';
12
- import {
13
- AllResponse,
14
- createEffectAPIRoutes,
15
- createJsonResponse,
16
- Effect,
17
- genLogger,
18
- OptionsResponse,
19
- readAPIContextJson,
20
- Schema,
21
- } from '@withstudiocms/effect';
22
- import { StudioCMSPageData } from '@withstudiocms/sdk/tables';
23
- import type { PluginAPIRoute } from '#plugins';
24
-
25
- type ApiEndpoints = {
26
- onCreate?: PluginAPIRoute<'onCreate'> | null;
27
- onEdit?: PluginAPIRoute<'onEdit'> | null;
28
- onDelete?: PluginAPIRoute<'onDelete'> | null;
29
- };
30
-
31
- type PageTypeOutput = {
32
- identifier: string;
33
- label: string;
34
- description?: string | undefined;
35
- pageContentComponent?: string | undefined;
36
- apiEndpoint?: string;
37
- apiEndpoints?: ApiEndpoints | undefined;
38
- };
39
-
40
- type UpdatePageData = Partial<tsPageDataSelect>;
41
- type UpdatePageContent = Partial<tsPageContentSelect>;
42
-
43
- const pageTypeOptions = plugins.flatMap(({ pageTypes }) => {
44
- const pageTypeOutput: PageTypeOutput[] = [];
45
-
46
- if (!pageTypes) return pageTypeOutput;
47
-
48
- for (const pageType of pageTypes) {
49
- pageTypeOutput.push({
50
- ...pageType,
51
- apiEndpoints: apiEndpoints.find((endpoint) => endpoint.identifier === pageType.identifier),
52
- });
53
- }
54
-
55
- return pageTypeOutput;
56
- });
57
-
58
- function getPageTypeEndpoints<T extends 'onCreate' | 'onEdit' | 'onDelete'>(pkg: string, type: T) {
59
- const currentPageType = pageTypeOptions.find((pageType) => pageType.identifier === pkg);
60
-
61
- if (!currentPageType) return undefined;
62
-
63
- return currentPageType.apiEndpoints?.[type];
64
- }
65
-
66
- export const { POST, PATCH, DELETE, OPTIONS, ALL } = createEffectAPIRoutes(
67
- {
68
- POST: (ctx) =>
69
- genLogger('studiocms/routes/api/dashboard/content/page.POST')(function* () {
70
- const [sdk, notify] = yield* Effect.all([SDKCore, Notifications]);
71
-
72
- // Get user data
73
- const userData = ctx.locals.StudioCMS.security?.userSessionData;
74
-
75
- // Check if user is logged in
76
- if (!userData?.isLoggedIn) {
77
- return apiResponseLogger(403, 'Unauthorized');
78
- }
79
-
80
- // Check if user has permission
81
- const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isEditor;
82
- if (!isAuthorized) {
83
- return apiResponseLogger(403, 'Unauthorized');
84
- }
85
-
86
- const data = yield* readAPIContextJson<UpdatePageData>(ctx);
87
-
88
- const content = {
89
- id: crypto.randomUUID(),
90
- // content is no longer supported during page creation due to current editor setup
91
- // look into options in the future for how we can do this correctly.
92
- // Requires being able to swap in editors which currently does not work.
93
- content: '',
94
- } as UpdatePageContent;
95
-
96
- if (!data.title) {
97
- return apiResponseLogger(400, 'Invalid form data, title is required');
98
- }
99
-
100
- const dataId = crypto.randomUUID();
101
-
102
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
103
- const apiRoute = getPageTypeEndpoints(data.package!, 'onCreate');
104
-
105
- const pageContent: CombinedInsertContent = {
106
- contentLang: 'default',
107
- content: content.content || '',
108
- };
109
-
110
- const {
111
- title,
112
- slug,
113
- description,
114
- categories,
115
- tags,
116
- augments,
117
- contributorIds,
118
- updatedAt: ___updatedAt,
119
- ...rest
120
- } = data as unknown as tsPageData['Insert']['Type'];
121
-
122
- yield* Effect.logInfo('11111');
123
-
124
- const newData = yield* sdk.POST.page({
125
- pageData: {
126
- ...rest,
127
- id: dataId,
128
- title: title,
129
- slug: slug || title.toLowerCase().replace(/\s/g, '-'),
130
- description: description || '',
131
- authorId: userData.user?.id || '',
132
- updatedAt: new Date().toISOString(),
133
- publishedAt: new Date().toISOString(),
134
- categories: JSON.stringify(categories || []),
135
- tags: JSON.stringify(tags || []),
136
- augments: JSON.stringify(augments || []),
137
- contributorIds: JSON.stringify(contributorIds || []),
138
- contentLang: 'default',
139
- },
140
- pageContent: pageContent,
141
- });
142
- yield* Effect.logInfo('22222');
143
-
144
- if (!newData) {
145
- return apiResponseLogger(500, 'Failed to create page');
146
- }
147
-
148
- if (apiRoute) {
149
- yield* Effect.tryPromise(() => apiRoute({ AstroCtx: ctx, pageData: newData }));
150
- }
151
-
152
- yield* Effect.all([sdk.CLEAR.pages, notify.sendEditorNotification('new_page', data.title)]);
153
-
154
- return apiResponseLogger(200, 'Page created successfully');
155
- }).pipe(Notifications.Provide),
156
- PATCH: (ctx) =>
157
- genLogger('studiocms/routes/api/dashboard/content/page.PATCH')(function* () {
158
- const [sdk, notify] = yield* Effect.all([SDKCore, Notifications]);
159
-
160
- // Get user data
161
- const userData = ctx.locals.StudioCMS.security?.userSessionData;
162
-
163
- // Check if user is logged in
164
- if (!userData?.isLoggedIn) {
165
- return apiResponseLogger(403, 'Unauthorized');
166
- }
167
-
168
- // Check if user has permission
169
- const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isEditor;
170
- if (!isAuthorized) {
171
- return apiResponseLogger(403, 'Unauthorized');
172
- }
173
-
174
- const combinedData = yield* readAPIContextJson<
175
- UpdatePageData & {
176
- contentId: string;
177
- content: string;
178
- pluginFields: Record<string, FormDataEntryValue | null>;
179
- augments?: string[];
180
- }
181
- >(ctx);
182
-
183
- const { contentId, content: incomingContent, pluginFields, ...data } = combinedData;
184
-
185
- if (!data.id) {
186
- return apiResponseLogger(400, 'Invalid form data, id is required');
187
- }
188
-
189
- if (!contentId) {
190
- return apiResponseLogger(400, 'Invalid form data, contentId is required');
191
- }
192
-
193
- const content = {
194
- id: contentId,
195
- contentId: data.id,
196
- content: incomingContent,
197
- contentLang: 'default',
198
- };
199
-
200
- const currentPageData = yield* sdk.GET.page.byId(data.id);
201
-
202
- if (!currentPageData) {
203
- return apiResponseLogger(404, 'Page not found');
204
- }
205
-
206
- const { authorId, contributorIds, defaultContent } = currentPageData;
207
-
208
- let AuthorId = authorId;
209
-
210
- if (!authorId) {
211
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
212
- AuthorId = userData.user!.id;
213
- }
214
-
215
- const ContributorIds = contributorIds || [];
216
-
217
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
218
- if (!ContributorIds.includes(userData.user!.id)) {
219
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
220
- ContributorIds.push(userData.user!.id);
221
- }
222
-
223
- const newData: tsPageData['Update']['Type'] = {
224
- ...(data as tsPageDataSelect),
225
- authorId: AuthorId,
226
- contributorIds: JSON.stringify(ContributorIds),
227
- updatedAt: new Date().toISOString(),
228
- publishedAt:
229
- currentPageData.draft && data.draft === false
230
- ? new Date().toISOString()
231
- : currentPageData.publishedAt?.toISOString() || new Date().toISOString(),
232
- categories: JSON.stringify(data.categories || []),
233
- tags: JSON.stringify(data.tags || []),
234
- augments: JSON.stringify(data.augments || []),
235
- contentLang: 'default',
236
- };
237
-
238
- const getMetaData = sdk.dbService.withCodec({
239
- encoder: Schema.String,
240
- decoder: StudioCMSPageData.Select,
241
- callbackFn: (query, input) =>
242
- query((db) =>
243
- db
244
- .selectFrom('StudioCMSPageData')
245
- .selectAll()
246
- .where('id', '=', input)
247
- .executeTakeFirstOrThrow()
248
- ),
249
- });
250
-
251
- const startMetaData = yield* getMetaData(data.id);
252
-
253
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
254
- const apiRoute = getPageTypeEndpoints(data.package!, 'onEdit');
255
-
256
- const updatedPage = yield* sdk.UPDATE.page.byId(data.id, {
257
- pageData: newData,
258
- pageContent: content,
259
- });
260
-
261
- if (!updatedPage) {
262
- return apiResponseLogger(500, 'Failed to update page');
263
- }
264
-
265
- const updatedMetaData = yield* getMetaData(data.id);
266
-
267
- const { enableDiffs, diffPerPage = 10 } = ctx.locals.StudioCMS.siteConfig.data;
268
-
269
- if (enableDiffs) {
270
- yield* sdk.diffTracking.insert(
271
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
272
- userData.user!.id,
273
- data.id,
274
- {
275
- content: {
276
- start: defaultContent?.content || '',
277
- end: content.content || '',
278
- },
279
- // biome-ignore lint/style/noNonNullAssertion: this is a valid use case for non-null assertion
280
- metaData: { start: startMetaData!, end: updatedMetaData! },
281
- },
282
- diffPerPage
283
- );
284
- }
285
-
286
- if (apiRoute) {
287
- yield* Effect.tryPromise(() =>
288
- apiRoute({ AstroCtx: ctx, pageData: updatedPage, pluginFields })
289
- );
290
- }
291
-
292
- yield* Effect.all([
293
- sdk.CLEAR.pages,
294
- notify.sendEditorNotification('page_updated', data.title || startMetaData?.title || ''),
295
- ]);
296
-
297
- return apiResponseLogger(200, 'Page updated successfully');
298
- }).pipe(Notifications.Provide),
299
- DELETE: (ctx) =>
300
- genLogger('studiocms/routes/api/dashboard/content/page.DELETE')(function* () {
301
- const [sdk, notify] = yield* Effect.all([SDKCore, Notifications]);
302
-
303
- // Get user data
304
- const userData = ctx.locals.StudioCMS.security?.userSessionData;
305
-
306
- // Check if user is logged in
307
- if (!userData?.isLoggedIn) {
308
- return apiResponseLogger(403, 'Unauthorized');
309
- }
310
-
311
- // Check if user has permission
312
- const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isAdmin;
313
- if (!isAuthorized) {
314
- return apiResponseLogger(403, 'Unauthorized');
315
- }
316
-
317
- const { id, slug } = yield* readAPIContextJson<{ id: string; slug?: string }>(ctx);
318
-
319
- const pageToDelete = yield* sdk.GET.page.byId(id);
320
-
321
- if (!pageToDelete) {
322
- return apiResponseLogger(404, 'Page not found');
323
- }
324
- if (pageToDelete.slug !== slug) {
325
- return apiResponseLogger(400, 'Invalid request');
326
- }
327
-
328
- const apiRoute = getPageTypeEndpoints(pageToDelete.package, 'onDelete');
329
-
330
- yield* sdk.DELETE.page(id);
331
-
332
- if (apiRoute) {
333
- yield* Effect.tryPromise(() => apiRoute({ AstroCtx: ctx, pageData: pageToDelete }));
334
- }
335
-
336
- yield* Effect.all([
337
- sdk.CLEAR.pages,
338
- notify.sendEditorNotification('page_deleted', pageToDelete.title),
339
- ]);
340
-
341
- return apiResponseLogger(200, 'Page deleted successfully');
342
- }).pipe(Notifications.Provide),
343
- OPTIONS: () =>
344
- Effect.try(() => OptionsResponse({ allowedMethods: ['POST', 'PATCH', 'DELETE'] })),
345
- ALL: () => Effect.try(() => AllResponse()),
346
- },
347
- {
348
- cors: { methods: ['POST', 'PATCH', 'DELETE', 'OPTIONS'] },
349
- onError: (error) => {
350
- console.error('API Error:', error);
351
- return createJsonResponse(
352
- { error: 'Internal Server Error' },
353
- {
354
- status: 500,
355
- }
356
- );
357
- },
358
- }
359
- );
@@ -1,77 +0,0 @@
1
- import { developerConfig } from 'studiocms:config';
2
- import { apiResponseLogger } from 'studiocms:logger';
3
- import { Notifications } from 'studiocms:notifier';
4
- import { SDKCore } from 'studiocms:sdk';
5
- import {
6
- AllResponse,
7
- createEffectAPIRoutes,
8
- createJsonResponse,
9
- Effect,
10
- genLogger,
11
- OptionsResponse,
12
- readAPIContextJson,
13
- } from '@withstudiocms/effect';
14
-
15
- export const { POST, OPTIONS, ALL } = createEffectAPIRoutes(
16
- {
17
- POST: (ctx) =>
18
- genLogger('studiocms/routes/api/dashboard/create-reset-link.POST')(function* () {
19
- const [notify, sdk] = yield* Effect.all([Notifications, SDKCore]);
20
-
21
- // Check if demo mode is enabled
22
- if (developerConfig.demoMode !== false) {
23
- return apiResponseLogger(403, 'Demo mode is enabled, this action is not allowed.');
24
- }
25
-
26
- // Get user data
27
- const userData = ctx.locals.StudioCMS.security?.userSessionData;
28
-
29
- // Check if user is logged in
30
- if (!userData?.isLoggedIn) {
31
- return apiResponseLogger(403, 'Unauthorized');
32
- }
33
-
34
- // Check if user has permission
35
- const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isAdmin;
36
- if (!isAuthorized) {
37
- return apiResponseLogger(403, 'Unauthorized');
38
- }
39
-
40
- const { userId } = yield* readAPIContextJson<{ userId: string }>(ctx);
41
-
42
- if (!userId) {
43
- return apiResponseLogger(400, 'Invalid form data, userId is required');
44
- }
45
-
46
- const token = yield* sdk.resetTokenBucket.new(userId);
47
-
48
- if (!token) {
49
- return apiResponseLogger(500, 'Failed to create reset link');
50
- }
51
-
52
- const user = yield* sdk.GET.users.byId(userId);
53
-
54
- if (!user) {
55
- return apiResponseLogger(404, 'User not found');
56
- }
57
-
58
- yield* notify.sendAdminNotification('user_updated', user.username);
59
-
60
- return createJsonResponse(token);
61
- }).pipe(Notifications.Provide),
62
- OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['POST'] })),
63
- ALL: () => Effect.try(() => AllResponse()),
64
- },
65
- {
66
- cors: { methods: ['POST', 'OPTIONS'] },
67
- onError: (error) => {
68
- console.error('API Error:', error);
69
- return createJsonResponse(
70
- { error: 'Internal Server Error' },
71
- {
72
- status: 500,
73
- }
74
- );
75
- },
76
- }
77
- );
@@ -1,231 +0,0 @@
1
- import { User } from 'studiocms:auth/lib';
2
- import { apiResponseLogger } from 'studiocms:logger';
3
- import { Mailer } from 'studiocms:mailer';
4
- import { Notifications } from 'studiocms:notifier';
5
- import { SDKCore } from 'studiocms:sdk';
6
- import templateEngine from 'studiocms:template-engine';
7
- import type { AvailablePermissionRanks } from '@withstudiocms/auth-kit/types';
8
- import {
9
- AllResponse,
10
- appendSearchParamsToUrl,
11
- createEffectAPIRoutes,
12
- createJsonResponse,
13
- Effect,
14
- genLogger,
15
- OptionsResponse,
16
- pipe,
17
- readAPIContextJson,
18
- } from '@withstudiocms/effect';
19
- import type { APIContext } from 'astro';
20
- import { z } from 'astro/zod';
21
-
22
- type JSONData = {
23
- username: string | undefined;
24
- email: string | undefined;
25
- displayname: string | undefined;
26
- rank: AvailablePermissionRanks | undefined;
27
- originalUrl: string;
28
- };
29
-
30
- type Token = {
31
- id: string;
32
- userId: string;
33
- token: string;
34
- };
35
-
36
- const generateResetUrl = (
37
- {
38
- locals: {
39
- StudioCMS: {
40
- routeMap: {
41
- mainLinks: { dashboardIndex },
42
- },
43
- },
44
- },
45
- }: APIContext,
46
- baseUrl: string,
47
- { id, userId, token }: Token
48
- ) => {
49
- const resetURL = new URL(`${dashboardIndex}/password-reset`, baseUrl);
50
- return pipe(
51
- resetURL,
52
- appendSearchParamsToUrl('userid', userId),
53
- appendSearchParamsToUrl('token', token),
54
- appendSearchParamsToUrl('id', id)
55
- );
56
- };
57
-
58
- const noMailerError = (message: string, resetLink: URL) =>
59
- `Failed to send email: ${message}. You can provide the following Reset link to your User: ${resetLink}`;
60
-
61
- export const { POST, OPTIONS, ALL } = createEffectAPIRoutes(
62
- {
63
- POST: (ctx) =>
64
- genLogger('studiocms/routes/api/dashboard/create-user-invite.POST')(function* () {
65
- const [userHelper, mailer, notify, sdk] = yield* Effect.all([
66
- User,
67
- Mailer,
68
- Notifications,
69
- SDKCore,
70
- ]);
71
-
72
- const siteConfig = ctx.locals.StudioCMS.siteConfig.data;
73
-
74
- if (!siteConfig) {
75
- return apiResponseLogger(500, 'Failed to get site config');
76
- }
77
-
78
- // Get user data
79
- const userData = ctx.locals.StudioCMS.security?.userSessionData;
80
-
81
- // Check if user is logged in
82
- if (!userData?.isLoggedIn) {
83
- return apiResponseLogger(403, 'Unauthorized');
84
- }
85
-
86
- // Check if user has permission
87
- const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isAdmin;
88
- if (!isAuthorized) {
89
- return apiResponseLogger(403, 'Unauthorized');
90
- }
91
-
92
- const { username, email, displayname, rank, originalUrl } =
93
- yield* readAPIContextJson<JSONData>(ctx);
94
-
95
- // If the username, password, email, or display name is missing, return an error
96
- if (!username) {
97
- return apiResponseLogger(400, 'Missing field: Username is required');
98
- }
99
-
100
- if (!email) {
101
- return apiResponseLogger(400, 'Missing field: Email is required');
102
- }
103
-
104
- if (!displayname) {
105
- return apiResponseLogger(400, 'Missing field: Display name is required');
106
- }
107
-
108
- if (!rank) {
109
- return apiResponseLogger(400, 'Missing field: Rank is required');
110
- }
111
-
112
- const userPerms = ctx.locals.StudioCMS.security?.userPermissionLevel;
113
- if (rank === 'owner' && !userPerms?.isOwner) {
114
- return apiResponseLogger(403, 'Unauthorized');
115
- }
116
-
117
- // If the email is invalid, return an error
118
- const checkEmail = z.coerce
119
- .string()
120
- .email({ message: 'Email address is invalid' })
121
- .safeParse(email);
122
-
123
- if (!checkEmail.success) {
124
- return apiResponseLogger(400, `Invalid email: ${checkEmail.error.message}`);
125
- }
126
-
127
- const [verifyUsernameResponse, { usernameSearch, emailSearch }] = yield* Effect.all([
128
- userHelper.verifyUsernameInput(username),
129
- sdk.AUTH.user.searchUsersForUsernameOrEmail(username, checkEmail.data),
130
- ]);
131
-
132
- if (verifyUsernameResponse !== true) {
133
- return apiResponseLogger(400, verifyUsernameResponse);
134
- }
135
-
136
- if (usernameSearch.length > 0) {
137
- return apiResponseLogger(400, 'Invalid username: Username is already in use');
138
- }
139
-
140
- if (emailSearch.length > 0) {
141
- return apiResponseLogger(400, 'Invalid email: Email is already in use');
142
- }
143
-
144
- // Creates a new user invite
145
- const token = yield* sdk.AUTH.user
146
- .create(
147
- {
148
- username,
149
- email: checkEmail.data,
150
- name: displayname,
151
- createdAt: new Date().toISOString(),
152
- id: crypto.randomUUID(),
153
- avatar: undefined,
154
- emailVerified: false,
155
- notifications: undefined,
156
- password: undefined,
157
- updatedAt: new Date().toISOString(),
158
- url: undefined,
159
- },
160
- rank
161
- )
162
- .pipe(Effect.flatMap((newUser) => sdk.resetTokenBucket.new(newUser.id)));
163
-
164
- if (!token) {
165
- return apiResponseLogger(500, 'Failed to create reset token');
166
- }
167
-
168
- const resetLink = generateResetUrl(ctx, originalUrl, token);
169
-
170
- yield* notify.sendAdminNotification('new_user', username);
171
-
172
- if (siteConfig.enableMailer) {
173
- const checkMailConnection = yield* mailer.verifyMailConnection;
174
-
175
- if (!checkMailConnection) {
176
- return apiResponseLogger(
177
- 500,
178
- noMailerError('Failed to connect to mail server', resetLink)
179
- );
180
- }
181
-
182
- if ('error' in checkMailConnection) {
183
- return apiResponseLogger(
184
- 500,
185
- noMailerError('Failed to connect to mail server', resetLink)
186
- );
187
- }
188
-
189
- const engine = yield* templateEngine;
190
- const { title: siteTitle, description, siteIcon } = siteConfig;
191
-
192
- const userInviteTemplate = yield* engine.render('userInvite', {
193
- site: { title: siteTitle, description, icon: siteIcon ?? undefined },
194
- data: { link: resetLink.toString() },
195
- });
196
-
197
- const mailResponse = yield* mailer.sendMail({
198
- to: checkEmail.data,
199
- subject: `You have been invited to join ${siteConfig.title}!`,
200
- html: userInviteTemplate,
201
- });
202
-
203
- if (!mailResponse) {
204
- return apiResponseLogger(500, noMailerError('Failed to send email', resetLink));
205
- }
206
-
207
- if ('error' in mailResponse) {
208
- return apiResponseLogger(500, noMailerError(mailResponse.error, resetLink));
209
- }
210
-
211
- return apiResponseLogger(200, 'User invite created and email sent');
212
- }
213
-
214
- return apiResponseLogger(200, resetLink.toString());
215
- }).pipe(Mailer.Provide, Notifications.Provide),
216
- OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['POST'] })),
217
- ALL: () => Effect.try(() => AllResponse()),
218
- },
219
- {
220
- cors: { methods: ['POST', 'OPTIONS'] },
221
- onError: (error) => {
222
- console.error('API Error:', error);
223
- return createJsonResponse(
224
- { error: 'Internal Server Error' },
225
- {
226
- status: 500,
227
- }
228
- );
229
- },
230
- }
231
- );