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.
- package/CHANGELOG.md +122 -0
- package/dist/cli/add/index.d.ts +2 -2
- package/dist/cli/add/index.js +4 -3
- package/dist/cli/add/npm-utils.d.ts +6 -6
- package/dist/cli/add/tryToInstallPlugins.d.ts +1 -1
- package/dist/cli/add/tryToInstallPlugins.js +6 -5
- package/dist/cli/add/updateStudioCMSConfig.d.ts +1 -1
- package/dist/cli/add/updateStudioCMSConfig.js +3 -4
- package/dist/cli/add/validatePlugins.d.ts +1 -2
- package/dist/cli/add/validatePlugins.js +15 -9
- package/dist/cli/crypto/genJWT/index.d.ts +1 -1
- package/dist/cli/crypto/genJWT/index.js +8 -9
- package/dist/cli/crypto/index.d.ts +1 -1
- package/dist/cli/init/steps/env.js +14 -4
- package/dist/cli/init/steps/next.d.ts +1 -1
- package/dist/cli/init/steps/next.js +6 -5
- package/dist/cli/migrator/index.d.ts +1 -1
- package/dist/cli/migrator/index.js +2 -2
- package/dist/cli/users/index.d.ts +1 -1
- package/dist/cli/users/shared.js +2 -2
- package/dist/cli/users/steps/createUsers.js +7 -7
- package/dist/cli/users/steps/modifyUsers.js +2 -2
- package/dist/cli/users/steps/next.d.ts +1 -1
- package/dist/cli/utils/checkRequiredEnvVars.js +2 -2
- package/dist/cli/utils/context.d.ts +2 -4
- package/dist/cli/utils/context.js +1 -3
- package/dist/cli/utils/getCliDbClient.d.ts +1 -1
- package/dist/cli/utils/intro.d.ts +1 -1
- package/dist/cli/utils/loadConfig.d.ts +54 -49
- package/dist/cli/utils/loadConfig.js +5 -8
- package/dist/cli/utils/logger.js +3 -3
- package/dist/cli/utils/user-utils.d.ts +1 -1
- package/dist/cli/utils/user-utils.js +4 -3
- package/dist/client/apiClient.d.ts +4923 -0
- package/dist/client/apiClient.js +72 -0
- package/dist/config.d.ts +1734 -1
- package/dist/consts.d.ts +5 -5
- package/dist/consts.js +3 -2
- package/dist/db/plugins.d.ts +1 -1
- package/dist/db/plugins.js +5 -8
- package/dist/handlers/frontend/routes.d.ts +4 -18
- package/dist/handlers/frontend/routes.js +13 -152
- package/dist/handlers/frontend/types.d.ts +1 -1
- package/dist/handlers/frontend/utils.js +0 -18
- package/dist/handlers/pluginHandler.d.ts +34 -257
- package/dist/handlers/pluginHandler.js +92 -46
- package/dist/handlers/routeHandler.js +32 -11
- package/dist/handlers/setupDbStudio.d.ts +3 -1
- package/dist/handlers/setupDbStudio.js +19 -10
- package/dist/handlers/storage-manager/core/effectify-astro-context.d.ts +25 -0
- package/dist/handlers/storage-manager/core/effectify-astro-context.js +78 -0
- package/dist/handlers/storage-manager/no-op.d.ts +2 -2
- package/dist/handlers/storage-manager/no-op.js +2 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js +10 -20
- package/dist/integrations/robots/index.d.ts +2 -2
- package/dist/integrations/robots/index.js +1 -3
- package/dist/integrations/robots/schema.d.ts +102 -273
- package/dist/integrations/robots/schema.js +220 -209
- package/dist/plugins/analytics/assets/schemas.d.ts +14 -9
- package/dist/plugins/analytics/assets/schemas.js +25 -17
- package/dist/plugins/analytics/db-client.d.ts +1 -1
- package/dist/plugins/analytics/index.d.ts +823 -3
- package/dist/plugins/analytics/index.js +4 -5
- package/dist/plugins/analytics/schemas.d.ts +54 -62
- package/dist/plugins/analytics/schemas.js +64 -13
- package/dist/plugins/analytics/table.d.ts +1 -1
- package/dist/plugins.d.ts +0 -1
- package/dist/schemas/config/api.d.ts +17 -0
- package/dist/schemas/config/api.js +14 -0
- package/dist/schemas/config/auth.d.ts +55 -59
- package/dist/schemas/config/auth.js +34 -11
- package/dist/schemas/config/dashboard.d.ts +43 -79
- package/dist/schemas/config/dashboard.js +43 -12
- package/dist/schemas/config/db.d.ts +15 -17
- package/dist/schemas/config/db.js +13 -5
- package/dist/schemas/config/developer.d.ts +33 -45
- package/dist/schemas/config/developer.js +22 -5
- package/dist/schemas/config/index.d.ts +398 -521
- package/dist/schemas/config/index.js +115 -57
- package/dist/schemas/config/sdk.d.ts +50 -196
- package/dist/schemas/config/sdk.js +61 -73
- package/dist/schemas/custom.d.ts +40 -0
- package/dist/schemas/custom.js +41 -0
- package/dist/schemas/external-schemas.d.ts +171 -0
- package/dist/schemas/external-schemas.js +179 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/plugins/i18n.d.ts +59 -39
- package/dist/schemas/plugins/i18n.js +42 -5
- package/dist/schemas/plugins/index.d.ts +7126 -10296
- package/dist/schemas/plugins/index.js +260 -276
- package/dist/schemas/plugins/shared.d.ts +1293 -3718
- package/dist/schemas/plugins/shared.js +320 -329
- package/dist/test-utils.d.ts +15 -4
- package/dist/test-utils.js +27 -11
- package/dist/toolbar/db-viewer/db-shared-types.d.ts +6 -6
- package/dist/toolbar/db-viewer/studio/connection.d.ts +8 -4
- package/dist/toolbar/db-viewer/studio/connection.js +2 -28
- package/dist/toolbar/db-viewer/studio/env/libsql.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/libsql.js +17 -0
- package/dist/toolbar/db-viewer/studio/env/mysql.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/mysql.js +23 -0
- package/dist/toolbar/db-viewer/studio/env/postgres.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/postgres.js +23 -0
- package/dist/toolbar/db-viewer/studio/index.js +20 -56
- package/dist/toolbar/db-viewer/studio/type.d.ts +1 -2
- package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.js +24 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.js +9 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.js +9 -0
- package/dist/toolbar/db-viewer/viewer.js +20 -21
- package/dist/types.d.ts +30 -0
- package/dist/utils/effects/smtp.d.ts +1 -1
- package/dist/utils/lang-helper.d.ts +10 -2
- package/dist/virtual.d.ts +35 -28
- package/dist/virtuals/auth/core.d.ts +5 -5
- package/dist/virtuals/auth/verify-email.d.ts +6 -6
- package/dist/virtuals/components/Generator.astro +2 -2
- package/dist/virtuals/components/Renderer.astro +9 -1
- package/dist/virtuals/components/renderFn.d.ts +3 -1
- package/dist/virtuals/components/renderFn.js +18 -0
- package/dist/virtuals/lib/headDefaults.d.ts +4 -2
- package/dist/virtuals/lib/headDefaults.js +0 -2
- package/dist/virtuals/lib/routeMap.d.ts +0 -12
- package/dist/virtuals/lib/routeMap.js +2 -14
- package/dist/virtuals/mailer/index.d.ts +3 -3
- package/dist/virtuals/notifier/index.d.ts +5 -5
- package/dist/virtuals/plugins/dashboard-pages.d.ts +2 -64
- package/dist/virtuals/scripts/StorageFileBrowser.d.ts +1 -172
- package/dist/virtuals/scripts/StorageFileBrowser.js +216 -119
- package/dist/virtuals/template-engine/index.d.ts +4 -4
- package/frontend/components/dashboard/configuration/ConfigForm.astro +218 -110
- package/frontend/components/dashboard/content-mgmt/ContentSearch.astro +21 -22
- package/frontend/components/dashboard/content-mgmt/CreateFolder.astro +66 -54
- package/frontend/components/dashboard/content-mgmt/CreatePage.astro +58 -104
- package/frontend/components/dashboard/content-mgmt/EditFolder.astro +65 -67
- package/frontend/components/dashboard/content-mgmt/EditPage.astro +86 -134
- package/frontend/components/dashboard/content-mgmt/InnerSidebarElement.astro +0 -1
- package/frontend/components/dashboard/content-mgmt/PageHeader.astro +33 -52
- package/frontend/components/dashboard/content-mgmt/PageTypeHandler.astro +2 -2
- package/frontend/components/dashboard/profile/APITokens.astro +219 -158
- package/frontend/components/dashboard/profile/BasicInfo.astro +165 -106
- package/frontend/components/dashboard/profile/Notifications.astro +27 -18
- package/frontend/components/dashboard/profile/UpdatePassword.astro +134 -94
- package/frontend/components/dashboard/sidebar/VersionCheck.astro +31 -16
- package/frontend/components/dashboard/sidebar/VersionCheckChangelog.astro +18 -11
- package/frontend/components/dashboard/sidebar-modals/VersionModal.astro +2 -2
- package/frontend/components/dashboard/smtp-config/TemplateEditor.astro +14 -14
- package/frontend/components/dashboard/taxonomy/InnerSidebarElement.astro +0 -1
- package/frontend/components/dashboard/taxonomy/MetaContainer.astro +0 -2
- package/frontend/components/dashboard/taxonomy/PageHeader.astro +16 -24
- package/frontend/components/dashboard/taxonomy/TaxonomySearch.astro +23 -27
- package/frontend/components/dashboard/user-mgmt/InnerSidebarElement.astro +111 -104
- package/frontend/components/dashboard/user-mgmt/UserListItem.astro +9 -22
- package/frontend/components/dashboard/user-mgmt/UserListItems.astro +18 -0
- package/frontend/components/first-time-setup/snippets/{opt2-studiocms.config.diff → studiocms.config.diff} +1 -0
- package/frontend/components/shared/Code.astro +1 -4
- package/frontend/components/shared/DynamicSettingsRenderer.astro +1 -1
- package/frontend/components/shared/SSRUser.astro +2 -4
- package/frontend/components/shared/foldertree/FolderTreeNode.astro +0 -6
- package/frontend/components/shared/storage-manager/StorageCopyOutput.astro +0 -1
- package/frontend/components/shared/taxonomy/TaxonomyTreeNode.astro +0 -6
- package/frontend/layouts/DashboardLayout.astro +1 -10
- package/frontend/layouts/TaxonomyLayout.astro +0 -1
- package/frontend/middleware/index.ts +102 -61
- package/frontend/pages/404.astro +5 -9
- package/frontend/pages/[dashboard]/[...pluginPage].astro +10 -1
- package/frontend/pages/[dashboard]/configuration.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/createfolder.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/createpage.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/diff.astro +39 -14
- package/frontend/pages/[dashboard]/content-management/editfolder.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/editpage.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/index.astro +10 -1
- package/frontend/pages/[dashboard]/index.astro +10 -1
- package/frontend/pages/[dashboard]/login.astro +86 -25
- package/frontend/pages/[dashboard]/password-reset.astro +22 -16
- package/frontend/pages/[dashboard]/plugins/[plugin].astro +10 -1
- package/frontend/pages/[dashboard]/profile.astro +10 -1
- package/frontend/pages/[dashboard]/signup.astro +153 -52
- package/frontend/pages/[dashboard]/smtp-configuration.astro +77 -75
- package/frontend/pages/[dashboard]/system-management.astro +10 -1
- package/frontend/pages/[dashboard]/taxonomy/categories.astro +30 -41
- package/frontend/pages/[dashboard]/taxonomy/index.astro +10 -0
- package/frontend/pages/[dashboard]/taxonomy/tags.astro +33 -43
- package/frontend/pages/[dashboard]/unverified-email.astro +29 -21
- package/frontend/pages/[dashboard]/user-management/edit.astro +170 -90
- package/frontend/pages/[dashboard]/user-management/index.astro +10 -1
- package/frontend/pages/studiocms_api/[...all].ts +106 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/auth.ts +26 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/changelog.ts +147 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/db-studio-driver.ts +46 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/parseLogLevel.ts +27 -0
- package/frontend/pages/studiocms_api/_handlers/auth/auth.ts +459 -0
- package/frontend/pages/studiocms_api/_handlers/auth/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/auth/oauth.ts +91 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/_shared.ts +57 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/apiTokens.ts +134 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/config.ts +64 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/content.ts +741 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/create.ts +480 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/emailNotifications.ts +49 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/index.ts +45 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/mailer.ts +136 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/plugins.ts +80 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/profile.ts +275 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/resetPassword.ts +140 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/search.ts +63 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/taxonomy.ts +285 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/templates.ts +75 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/users.ts +312 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/verifyEndpoints.ts +307 -0
- package/frontend/pages/studiocms_api/_handlers/integration/dbStudio.ts +98 -0
- package/frontend/pages/studiocms_api/_handlers/integration/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/integration/storageManager.ts +107 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/index.ts +8 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/_shared.ts +41 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/public.ts +195 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts +1726 -0
- package/frontend/pages/studiocms_api/_handlers/sdk.ts +129 -0
- package/frontend/pages/studiocms_api/_middleware/astroLocals.ts +36 -0
- package/frontend/pages/studiocms_api/_middleware/restApi.ts +56 -0
- package/frontend/pages/studiocms_api/integrations/[...all].ts +8 -0
- package/frontend/scripts/formdata.ts +219 -0
- package/frontend/setup-pages/3-done.astro +4 -20
- package/frontend/setup-pages/studiocms_api/dashboard/step-2.ts +3 -6
- package/frontend/styles/dashboard-base.css +0 -1
- package/frontend/web-vitals/endpoint.ts +2 -1
- package/package.json +35 -31
- package/dist/global.d.ts +0 -9
- package/frontend/components/dashboard/LoginChecker.astro +0 -68
- package/frontend/components/dashboard/user-mgmt/RankCheck.astro +0 -57
- package/frontend/components/first-time-setup/snippets/opt1-astro.config.diff +0 -14
- package/frontend/components/first-time-setup/snippets/opt2-astro.config.diff +0 -9
- package/frontend/middleware/_authmap.ts +0 -63
- package/frontend/pages/studiocms_api/auth/[path].ts +0 -390
- package/frontend/pages/studiocms_api/auth/[provider]/[...id].ts +0 -64
- package/frontend/pages/studiocms_api/auth/[provider]/_effects/index.ts +0 -101
- package/frontend/pages/studiocms_api/auth/_shared.ts +0 -52
- package/frontend/pages/studiocms_api/dashboard/api-tokens.ts +0 -115
- package/frontend/pages/studiocms_api/dashboard/config.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/content/diff.ts +0 -73
- package/frontend/pages/studiocms_api/dashboard/content/folder.ts +0 -220
- package/frontend/pages/studiocms_api/dashboard/content/page.ts +0 -359
- package/frontend/pages/studiocms_api/dashboard/create-reset-link.ts +0 -77
- package/frontend/pages/studiocms_api/dashboard/create-user-invite.ts +0 -231
- package/frontend/pages/studiocms_api/dashboard/create-user.ts +0 -186
- package/frontend/pages/studiocms_api/dashboard/email-notification-settings-site.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/mailer/check-email.ts +0 -75
- package/frontend/pages/studiocms_api/dashboard/mailer/config.ts +0 -136
- package/frontend/pages/studiocms_api/dashboard/plugins/[plugin].ts +0 -80
- package/frontend/pages/studiocms_api/dashboard/profile.ts +0 -245
- package/frontend/pages/studiocms_api/dashboard/resend-verify-email.ts +0 -77
- package/frontend/pages/studiocms_api/dashboard/reset-password.ts +0 -124
- package/frontend/pages/studiocms_api/dashboard/search-list.ts +0 -59
- package/frontend/pages/studiocms_api/dashboard/taxonomy-search.ts +0 -47
- package/frontend/pages/studiocms_api/dashboard/taxonomy.ts +0 -230
- package/frontend/pages/studiocms_api/dashboard/templates.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/update-user-notifications.ts +0 -86
- package/frontend/pages/studiocms_api/dashboard/users.ts +0 -236
- package/frontend/pages/studiocms_api/dashboard/verify-email.ts +0 -83
- package/frontend/pages/studiocms_api/dashboard/verify-session.ts +0 -187
- package/frontend/pages/studiocms_api/integrations/[type]/[...id].ts +0 -15
- package/frontend/pages/studiocms_api/integrations/[type]/_routes/db-studio.ts +0 -173
- package/frontend/pages/studiocms_api/integrations/[type]/_routes/storage.ts +0 -88
- package/frontend/pages/studiocms_api/partials/editor.astro +0 -74
- package/frontend/pages/studiocms_api/partials/render.astro +0 -39
- package/frontend/pages/studiocms_api/partials/user-list-items.astro +0 -43
- package/frontend/pages/studiocms_api/rest/utils/auth-token.ts +0 -59
- package/frontend/pages/studiocms_api/rest/v1/[type]/[...id].ts +0 -23
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/categories.ts +0 -267
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/folders.ts +0 -283
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/pages.ts +0 -505
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/settings.ts +0 -100
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/tags.ts +0 -229
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/users.ts +0 -553
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/[...id].ts +0 -19
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/categories.ts +0 -74
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/folders.ts +0 -85
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/pages.ts +0 -103
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/tags.ts +0 -67
- package/frontend/pages/studiocms_api/sdk/[...path].ts +0 -97
- package/frontend/pages/studiocms_api/sdk/utils/changelog.ts +0 -119
- package/frontend/scripts/auth/formListener.ts +0 -81
- package/frontend/scripts/formdata-utils.ts +0 -116
- package/frontend/utils/build-partial-schema.ts +0 -46
- package/frontend/utils/errors.ts +0 -6
- package/frontend/utils/param-extractor.ts +0 -83
- package/frontend/utils/rest-router.ts +0 -444
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { User } from 'studiocms:auth/lib';
|
|
2
3
|
import { FormattedDate } from 'studiocms:components';
|
|
3
4
|
import { useTranslations } from 'studiocms:i18n';
|
|
4
5
|
import { SDKCore } from 'studiocms:sdk';
|
|
@@ -11,9 +12,9 @@ import { Input } from 'studiocms:ui/components/input';
|
|
|
11
12
|
import { Modal } from 'studiocms:ui/components/modal';
|
|
12
13
|
import { Select } from 'studiocms:ui/components/select';
|
|
13
14
|
import { Effect, genLogger, runEffect } from '@withstudiocms/effect';
|
|
15
|
+
import { setDataContext } from '@withstudiocms/effect/astro/data-middleware';
|
|
14
16
|
import PageHeader from '../../../components/dashboard/PageHeader.astro';
|
|
15
17
|
import InnerSidebarElement from '../../../components/dashboard/user-mgmt/InnerSidebarElement.astro';
|
|
16
|
-
import RankCheck from '../../../components/dashboard/user-mgmt/RankCheck.astro';
|
|
17
18
|
import SocialSignin from '../../../components/dashboard/user-mgmt/SocialSignin.astro';
|
|
18
19
|
import { providerData, showOAuth } from '../../../components/shared/oAuthButtonProviders.js';
|
|
19
20
|
import Layout from '../../../layouts/DashboardLayout.astro';
|
|
@@ -62,20 +63,35 @@ const disableDelete = user?.permissionsData?.rank === 'owner' || user?.id === us
|
|
|
62
63
|
if (isOwner) allowedRanks.unshift({ label: 'Owner', value: 'owner' });
|
|
63
64
|
|
|
64
65
|
if (isAdmin) allowedRanks.unshift({ label: 'Administrator', value: 'admin' });
|
|
66
|
+
|
|
67
|
+
const isAuthorized = await Effect.runPromise(
|
|
68
|
+
Effect.gen(function* () {
|
|
69
|
+
const { isUserAllowed } = yield* User;
|
|
70
|
+
return yield* isUserAllowed(userSession, user?.permissionsData?.rank ?? 'unknown');
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!isAuthorized) {
|
|
75
|
+
return Astro.redirect(Astro.locals.StudioCMS.routeMap.mainLinks.userManagement);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setDataContext(Astro, {
|
|
79
|
+
key: 'x-required-role',
|
|
80
|
+
value: 'admin',
|
|
81
|
+
});
|
|
82
|
+
setDataContext(Astro, {
|
|
83
|
+
key: 'x-redirect-url',
|
|
84
|
+
value: Astro.locals.StudioCMS.routeMap.mainLinks.dashboardIndex,
|
|
85
|
+
});
|
|
65
86
|
---
|
|
66
87
|
<Layout
|
|
67
88
|
title={t('title')}
|
|
68
89
|
description={t('description')}
|
|
69
|
-
requiredPermission='admin'
|
|
70
90
|
sidebar='double'
|
|
71
91
|
{lang}
|
|
72
92
|
{config}
|
|
73
93
|
currentUser={userSession}
|
|
74
94
|
>
|
|
75
|
-
{/*
|
|
76
|
-
Check if the user has the required permission to manage the user's rank level
|
|
77
|
-
*/}
|
|
78
|
-
<RankCheck requiredPermission={user?.permissionsData?.rank} />
|
|
79
95
|
|
|
80
96
|
<div slot="double-sidebar" class="inner-sidebar-container">
|
|
81
97
|
<div class="sidebar-user-links-container">
|
|
@@ -325,27 +341,22 @@ if (isAdmin) allowedRanks.unshift({ label: 'Administrator', value: 'admin' });
|
|
|
325
341
|
<script>
|
|
326
342
|
import { toast } from 'studiocms:ui/components/toaster/client';
|
|
327
343
|
import { ModalHelper } from 'studiocms:ui/components/modal/client';
|
|
344
|
+
import { dashboardClient, dashboardSharedCatchTags } from 'studiocms:client/apiClients';
|
|
345
|
+
import * as Effect from 'effect/Effect';
|
|
346
|
+
import { availablePermissionRanks, type AvailablePermissionRanks } from '@withstudiocms/auth-kit/types';
|
|
328
347
|
|
|
329
348
|
const resetLinkModal = new ModalHelper('password-reset-link-modal');
|
|
330
349
|
|
|
331
350
|
const deleteButtons = document.querySelectorAll('[data-token]') as NodeListOf<HTMLButtonElement>;
|
|
332
351
|
|
|
333
|
-
const tokenAPILinks = document.getElementById('token-api-links') as HTMLDivElement;
|
|
334
|
-
|
|
335
|
-
const tokenAPI = tokenAPILinks.dataset.token_api!;
|
|
336
|
-
|
|
337
352
|
const editUserForm = document.getElementById('edit-user-form') as HTMLFormElement;
|
|
338
353
|
|
|
339
|
-
const deleteUserAction = (document.getElementById('delete-user-modal-trigger') as HTMLButtonElement).dataset.action;
|
|
340
|
-
|
|
341
354
|
const userDeleteModal = new ModalHelper('delete-user-modal', 'delete-user-modal-trigger');
|
|
342
355
|
|
|
343
356
|
const passwordResetLinkTrigger = document.getElementById('password-reset-link-trigger') as HTMLButtonElement;
|
|
344
357
|
const resetLinkPlaceholder = document.getElementById('reset-link-placeholder') as HTMLElement;
|
|
345
358
|
|
|
346
|
-
const resetLinkAPI = (document.getElementById('reset-link-selector') as HTMLDivElement).dataset.link;
|
|
347
359
|
const userId = (document.getElementById('reset-link-selector') as HTMLDivElement).dataset.userid;
|
|
348
|
-
const dashboardIndex = (document.getElementById('reset-link-selector') as HTMLDivElement).dataset.dashboard;
|
|
349
360
|
|
|
350
361
|
const passwordResetUrl = (document.getElementById('reset-link-selector') as HTMLDivElement).dataset.reseturl;
|
|
351
362
|
|
|
@@ -360,27 +371,44 @@ if (isAdmin) allowedRanks.unshift({ label: 'Administrator', value: 'admin' });
|
|
|
360
371
|
passwordResetLinkTrigger.addEventListener('click', async (e) => {
|
|
361
372
|
e.preventDefault();
|
|
362
373
|
|
|
363
|
-
|
|
364
|
-
method: 'POST',
|
|
365
|
-
headers: {
|
|
366
|
-
'Content-Type': 'application/json',
|
|
367
|
-
},
|
|
368
|
-
body: JSON.stringify({ userId: userId })
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
const token = await apiRes.json();
|
|
372
|
-
|
|
373
|
-
if (!apiRes.ok) {
|
|
374
|
-
console.error('Failed to create reset link');
|
|
374
|
+
if (!userId) {
|
|
375
375
|
toast({
|
|
376
376
|
title: 'Error',
|
|
377
|
-
description:
|
|
377
|
+
description: 'User ID is missing. Cannot generate password reset link.',
|
|
378
378
|
type: 'danger'
|
|
379
379
|
})
|
|
380
380
|
return;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
|
|
383
|
+
const response = await dashboardClient.pipe(
|
|
384
|
+
Effect.flatMap((client) =>
|
|
385
|
+
client.create.createPasswordResetLink({
|
|
386
|
+
payload: {
|
|
387
|
+
userId
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
),
|
|
391
|
+
Effect.catchTags(dashboardSharedCatchTags),
|
|
392
|
+
Effect.runPromise
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
if ('error' in response) {
|
|
396
|
+
toast({
|
|
397
|
+
title: 'Error',
|
|
398
|
+
description: response.error,
|
|
399
|
+
type: 'danger',
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
} else if (!response) {
|
|
403
|
+
toast({
|
|
404
|
+
title: 'Error',
|
|
405
|
+
description: 'Failed to generate password reset link.',
|
|
406
|
+
type: 'danger',
|
|
407
|
+
});
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
resetLinkPlaceholder.innerText = generateResetLink(response);
|
|
384
412
|
|
|
385
413
|
resetLinkModal.show();
|
|
386
414
|
})
|
|
@@ -391,36 +419,55 @@ if (isAdmin) allowedRanks.unshift({ label: 'Administrator', value: 'admin' });
|
|
|
391
419
|
return
|
|
392
420
|
};
|
|
393
421
|
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
usernameConfirm: formData.get('confirm-user-username')?.toString()
|
|
398
|
-
}
|
|
422
|
+
const userId = formData.get('user-id')?.toString();
|
|
423
|
+
const username = formData.get('user-username')?.toString();
|
|
424
|
+
const usernameConfirm = formData.get('confirm-user-username')?.toString();
|
|
399
425
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const res = await response.json();
|
|
426
|
+
if (!userId || !username || !usernameConfirm) {
|
|
427
|
+
toast({
|
|
428
|
+
title: 'Error',
|
|
429
|
+
description: 'Missing necessary data to delete user.',
|
|
430
|
+
type: 'danger'
|
|
431
|
+
})
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
409
434
|
|
|
410
|
-
if (
|
|
435
|
+
if (username !== usernameConfirm) {
|
|
411
436
|
toast({
|
|
412
437
|
title: 'Error',
|
|
413
|
-
description:
|
|
438
|
+
description: 'Username confirmation does not match.',
|
|
414
439
|
type: 'danger'
|
|
415
440
|
})
|
|
441
|
+
return;
|
|
416
442
|
}
|
|
417
443
|
|
|
418
|
-
|
|
444
|
+
const response = await dashboardClient.pipe(
|
|
445
|
+
Effect.flatMap((client) =>
|
|
446
|
+
client.users.deleteUser({
|
|
447
|
+
payload: {
|
|
448
|
+
userId,
|
|
449
|
+
username,
|
|
450
|
+
usernameConfirm
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
),
|
|
454
|
+
Effect.catchTags(dashboardSharedCatchTags),
|
|
455
|
+
Effect.runPromise
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
if ('error' in response) {
|
|
459
|
+
toast({
|
|
460
|
+
title: 'Error',
|
|
461
|
+
description: response.error,
|
|
462
|
+
type: 'danger',
|
|
463
|
+
});
|
|
464
|
+
return;
|
|
465
|
+
} else {
|
|
419
466
|
toast({
|
|
420
467
|
title: 'Success',
|
|
421
|
-
description:
|
|
422
|
-
type: 'success'
|
|
423
|
-
})
|
|
468
|
+
description: response.message || 'User deleted successfully.',
|
|
469
|
+
type: 'success',
|
|
470
|
+
});
|
|
424
471
|
}
|
|
425
472
|
})
|
|
426
473
|
|
|
@@ -429,78 +476,111 @@ if (isAdmin) allowedRanks.unshift({ label: 'Administrator', value: 'admin' });
|
|
|
429
476
|
|
|
430
477
|
const formData = new FormData(editUserForm);
|
|
431
478
|
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
emailVerified: formData.get('email-verified') === 'true'
|
|
436
|
-
}
|
|
479
|
+
const id = formData.get('user-id')?.toString();
|
|
480
|
+
let rank = formData.get('rank')?.toString();
|
|
481
|
+
const emailVerified = formData.get('email-verified')?.toString() === 'true';
|
|
437
482
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
483
|
+
if (!id || !rank || emailVerified === undefined) {
|
|
484
|
+
toast({
|
|
485
|
+
title: 'Error',
|
|
486
|
+
description: 'Missing necessary data to update user.',
|
|
487
|
+
type: 'danger'
|
|
488
|
+
})
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
445
491
|
|
|
446
|
-
|
|
492
|
+
function isAvailableRank(rank: string): rank is AvailablePermissionRanks {
|
|
493
|
+
return availablePermissionRanks.includes(rank as AvailablePermissionRanks);
|
|
494
|
+
}
|
|
447
495
|
|
|
448
|
-
if (
|
|
496
|
+
if (!isAvailableRank(rank)) {
|
|
449
497
|
toast({
|
|
450
498
|
title: 'Error',
|
|
451
|
-
description:
|
|
499
|
+
description: 'Invalid rank value.',
|
|
452
500
|
type: 'danger'
|
|
453
501
|
})
|
|
502
|
+
return;
|
|
454
503
|
}
|
|
455
504
|
|
|
456
|
-
|
|
505
|
+
const response = await dashboardClient.pipe(
|
|
506
|
+
Effect.flatMap((client) =>
|
|
507
|
+
client.users.updateUser({
|
|
508
|
+
payload: {
|
|
509
|
+
id,
|
|
510
|
+
emailVerified,
|
|
511
|
+
rank
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
),
|
|
515
|
+
Effect.catchTags(dashboardSharedCatchTags),
|
|
516
|
+
Effect.runPromise
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
if ('error' in response) {
|
|
520
|
+
toast({
|
|
521
|
+
title: 'Error',
|
|
522
|
+
description: response.error,
|
|
523
|
+
type: 'danger',
|
|
524
|
+
});
|
|
525
|
+
return;
|
|
526
|
+
} else {
|
|
457
527
|
toast({
|
|
458
528
|
title: 'Success',
|
|
459
|
-
description:
|
|
460
|
-
type: 'success'
|
|
461
|
-
})
|
|
529
|
+
description: response.message || 'User updated successfully.',
|
|
530
|
+
type: 'success',
|
|
531
|
+
});
|
|
462
532
|
}
|
|
463
533
|
})
|
|
464
534
|
|
|
465
535
|
deleteButtons.forEach((button) => {
|
|
466
536
|
button.addEventListener('click', async (event) => {
|
|
467
537
|
event.preventDefault();
|
|
538
|
+
|
|
468
539
|
const tokenID = button.getAttribute('data-token');
|
|
469
540
|
const userID = button.getAttribute('data-user');
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
method: 'DELETE',
|
|
473
|
-
headers: {
|
|
474
|
-
'Content-Type': 'application/json'
|
|
475
|
-
},
|
|
476
|
-
body: JSON.stringify({
|
|
477
|
-
tokenID,
|
|
478
|
-
userID
|
|
479
|
-
})
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
const res = await response.json();
|
|
483
|
-
|
|
484
|
-
if (!response.ok) {
|
|
541
|
+
|
|
542
|
+
if (!tokenID || !userID) {
|
|
485
543
|
toast({
|
|
486
544
|
title: 'Error',
|
|
487
|
-
description:
|
|
545
|
+
description: 'Missing necessary data to revoke API token.',
|
|
488
546
|
type: 'danger',
|
|
489
547
|
duration: 5000
|
|
490
548
|
})
|
|
491
549
|
return;
|
|
492
550
|
}
|
|
493
551
|
|
|
494
|
-
|
|
552
|
+
const response = await dashboardClient.pipe(
|
|
553
|
+
Effect.flatMap((client) =>
|
|
554
|
+
client.apiTokens.adminRevokeUserApiToken({
|
|
555
|
+
payload: {
|
|
556
|
+
tokenID,
|
|
557
|
+
userID
|
|
558
|
+
}
|
|
559
|
+
})
|
|
560
|
+
),
|
|
561
|
+
Effect.catchTags(dashboardSharedCatchTags),
|
|
562
|
+
Effect.runPromise
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
if ('error' in response) {
|
|
566
|
+
toast({
|
|
567
|
+
title: 'Error',
|
|
568
|
+
description: response.error,
|
|
569
|
+
type: 'danger',
|
|
570
|
+
duration: 5000
|
|
571
|
+
})
|
|
572
|
+
return;
|
|
573
|
+
} else {
|
|
574
|
+
toast({
|
|
495
575
|
title: 'Success',
|
|
496
|
-
description:
|
|
576
|
+
description: response.message || 'API token revoked successfully.',
|
|
497
577
|
type: 'success',
|
|
498
578
|
duration: 5000
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
579
|
+
})
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
window.location.reload();
|
|
582
|
+
}, 1000);
|
|
583
|
+
}
|
|
504
584
|
});
|
|
505
585
|
});
|
|
506
586
|
</script>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { useTranslations } from 'studiocms:i18n';
|
|
3
|
+
import { setDataContext } from '@withstudiocms/effect/astro/data-middleware';
|
|
3
4
|
import PageHeader from '../../../components/dashboard/PageHeader.astro';
|
|
4
5
|
import InnerSidebarElement from '../../../components/dashboard/user-mgmt/InnerSidebarElement.astro';
|
|
5
6
|
import Layout from '../../../layouts/DashboardLayout.astro';
|
|
@@ -9,11 +10,19 @@ const { siteConfig: config, defaultLang: lang, security } = Astro.locals.StudioC
|
|
|
9
10
|
const currentUser = security?.userSessionData ?? null;
|
|
10
11
|
|
|
11
12
|
const t = useTranslations(lang, '@studiocms/dashboard:user-mngmt-index');
|
|
13
|
+
|
|
14
|
+
setDataContext(Astro, {
|
|
15
|
+
key: 'x-required-role',
|
|
16
|
+
value: 'admin',
|
|
17
|
+
});
|
|
18
|
+
setDataContext(Astro, {
|
|
19
|
+
key: 'x-redirect-url',
|
|
20
|
+
value: Astro.locals.StudioCMS.routeMap.mainLinks.dashboardIndex,
|
|
21
|
+
});
|
|
12
22
|
---
|
|
13
23
|
<Layout
|
|
14
24
|
title={t('title')}
|
|
15
25
|
description={t('description')}
|
|
16
|
-
requiredPermission='admin'
|
|
17
26
|
sidebar='double'
|
|
18
27
|
{lang}
|
|
19
28
|
{config}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import config from 'studiocms:config';
|
|
2
|
+
import { HttpApiBuilder, HttpServerResponse } from '@effect/platform';
|
|
3
|
+
import type { Api } from '@effect/platform/HttpApi';
|
|
4
|
+
import {
|
|
5
|
+
StudioCMSAuthApi,
|
|
6
|
+
StudioCMSDashboardApiSpec,
|
|
7
|
+
StudioCMSIntegrationsApiSpec,
|
|
8
|
+
StudioCMSRestApiV1Spec,
|
|
9
|
+
StudioCMSSDKApiSpec,
|
|
10
|
+
} from '@withstudiocms/api-spec';
|
|
11
|
+
import type { APIRoute } from 'astro';
|
|
12
|
+
import { Layer } from 'effect';
|
|
13
|
+
import { HttpApiToAstroRoute } from 'effectify/astro/HttpApi';
|
|
14
|
+
import * as Scalar from 'effectify/scalar';
|
|
15
|
+
import { AuthAPILive } from './_handlers/auth/index.js';
|
|
16
|
+
import { DashboardAPILive } from './_handlers/dashboard/index.js';
|
|
17
|
+
import { IntegrationsAPILive } from './_handlers/integration/index.js';
|
|
18
|
+
import { RestAPILive } from './_handlers/rest-api/index.js';
|
|
19
|
+
import { SDKAPILive } from './_handlers/sdk.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Scalar layer for the StudioCMS API Documentation route, providing a custom header and linking to the documentation for all API specifications.
|
|
23
|
+
*/
|
|
24
|
+
const DocsRouteLive = Scalar.layer({
|
|
25
|
+
title: 'StudioCMS API Documentation',
|
|
26
|
+
description:
|
|
27
|
+
'The Documentation for the StudioCMS API, including all available endpoints and specifications.',
|
|
28
|
+
customHeader: {
|
|
29
|
+
title: {
|
|
30
|
+
text: 'StudioCMS API Documentation',
|
|
31
|
+
link: '/studiocms_api/docs',
|
|
32
|
+
},
|
|
33
|
+
nav: [
|
|
34
|
+
{
|
|
35
|
+
text: 'StudioCMS Website',
|
|
36
|
+
link: 'https://studiocms.dev',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
text: 'GitHub Repository',
|
|
40
|
+
link: 'https://github.com/withstudiocms/studiocms',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
text: 'StudioCMS Discord',
|
|
44
|
+
link: 'https://chat.studiocms.dev',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
path: '/studiocms_api/docs',
|
|
49
|
+
sources: [
|
|
50
|
+
{
|
|
51
|
+
title: 'Auth API',
|
|
52
|
+
httpApi: StudioCMSAuthApi,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: 'Dashboard API',
|
|
56
|
+
httpApi: StudioCMSDashboardApiSpec,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: 'Integrations API',
|
|
60
|
+
httpApi: StudioCMSIntegrationsApiSpec,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: 'REST API v1',
|
|
64
|
+
httpApi: StudioCMSRestApiV1Spec,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: 'SDK API',
|
|
68
|
+
httpApi: StudioCMSSDKApiSpec,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Catch-All Route Layer - Provides a catch-all route that redirects any unmatched requests to a 404 page.
|
|
75
|
+
*
|
|
76
|
+
* This is needed to ensure that any requests that are passed to the Effect API handlers return an Astro 404 page instead of the Effect 404 response.
|
|
77
|
+
*/
|
|
78
|
+
export const CatchAllGroup = HttpApiBuilder.Router.use((router) =>
|
|
79
|
+
router.get('*', HttpServerResponse.redirect('/404'))
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Collection of API handlers for the new Effect HttpApi handlers
|
|
84
|
+
*/
|
|
85
|
+
const APICollection = Layer.mergeAll(
|
|
86
|
+
AuthAPILive,
|
|
87
|
+
IntegrationsAPILive,
|
|
88
|
+
RestAPILive,
|
|
89
|
+
SDKAPILive,
|
|
90
|
+
DashboardAPILive,
|
|
91
|
+
CatchAllGroup
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Combined API Layer - Combines the API handlers with the documentation route if enabled in the configuration.
|
|
96
|
+
*
|
|
97
|
+
* If the API documentation is enabled, it merges the DocsRouteLive layer with the APICollection layer to serve both the API endpoints and the documentation. If the documentation is disabled, it only serves the API endpoints.
|
|
98
|
+
*/
|
|
99
|
+
const APILive: Layer.Layer<Api, never, never> = config.features.api.apiDocs
|
|
100
|
+
? Layer.merge(DocsRouteLive, APICollection)
|
|
101
|
+
: APICollection;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Astro API Route - Converts the combined Effect API stack into an Astro route for serving the API documentation and endpoints.
|
|
105
|
+
*/
|
|
106
|
+
export const ALL: APIRoute = HttpApiToAstroRoute(APILive);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AuthAPIError } from '@withstudiocms/api-spec/auth';
|
|
2
|
+
import { Effect } from 'effect';
|
|
3
|
+
import { isValidEmail } from '#schemas/external-schemas';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility service for authentication-related operations in the StudioCMS API.
|
|
7
|
+
*/
|
|
8
|
+
export class AuthAPIUtils extends Effect.Service<AuthAPIUtils>()(
|
|
9
|
+
'studiocms/routes/api/auth/shared/AuthAPIUtils',
|
|
10
|
+
{
|
|
11
|
+
effect: Effect.gen(function* () {
|
|
12
|
+
return {
|
|
13
|
+
validateEmail: (email: string) =>
|
|
14
|
+
Effect.try({
|
|
15
|
+
try: () => isValidEmail(email),
|
|
16
|
+
catch: () =>
|
|
17
|
+
new AuthAPIError({
|
|
18
|
+
error: 'Failed to validate email.',
|
|
19
|
+
}),
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
}),
|
|
23
|
+
}
|
|
24
|
+
) {
|
|
25
|
+
static Provide = Effect.provide(this.Default);
|
|
26
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { sdkClient } from 'studiocms:client/apiClients';
|
|
2
|
+
import { HTTPClient } from '@withstudiocms/effect';
|
|
3
|
+
import { loadChangelog, semverCategories } from '@withstudiocms/internal_helpers/utils';
|
|
4
|
+
import { Data, Effect, pipe } from 'effect';
|
|
5
|
+
import type { List, Root } from 'mdast';
|
|
6
|
+
import { toMarkdown } from 'mdast-util-to-markdown';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom error class used for quick escaping from deep error handling in the Effect chain.
|
|
10
|
+
*/
|
|
11
|
+
export class ChangelogError extends Data.TaggedError('ChangelogError')<{ message: string }> {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Service for processing the changelog of StudioCMS.
|
|
15
|
+
*
|
|
16
|
+
* This service fetches the raw changelog from the GitHub repository, processes it to extract relevant information, and renders it using a partial endpoint.
|
|
17
|
+
*/
|
|
18
|
+
export class ProcessChangelog extends Effect.Service<ProcessChangelog>()('ProcessChangelog', {
|
|
19
|
+
effect: Effect.gen(function* () {
|
|
20
|
+
const httpClient = yield* HTTPClient;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fetches the raw changelog markdown from the GitHub repository. If the fetch fails, it returns a ChangelogError with details about the failure.
|
|
24
|
+
*/
|
|
25
|
+
const getRawChangelog = () =>
|
|
26
|
+
Effect.gen(function* () {
|
|
27
|
+
const data = yield* httpClient.get(
|
|
28
|
+
'https://raw.githubusercontent.com/withstudiocms/studiocms/refs/heads/main/packages/studiocms/CHANGELOG.md'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (data.status !== 200) {
|
|
32
|
+
return yield* new ChangelogError({
|
|
33
|
+
message: `Failed to fetch CHANGELOG.md: ${data.status} ${data.toString()}`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return yield* data.text;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Processes the raw changelog markdown to extract version information and changes, and then converts it back to markdown format. If any step in the processing fails, it returns a ChangelogError with details about the failure.
|
|
42
|
+
* @param raw The raw changelog markdown content.
|
|
43
|
+
* @returns The processed changelog in markdown format.
|
|
44
|
+
* @throws ChangelogError if processing fails at any step.
|
|
45
|
+
*
|
|
46
|
+
*/
|
|
47
|
+
const generateChangelog = (raw: string) =>
|
|
48
|
+
Effect.gen(function* () {
|
|
49
|
+
const ToProcess = yield* Effect.try(() => loadChangelog({ raw }));
|
|
50
|
+
|
|
51
|
+
const output: string[] = [];
|
|
52
|
+
|
|
53
|
+
const astEnd: Root = {
|
|
54
|
+
type: 'root',
|
|
55
|
+
children: [],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
for (const version of ToProcess.versions) {
|
|
59
|
+
const versionChanges: List = { type: 'list', children: [] };
|
|
60
|
+
|
|
61
|
+
for (const semverCategory of semverCategories) {
|
|
62
|
+
for (const listItem of version.changes[semverCategory].children) {
|
|
63
|
+
versionChanges.children.push(listItem);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (version.includes.size) {
|
|
68
|
+
versionChanges.children.push({
|
|
69
|
+
type: 'listItem',
|
|
70
|
+
children: [
|
|
71
|
+
{
|
|
72
|
+
type: 'paragraph',
|
|
73
|
+
children: [
|
|
74
|
+
{ type: 'text', value: `Includes: ${[...version.includes].join(', ')} ` },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!versionChanges.children.length) continue;
|
|
82
|
+
|
|
83
|
+
astEnd.children.push({
|
|
84
|
+
type: 'heading',
|
|
85
|
+
depth: 2,
|
|
86
|
+
children: [{ type: 'text', value: version.version }],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
astEnd.children.push(versionChanges);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const outputData = yield* Effect.try(() => toMarkdown(astEnd, { bullet: '-' }));
|
|
93
|
+
|
|
94
|
+
output.push(outputData);
|
|
95
|
+
|
|
96
|
+
const markdownString = output.join('\n');
|
|
97
|
+
|
|
98
|
+
return markdownString;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Renders the processed changelog markdown by sending it to a partial endpoint. If the rendering fails, it returns a ChangelogError with details about the failure.
|
|
103
|
+
*
|
|
104
|
+
* @param content The processed changelog markdown content to be rendered.
|
|
105
|
+
* @returns The rendered changelog content as returned by the partial endpoint.
|
|
106
|
+
*/
|
|
107
|
+
const renderChangelog = (content: string) =>
|
|
108
|
+
Effect.gen(function* () {
|
|
109
|
+
const client = yield* sdkClient;
|
|
110
|
+
|
|
111
|
+
return yield* client.utils
|
|
112
|
+
.renderMarkdown({
|
|
113
|
+
payload: {
|
|
114
|
+
content,
|
|
115
|
+
},
|
|
116
|
+
urlParams: {},
|
|
117
|
+
})
|
|
118
|
+
.pipe(Effect.map((response) => response.html));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Runs the entire changelog processing pipeline, which includes fetching the raw changelog, generating the processed markdown, and rendering it. If any step in the pipeline fails, it returns a ChangelogError with details about the failure.
|
|
123
|
+
*/
|
|
124
|
+
const runPipeline = () =>
|
|
125
|
+
pipe(
|
|
126
|
+
getRawChangelog(),
|
|
127
|
+
Effect.flatMap(generateChangelog),
|
|
128
|
+
Effect.flatMap(renderChangelog),
|
|
129
|
+
Effect.catchAll(
|
|
130
|
+
(error) =>
|
|
131
|
+
new ChangelogError({
|
|
132
|
+
message: `Failed to process changelog: ${error instanceof Error ? error.message : String(error)}`,
|
|
133
|
+
})
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
getRawChangelog,
|
|
139
|
+
generateChangelog,
|
|
140
|
+
renderChangelog,
|
|
141
|
+
runPipeline,
|
|
142
|
+
};
|
|
143
|
+
}),
|
|
144
|
+
dependencies: [HTTPClient.Default],
|
|
145
|
+
}) {
|
|
146
|
+
static Provide = Effect.provide(this.Default);
|
|
147
|
+
}
|