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
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { Notifications } from 'studiocms:notifier';
|
|
2
|
+
import { SDKCore } from 'studiocms:sdk';
|
|
3
|
+
import routeConfig from 'virtual:studiocms/route-config';
|
|
4
|
+
import { HttpApiBuilder } from '@effect/platform';
|
|
5
|
+
import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
|
|
6
|
+
import { CurrentUser } from '@withstudiocms/api-spec/astro-context';
|
|
7
|
+
import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
|
|
8
|
+
import { StudioCMSPageDataCategories, StudioCMSPageDataTags } from '@withstudiocms/sdk/tables';
|
|
9
|
+
import { Effect, Schema } from 'effect';
|
|
10
|
+
import {
|
|
11
|
+
categoriesToTaxonomyNodes,
|
|
12
|
+
tagsToTaxonomyNodes,
|
|
13
|
+
} from '#frontend/components/shared/taxonomy/shared.js';
|
|
14
|
+
import { sharedDBErrors, sharedNotifierErrors } from './_shared.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if the Dashboard API is enabled in the route configuration.
|
|
18
|
+
*/
|
|
19
|
+
const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Taxonomy Handlers for the Dashboard API
|
|
23
|
+
*/
|
|
24
|
+
export const TaxonomyHandlers = HttpApiBuilder.group(
|
|
25
|
+
StudioCMSDashboardApiSpec,
|
|
26
|
+
'taxonomy',
|
|
27
|
+
(handlers) =>
|
|
28
|
+
handlers
|
|
29
|
+
.handle(
|
|
30
|
+
'taxonomy',
|
|
31
|
+
Effect.fn(
|
|
32
|
+
function* ({ payload }) {
|
|
33
|
+
if (!dashboardAPIEnabled) {
|
|
34
|
+
return yield* new DashboardAPIError({
|
|
35
|
+
error: 'Dashboard API is disabled',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const [sdk, userData, notifier] = yield* Effect.all([
|
|
40
|
+
SDKCore,
|
|
41
|
+
CurrentUser,
|
|
42
|
+
Notifications,
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isEditor) {
|
|
46
|
+
return yield* new DashboardAPIError({ error: 'Unauthorized' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!payload.mode || !payload.type) {
|
|
50
|
+
return yield* new DashboardAPIError({ error: 'Invalid request payload' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const newId = yield* sdk.UTIL.Generators.generateRandomIDNumber(9);
|
|
54
|
+
|
|
55
|
+
switch (payload.type) {
|
|
56
|
+
case 'categories': {
|
|
57
|
+
const { mode, type: _, ...categoryData } = payload;
|
|
58
|
+
const decoder = Schema.encode(StudioCMSPageDataCategories.Select);
|
|
59
|
+
|
|
60
|
+
switch (mode) {
|
|
61
|
+
case 'create': {
|
|
62
|
+
const newCategory = yield* decoder({
|
|
63
|
+
...categoryData,
|
|
64
|
+
id: categoryData.id && categoryData.id > 0 ? categoryData.id : newId,
|
|
65
|
+
}).pipe(Effect.flatMap(sdk.POST.databaseEntry.categories));
|
|
66
|
+
|
|
67
|
+
if (!newCategory) {
|
|
68
|
+
return yield* new DashboardAPIError({ error: 'Failed to create category' });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
yield* notifier
|
|
72
|
+
.sendEditorNotification('new_category', categoryData.name)
|
|
73
|
+
.pipe(
|
|
74
|
+
Effect.catchAll(
|
|
75
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
message: 'Category created successfully',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case 'edit': {
|
|
84
|
+
if (categoryData.id === categoryData.parent) {
|
|
85
|
+
return yield* new DashboardAPIError({
|
|
86
|
+
error: 'Category cannot be its own parent',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const updatedCategory = yield* decoder(categoryData).pipe(
|
|
91
|
+
Effect.flatMap(sdk.UPDATE.categories)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (!updatedCategory) {
|
|
95
|
+
return yield* new DashboardAPIError({ error: 'Failed to update category' });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
yield* notifier
|
|
99
|
+
.sendEditorNotification('update_category', categoryData.name)
|
|
100
|
+
.pipe(
|
|
101
|
+
Effect.catchAll(
|
|
102
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
message: 'Category updated successfully',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return yield* new DashboardAPIError({ error: 'Invalid mode' });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
case 'tags': {
|
|
115
|
+
const { mode, type: _, ...tagData } = payload;
|
|
116
|
+
const decoder = Schema.encode(StudioCMSPageDataTags.Select);
|
|
117
|
+
|
|
118
|
+
switch (mode) {
|
|
119
|
+
case 'create': {
|
|
120
|
+
const newTag = yield* decoder({
|
|
121
|
+
...tagData,
|
|
122
|
+
id: tagData.id && tagData.id > 0 ? tagData.id : newId,
|
|
123
|
+
}).pipe(Effect.flatMap(sdk.POST.databaseEntry.tags));
|
|
124
|
+
|
|
125
|
+
if (!newTag) {
|
|
126
|
+
return yield* new DashboardAPIError({ error: 'Failed to create tag' });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
yield* notifier
|
|
130
|
+
.sendEditorNotification('new_tag', tagData.name)
|
|
131
|
+
.pipe(
|
|
132
|
+
Effect.catchAll(
|
|
133
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
message: 'Tag created successfully',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
case 'edit': {
|
|
142
|
+
const updatedTag = yield* decoder(tagData).pipe(
|
|
143
|
+
Effect.flatMap(sdk.UPDATE.tags)
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (!updatedTag) {
|
|
147
|
+
return yield* new DashboardAPIError({ error: 'Failed to update tag' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
yield* notifier
|
|
151
|
+
.sendEditorNotification('update_tag', tagData.name)
|
|
152
|
+
.pipe(
|
|
153
|
+
Effect.catchAll(
|
|
154
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
message: 'Tag updated successfully',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
return yield* new DashboardAPIError({ error: 'Invalid mode' });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
default:
|
|
167
|
+
return yield* new DashboardAPIError({ error: 'Invalid taxonomy type' });
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
Notifications.Provide,
|
|
171
|
+
Effect.catchTags({
|
|
172
|
+
...sharedDBErrors,
|
|
173
|
+
...sharedNotifierErrors,
|
|
174
|
+
GeneratorError: () => new DashboardAPIError({ error: 'Failed to generate new ID' }),
|
|
175
|
+
ParseError: () => new DashboardAPIError({ error: 'Failed to parse category data' }),
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
.handle(
|
|
180
|
+
'taxonomyDelete',
|
|
181
|
+
Effect.fn(
|
|
182
|
+
function* ({ payload: { id, type } }) {
|
|
183
|
+
if (!dashboardAPIEnabled) {
|
|
184
|
+
return yield* new DashboardAPIError({
|
|
185
|
+
error: 'Dashboard API is disabled',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const [sdk, userData, notify] = yield* Effect.all([
|
|
190
|
+
SDKCore,
|
|
191
|
+
CurrentUser,
|
|
192
|
+
Notifications,
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isAdmin) {
|
|
196
|
+
return yield* new DashboardAPIError({ error: 'Unauthorized' });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
switch (type) {
|
|
200
|
+
case 'categories': {
|
|
201
|
+
const allCategories = yield* sdk.GET.categories.getAll();
|
|
202
|
+
|
|
203
|
+
const isParentCategory = allCategories.some((c) => c.parent === id);
|
|
204
|
+
|
|
205
|
+
if (isParentCategory) {
|
|
206
|
+
return yield* new DashboardAPIError({
|
|
207
|
+
error: 'Cannot delete category that has child categories',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const foundCategory = allCategories.find((c) => c.id === id);
|
|
212
|
+
|
|
213
|
+
if (!foundCategory) {
|
|
214
|
+
return yield* new DashboardAPIError({ error: 'Category not found' });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const deletionSuccess = yield* sdk.DELETE.categories(id);
|
|
218
|
+
|
|
219
|
+
if (!deletionSuccess) {
|
|
220
|
+
return yield* new DashboardAPIError({ error: 'Failed to delete category' });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
yield* notify
|
|
224
|
+
.sendEditorNotification('delete_category', foundCategory.name)
|
|
225
|
+
.pipe(
|
|
226
|
+
Effect.catchAll(
|
|
227
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
228
|
+
)
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
message: 'Category deleted successfully',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case 'tags': {
|
|
236
|
+
const foundTag = yield* sdk.GET.tags.byId(id);
|
|
237
|
+
|
|
238
|
+
if (!foundTag) {
|
|
239
|
+
return yield* new DashboardAPIError({ error: 'Tag not found' });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const deletionSuccess = yield* sdk.DELETE.tags(id);
|
|
243
|
+
|
|
244
|
+
if (!deletionSuccess) {
|
|
245
|
+
return yield* new DashboardAPIError({ error: 'Failed to delete tag' });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
yield* notify
|
|
249
|
+
.sendEditorNotification('delete_tag', foundTag.name)
|
|
250
|
+
.pipe(
|
|
251
|
+
Effect.catchAll(
|
|
252
|
+
() => new DashboardAPIError({ error: 'Failed to send notification' })
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
message: 'Tag deleted successfully',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
return yield* new DashboardAPIError({ error: 'Invalid taxonomy type' });
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
Notifications.Provide,
|
|
265
|
+
Effect.catchTags({
|
|
266
|
+
...sharedDBErrors,
|
|
267
|
+
...sharedNotifierErrors,
|
|
268
|
+
})
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
.handle('taxonomySearch', () =>
|
|
272
|
+
!dashboardAPIEnabled
|
|
273
|
+
? Effect.fail(new DashboardAPIError({ error: 'Dashboard API is disabled' }))
|
|
274
|
+
: SDKCore.pipe(
|
|
275
|
+
Effect.flatMap((sdk) =>
|
|
276
|
+
Effect.all([
|
|
277
|
+
sdk.GET.categories.getAll().pipe(Effect.map(categoriesToTaxonomyNodes)),
|
|
278
|
+
sdk.GET.tags.getAll().pipe(Effect.map(tagsToTaxonomyNodes)),
|
|
279
|
+
])
|
|
280
|
+
),
|
|
281
|
+
Effect.flatMap(([categories, tags]) => Effect.succeed([...categories, ...tags])),
|
|
282
|
+
Effect.catchTags(sharedDBErrors)
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SDKCore } from 'studiocms:sdk';
|
|
2
|
+
import templateEngine from 'studiocms:template-engine';
|
|
3
|
+
import routeConfig from 'virtual:studiocms/route-config';
|
|
4
|
+
import { HttpApiBuilder } from '@effect/platform';
|
|
5
|
+
import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
|
|
6
|
+
import { CurrentUser } from '@withstudiocms/api-spec/astro-context';
|
|
7
|
+
import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
|
|
8
|
+
import { Effect } from 'effect';
|
|
9
|
+
import { sharedDBErrors } from './_shared.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if the Dashboard API is enabled in the route configuration.
|
|
13
|
+
*/
|
|
14
|
+
const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Templates Handlers for the Dashboard API
|
|
18
|
+
*/
|
|
19
|
+
export const TemplatesHandlers = HttpApiBuilder.group(
|
|
20
|
+
StudioCMSDashboardApiSpec,
|
|
21
|
+
'templates',
|
|
22
|
+
(handlers) =>
|
|
23
|
+
handlers.handle(
|
|
24
|
+
'updateEmailTemplates',
|
|
25
|
+
Effect.fn(
|
|
26
|
+
function* ({ payload }) {
|
|
27
|
+
if (!dashboardAPIEnabled) {
|
|
28
|
+
return yield* new DashboardAPIError({
|
|
29
|
+
error: 'Dashboard API is disabled',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [sdk, engine, userData] = yield* Effect.all([SDKCore, templateEngine, CurrentUser]);
|
|
34
|
+
|
|
35
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isAdmin) {
|
|
36
|
+
return yield* new DashboardAPIError({ error: 'Unauthorized' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const keys = engine.availableTemplates;
|
|
40
|
+
type Keys = (typeof keys)[number];
|
|
41
|
+
const updates: Partial<Record<Keys, string>> = {};
|
|
42
|
+
for (const key of keys) {
|
|
43
|
+
if (key in payload) {
|
|
44
|
+
updates[key] = payload[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Object.keys(updates).length === 0) {
|
|
49
|
+
return yield* new DashboardAPIError({
|
|
50
|
+
error: 'No valid templates provided for update.',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const updatedConfig = yield* sdk.CONFIG.templateConfig.update(updates);
|
|
55
|
+
|
|
56
|
+
if (!updatedConfig) {
|
|
57
|
+
return yield* new DashboardAPIError({ error: 'Failed to update templates.' });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
message: 'Templates updated successfully',
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
Effect.catchTags({
|
|
65
|
+
...sharedDBErrors,
|
|
66
|
+
TemplateEngineError: (cause) =>
|
|
67
|
+
new DashboardAPIError({ error: cause.message || 'Template Engine Error' }),
|
|
68
|
+
DeepmergeError: (cause) =>
|
|
69
|
+
new DashboardAPIError({
|
|
70
|
+
error: cause.message || 'Failed to merge template config updates',
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
);
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { developerConfig } from 'studiocms:config';
|
|
2
|
+
import { Notifications } from 'studiocms:notifier';
|
|
3
|
+
import { SDKCore } from 'studiocms:sdk';
|
|
4
|
+
import type { tsPermissionsSelect } from 'studiocms:sdk/types';
|
|
5
|
+
import routeConfig from 'virtual:studiocms/route-config';
|
|
6
|
+
import { HttpApiBuilder } from '@effect/platform';
|
|
7
|
+
import { StudioCMSDashboardApiSpec } from '@withstudiocms/api-spec';
|
|
8
|
+
import { CurrentUser } from '@withstudiocms/api-spec/astro-context';
|
|
9
|
+
import { DashboardAPIError } from '@withstudiocms/api-spec/dashboard';
|
|
10
|
+
import { availablePermissionRanks } from '@withstudiocms/auth-kit/types';
|
|
11
|
+
import { Effect } from 'effect';
|
|
12
|
+
import { ValidRanks } from '#consts';
|
|
13
|
+
import { sharedDBErrors, sharedNotifierErrors } from './_shared.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if the Dashboard API is enabled in the route configuration.
|
|
17
|
+
*/
|
|
18
|
+
const dashboardAPIEnabled = routeConfig.dashboardAPIEnabled;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Users Handlers for the Dashboard API
|
|
22
|
+
*/
|
|
23
|
+
export const UsersHandlers = HttpApiBuilder.group(StudioCMSDashboardApiSpec, 'users', (handlers) =>
|
|
24
|
+
handlers
|
|
25
|
+
.handle(
|
|
26
|
+
'updateUser',
|
|
27
|
+
Effect.fn(
|
|
28
|
+
function* ({ payload: { id, rank, emailVerified } }) {
|
|
29
|
+
if (!dashboardAPIEnabled) {
|
|
30
|
+
return yield* new DashboardAPIError({
|
|
31
|
+
error: 'Dashboard API is disabled',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (developerConfig.demoMode !== false) {
|
|
36
|
+
return yield* new DashboardAPIError({
|
|
37
|
+
error: 'Demo mode is enabled, this action is not allowed.',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const [sdk, notifier, userData] = yield* Effect.all([
|
|
42
|
+
SDKCore,
|
|
43
|
+
Notifications,
|
|
44
|
+
CurrentUser,
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isAdmin) {
|
|
48
|
+
return yield* new DashboardAPIError({
|
|
49
|
+
error: 'Unauthorized',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!ValidRanks.has(rank) || rank === 'unknown') {
|
|
54
|
+
return yield* new DashboardAPIError({
|
|
55
|
+
error: 'Invalid permission rank provided.',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const insertData: tsPermissionsSelect = {
|
|
60
|
+
user: id,
|
|
61
|
+
rank,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const user = yield* sdk.GET.users.byId(id);
|
|
65
|
+
|
|
66
|
+
if (!user) {
|
|
67
|
+
return yield* new DashboardAPIError({
|
|
68
|
+
error: 'User not found.',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const userPerms = availablePermissionRanks.indexOf(userData.permissionLevel);
|
|
73
|
+
const targetCurrentLevel = availablePermissionRanks.indexOf(
|
|
74
|
+
user.permissionsData?.rank || 'unknown'
|
|
75
|
+
);
|
|
76
|
+
const targetNewLevel = availablePermissionRanks.indexOf(rank);
|
|
77
|
+
|
|
78
|
+
if (userPerms === -1 || targetCurrentLevel === -1 || targetNewLevel === -1) {
|
|
79
|
+
return yield* new DashboardAPIError({
|
|
80
|
+
error: 'Invalid permission rank encountered.',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (userPerms <= targetCurrentLevel) {
|
|
85
|
+
return yield* new DashboardAPIError({
|
|
86
|
+
error: 'You do not have permission to modify this user.',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (userPerms <= targetNewLevel) {
|
|
91
|
+
return yield* new DashboardAPIError({
|
|
92
|
+
error: 'You cannot assign a permission rank equal to or higher than your own.',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const updatedData = yield* sdk.UPDATE.permissions(insertData);
|
|
97
|
+
|
|
98
|
+
if (!updatedData) {
|
|
99
|
+
return yield* new DashboardAPIError({
|
|
100
|
+
error: 'Failed to update user permissions.',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof emailVerified === 'boolean') {
|
|
105
|
+
yield* sdk.AUTH.user.update({
|
|
106
|
+
userId: id,
|
|
107
|
+
userData: {
|
|
108
|
+
id,
|
|
109
|
+
name: user.name,
|
|
110
|
+
username: user.username,
|
|
111
|
+
updatedAt: new Date().toISOString(),
|
|
112
|
+
createdAt: undefined,
|
|
113
|
+
emailVerified,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
yield* Effect.all([
|
|
119
|
+
notifier.sendUserNotification('account_updated', id),
|
|
120
|
+
notifier.sendAdminNotification('user_updated', user.username),
|
|
121
|
+
]).pipe(
|
|
122
|
+
Effect.catchAll(
|
|
123
|
+
() =>
|
|
124
|
+
new DashboardAPIError({
|
|
125
|
+
error: 'Failed to send notifications',
|
|
126
|
+
})
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
message: 'User updated successfully.',
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
Notifications.Provide,
|
|
135
|
+
Effect.catchTags({
|
|
136
|
+
...sharedDBErrors,
|
|
137
|
+
...sharedNotifierErrors,
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
.handle(
|
|
142
|
+
'deleteUser',
|
|
143
|
+
Effect.fn(
|
|
144
|
+
function* ({ payload: { userId, username, usernameConfirm } }) {
|
|
145
|
+
if (!dashboardAPIEnabled) {
|
|
146
|
+
return yield* new DashboardAPIError({
|
|
147
|
+
error: 'Dashboard API is disabled',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (developerConfig.demoMode !== false) {
|
|
152
|
+
return yield* new DashboardAPIError({
|
|
153
|
+
error: 'Demo mode is enabled, this action is not allowed.',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const [sdk, notifier, userData] = yield* Effect.all([
|
|
158
|
+
SDKCore,
|
|
159
|
+
Notifications,
|
|
160
|
+
CurrentUser,
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isAdmin) {
|
|
164
|
+
return yield* new DashboardAPIError({
|
|
165
|
+
error: 'Unauthorized',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (username !== usernameConfirm) {
|
|
170
|
+
return yield* new DashboardAPIError({
|
|
171
|
+
error: 'Username confirmation does not match.',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const targetUser = yield* sdk.GET.users.byId(userId);
|
|
176
|
+
|
|
177
|
+
if (!targetUser) {
|
|
178
|
+
return yield* new DashboardAPIError({
|
|
179
|
+
error: 'User not found',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (targetUser.username !== username) {
|
|
184
|
+
return yield* new DashboardAPIError({
|
|
185
|
+
error: 'Username confirmation does not match target user',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (userData.user?.id && userData.user.id === userId) {
|
|
190
|
+
return yield* new DashboardAPIError({
|
|
191
|
+
error: 'You cannot delete your own account.',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const actorPerms = availablePermissionRanks.indexOf(userData.permissionLevel);
|
|
196
|
+
const targetPerms = availablePermissionRanks.indexOf(
|
|
197
|
+
targetUser.permissionsData?.rank || 'unknown'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (actorPerms === -1 || targetPerms === -1) {
|
|
201
|
+
return yield* new DashboardAPIError({
|
|
202
|
+
error: 'Invalid permission rank encountered.',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (actorPerms <= targetPerms) {
|
|
207
|
+
return yield* new DashboardAPIError({
|
|
208
|
+
error: 'You do not have permission to delete this user.',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (targetUser.permissionsData?.rank === 'owner') {
|
|
213
|
+
const allUsers = yield* sdk.GET.users.all();
|
|
214
|
+
const ownerCount = allUsers.filter((u) => u.permissionsData?.rank === 'owner').length;
|
|
215
|
+
|
|
216
|
+
if (ownerCount <= 1) {
|
|
217
|
+
return yield* new DashboardAPIError({
|
|
218
|
+
error: 'Cannot delete the last owner account.',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const deleteResult = yield* sdk.DELETE.user(userId);
|
|
224
|
+
|
|
225
|
+
if (!deleteResult) {
|
|
226
|
+
return yield* new DashboardAPIError({
|
|
227
|
+
error: 'Failed to delete user.',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (deleteResult.status === 'error') {
|
|
232
|
+
return yield* new DashboardAPIError({
|
|
233
|
+
error: deleteResult.message || 'Failed to delete user.',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
yield* notifier.sendAdminNotification('user_deleted', targetUser.username).pipe(
|
|
238
|
+
Effect.catchAll(
|
|
239
|
+
() =>
|
|
240
|
+
new DashboardAPIError({
|
|
241
|
+
error: 'Failed to send notifications',
|
|
242
|
+
})
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
message: deleteResult.message || 'User deleted successfully.',
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
Notifications.Provide,
|
|
251
|
+
Effect.catchTags({
|
|
252
|
+
...sharedDBErrors,
|
|
253
|
+
...sharedNotifierErrors,
|
|
254
|
+
})
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
.handle(
|
|
258
|
+
'updateUserNotifications',
|
|
259
|
+
Effect.fn(function* ({ payload: { id, notifications } }) {
|
|
260
|
+
if (!dashboardAPIEnabled) {
|
|
261
|
+
return yield* new DashboardAPIError({
|
|
262
|
+
error: 'Dashboard API is disabled',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (developerConfig.demoMode !== false) {
|
|
267
|
+
return yield* new DashboardAPIError({
|
|
268
|
+
error: 'Demo mode is enabled, this action is not allowed.',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const [sdk, userData] = yield* Effect.all([SDKCore, CurrentUser]);
|
|
273
|
+
|
|
274
|
+
if (!userData.isLoggedIn || !userData.userPermissionLevel.isVisitor) {
|
|
275
|
+
return yield* new DashboardAPIError({
|
|
276
|
+
error: 'Unauthorized',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const existingUser = yield* sdk.GET.users.byId(id);
|
|
281
|
+
|
|
282
|
+
if (!existingUser) {
|
|
283
|
+
return yield* new DashboardAPIError({
|
|
284
|
+
error: 'User not found',
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const updatedData = yield* sdk.AUTH.user.update({
|
|
289
|
+
userId: id,
|
|
290
|
+
userData: {
|
|
291
|
+
id,
|
|
292
|
+
name: existingUser.name,
|
|
293
|
+
username: existingUser.username,
|
|
294
|
+
updatedAt: new Date().toISOString(),
|
|
295
|
+
emailVerified: existingUser.emailVerified,
|
|
296
|
+
createdAt: undefined,
|
|
297
|
+
notifications,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (!updatedData) {
|
|
302
|
+
return yield* new DashboardAPIError({
|
|
303
|
+
error: 'Failed to update user notifications',
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
message: 'User notifications updated successfully',
|
|
309
|
+
};
|
|
310
|
+
}, Effect.catchTags(sharedDBErrors))
|
|
311
|
+
)
|
|
312
|
+
);
|