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,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
- };