diff --git a/packages/studiocms_auth/src/utils/routeBuilder.ts b/packages/studiocms_auth/src/utils/routeBuilder.ts index 030105b2bf..085ab3a4fe 100644 --- a/packages/studiocms_auth/src/utils/routeBuilder.ts +++ b/packages/studiocms_auth/src/utils/routeBuilder.ts @@ -1,12 +1,10 @@ import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; -import { removeLeadingTrailingSlashes } from '@studiocms/core/lib'; +import { makeAPIRoute, removeLeadingTrailingSlashes } from '@studiocms/core/lib'; import { DashboardStrings } from '@studiocms/core/strings'; import { defineUtility } from 'astro-integration-kit'; import type { StudioCMSAuthOptions } from '../schema'; -export const makeStudioCMSAuthAPIRoute = (route: string) => { - return `studiocms_api/auth/${route}`; -}; +const apiRoute = makeAPIRoute('auth'); export const makeDashboardRoute = (route: string, options: StudioCMSAuthOptions) => { // Get the dashboard route override from the options @@ -80,7 +78,7 @@ export const injectAuthAPIRoutes = defineUtility('astro:config:setup')( } injectRoute({ - pattern: makeStudioCMSAuthAPIRoute(pattern), + pattern: apiRoute(pattern), entrypoint, prerender: false, }); diff --git a/packages/studiocms_core/src/lib/index.ts b/packages/studiocms_core/src/lib/index.ts index 6f259cf798..6ab77057bd 100644 --- a/packages/studiocms_core/src/lib/index.ts +++ b/packages/studiocms_core/src/lib/index.ts @@ -3,3 +3,4 @@ export * from './jsonUtils'; export * from './pathGenerators'; export * from './removeLeadingTrailingSlashes'; export * from './urlGen'; +export * from './makeAPIRoute'; diff --git a/packages/studiocms_core/src/lib/makeAPIRoute.ts b/packages/studiocms_core/src/lib/makeAPIRoute.ts new file mode 100644 index 0000000000..d5996151aa --- /dev/null +++ b/packages/studiocms_core/src/lib/makeAPIRoute.ts @@ -0,0 +1,5 @@ +export function makeAPIRoute(path: 'auth' | 'dashboard') { + return function api(route: string) { + return `studiocms_api/${path}/${route}`; + }; +} diff --git a/packages/studiocms_core/src/lib/routeMap.ts b/packages/studiocms_core/src/lib/routeMap.ts index 4b3cce3179..4f24858874 100644 --- a/packages/studiocms_core/src/lib/routeMap.ts +++ b/packages/studiocms_core/src/lib/routeMap.ts @@ -1,6 +1,7 @@ import Config from 'studiocms:config'; // import { dashboardPageLinks } from 'virtual:studiocms/pluginSystem'; import type { SideBarLink } from '../schemas'; +import { makeAPIRoute } from './makeAPIRoute'; import urlGenFactory from './urlGen'; const { @@ -27,48 +28,47 @@ export function makeDashboardRoute(route?: string | undefined): string { return urlGenFactory(true, route, dashboardRouteOverride); } -export function makeStudioCMSAPIRoute(route: string): string { - return urlGenFactory(false, `studiocms_api/${route}`); -} +const authAPIRoute = makeAPIRoute('auth'); +const dashboardAPIRoute = makeAPIRoute('dashboard'); export const StudioCMSRoutes = { mainLinks: { baseSiteURL: makeNonDashboardRoute(), dashboardIndex: makeDashboardRoute(), userProfile: makeDashboardRoute('profile/'), - pageNew: makeDashboardRoute('new/page/'), - pageEdit: makeDashboardRoute('page-list/'), + contentManagement: makeDashboardRoute('content-management/'), + createPage: makeDashboardRoute('create-page/'), siteConfiguration: makeDashboardRoute('configuration/'), - configurationAdmins: makeDashboardRoute('configuration/admins/'), + userManagement: makeDashboardRoute('user-management/'), }, authLinks: { loginURL: makeDashboardRoute('login'), logoutURL: makeDashboardRoute('logout'), signupURL: makeDashboardRoute('signup'), - loginAPI: makeStudioCMSAPIRoute('auth/login'), // /studiocms_api/auth/login - logoutAPI: makeStudioCMSAPIRoute('auth/logout'), // /studiocms_api/auth/logout - registerAPI: makeStudioCMSAPIRoute('auth/register'), // /studiocms_api/auth/register - githubIndex: makeStudioCMSAPIRoute('auth/github'), // /studiocms_api/auth/github - githubCallback: makeStudioCMSAPIRoute('auth/github/callback'), // /studiocms_api/auth/github/callback - discordIndex: makeStudioCMSAPIRoute('auth/discord'), // /studiocms_api/auth/discord - discordCallback: makeStudioCMSAPIRoute('auth/discord/callback'), // /studiocms_api/auth/discord/callback - googleIndex: makeStudioCMSAPIRoute('auth/google'), // /studiocms_api/auth/google - googleCallback: makeStudioCMSAPIRoute('auth/google/callback'), // /studiocms_api/auth/google/callback - auth0Index: makeStudioCMSAPIRoute('auth/auth0'), // /studiocms_api/auth/auth0 - auth0Callback: makeStudioCMSAPIRoute('auth/auth0/callback'), // /studiocms_api/auth/auth0/callback + loginAPI: authAPIRoute('login'), + logoutAPI: authAPIRoute('logout'), + registerAPI: authAPIRoute('register'), + githubIndex: authAPIRoute('github'), + githubCallback: authAPIRoute('github/callback'), + discordIndex: authAPIRoute('discord'), + discordCallback: authAPIRoute('discord/callback'), + googleIndex: authAPIRoute('google'), + googleCallback: authAPIRoute('google/callback'), + auth0Index: authAPIRoute('auth0'), + auth0Callback: authAPIRoute('auth0/callback'), }, endpointLinks: { partials: { - livePreviewBox: makeStudioCMSAPIRoute('dashboard/liverender'), // /studiocms_api/dashboard/liverender + livePreviewBox: dashboardAPIRoute('liverender'), }, config: { - siteConfig: makeStudioCMSAPIRoute('dashboard/config/site'), // /studiocms_api/dashboard/config/site - adminConfig: makeStudioCMSAPIRoute('dashboard/config/admin'), // /studiocms_api/dashboard/config/admin + siteConfig: dashboardAPIRoute('config/site'), + adminConfig: dashboardAPIRoute('config/admin'), }, pages: { - createPages: makeStudioCMSAPIRoute('dashboard/pages/create'), // /studiocms_api/dashboard/pages/create - editPages: makeStudioCMSAPIRoute('dashboard/pages/edit'), // /studiocms_api/dashboard/pages/edit - deletePages: makeStudioCMSAPIRoute('dashboard/pages/delete'), // /studiocms_api/dashboard/pages/delete + createPages: dashboardAPIRoute('pages/create'), + editPages: dashboardAPIRoute('pages/edit'), + deletePages: dashboardAPIRoute('pages/delete'), }, }, }; @@ -97,16 +97,16 @@ const defaultDashboardPageLinks: SideBarLink[] = [ icon: 'user', }, { - id: 'new-page', - href: StudioCMSRoutes.mainLinks.pageNew, - text: 'Create New Page', + id: 'content-management', + href: StudioCMSRoutes.mainLinks.contentManagement, + text: 'Content Management', minPermissionLevel: 'editor', icon: 'plus', }, { id: 'edit-pages', - href: StudioCMSRoutes.mainLinks.pageEdit, - text: 'Edit Pages', + href: StudioCMSRoutes.mainLinks.createPage, + text: 'Create Page', minPermissionLevel: 'editor', icon: 'pencil-square', }, @@ -117,6 +117,13 @@ const defaultDashboardPageLinks: SideBarLink[] = [ minPermissionLevel: 'admin', icon: 'cog-6-tooth', }, + { + id: 'user-management', + href: StudioCMSRoutes.mainLinks.userManagement, + text: 'User Management', + minPermissionLevel: 'admin', + icon: 'cog-6-tooth', + }, ]; // Side bar links map diff --git a/packages/studiocms_core/src/stubs/lib.ts b/packages/studiocms_core/src/stubs/lib.ts index a8fe831762..2ed6b4159c 100644 --- a/packages/studiocms_core/src/stubs/lib.ts +++ b/packages/studiocms_core/src/stubs/lib.ts @@ -95,10 +95,6 @@ dtsFile.addModule('studiocms:lib', { name: 'makeDashboardRoute', typeDef: `typeof import('${resolve('../lib/routeMap.ts')}').makeDashboardRoute`, }, - { - name: 'makeStudioCMSAPIRoute', - typeDef: `typeof import('${resolve('../lib/routeMap.ts')}').makeStudioCMSAPIRoute`, - }, { name: 'StudioCMSRoutes', typeDef: `typeof import('${resolve('../lib/routeMap.ts')}').StudioCMSRoutes`, diff --git a/packages/studiocms_dashboard/src/integration.ts b/packages/studiocms_dashboard/src/integration.ts index 00eba61472..79b6f2b7a4 100644 --- a/packages/studiocms_dashboard/src/integration.ts +++ b/packages/studiocms_dashboard/src/integration.ts @@ -17,7 +17,7 @@ export default defineIntegration({ const { resolve } = createResolver(import.meta.url); // Declaration for Web Vitals DTS File - let WEBVITALSDTSFILE: InjectedType; + let WebVitalsDtsFile: InjectedType; return { hooks: { @@ -119,63 +119,46 @@ export default defineIntegration({ pattern: '/test', entrypoint: resolve('./routes/test.astro'), }, + // { + // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, + // pattern: '/content-management', + // entrypoint: resolve('./routes/content-management.astro'), + // }, + // { + // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, + // pattern: '/create-page', + // entrypoint: resolve('./routes/create-page.astro'), + // }, + // { + // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, + // pattern: '/profile', + // entrypoint: resolve('./routes/profile.astro'), + // }, + // { + // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, + // pattern: '/configuration', + // entrypoint: resolve('./routes/configuration.astro'), + // }, + // { + // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, + // pattern: '/user-management', + // entrypoint: resolve('./routes/user-management.astro'), + // }, ], }); - // // OLD ROUTES - TO BE REMOVED - // injectRouteArray(params, { - // options, - // routes: [ - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'profile/', - // entrypoint: resolve('./routes/profile.astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'configuration', - // entrypoint: resolve('./routes/configuration/index.astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'configuration/admins', - // entrypoint: resolve('./routes/configuration/admins.astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'new/page', - // entrypoint: resolve('./routes/create-page.astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'page-list', - // entrypoint: resolve('./routes/page-list.astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'edit/pages/[...id]', - // entrypoint: resolve('./routes/edit-pages/[...id].astro'), - // }, - // { - // enabled: options.dashboardConfig.dashboardEnabled && !options.dbStartPage, - // pattern: 'delete/pages/[...id]', - // entrypoint: resolve('./routes/delete-pages/[...id].astro'), - // }, - // ], - // }); - // Check for `@astrojs/web-vitals` Integration const { webVitalDtsFile } = checkForWebVitals(params, { name, verbose }); // Set the Web Vitals DTS File - WEBVITALSDTSFILE = webVitalDtsFile; + WebVitalsDtsFile = webVitalDtsFile; // Log that the setup is complete integrationLogger({ logger, logLevel: 'info', verbose }, DashboardStrings.SetupComplete); }, 'astro:config:done': async ({ injectTypes }) => { // Inject the Web Vitals DTS File - injectTypes(WEBVITALSDTSFILE); + injectTypes(WebVitalsDtsFile); }, 'astro:server:start': async ({ logger }) => { // Display Console Message if dbStartPage(First Time DB Initialization) is enabled diff --git a/packages/studiocms_dashboard/src/routes/site-config.astro b/packages/studiocms_dashboard/src/routes/configuration.astro similarity index 93% rename from packages/studiocms_dashboard/src/routes/site-config.astro rename to packages/studiocms_dashboard/src/routes/configuration.astro index a1b18c025d..c0b9d6c70f 100644 --- a/packages/studiocms_dashboard/src/routes/site-config.astro +++ b/packages/studiocms_dashboard/src/routes/configuration.astro @@ -19,7 +19,7 @@ const t = useTranslations(lang, '@studiocms/dashboard:index'); // const webVitals = await getWebVitals(); --- - + @@ -26,6 +23,9 @@ const t = useTranslations(lang, '@studiocms/dashboard:index'); title={makePageTitle(t('title'), dbConfig)} description={dbConfig.description} > - - {t('sub-header')} +Sus + +
+ +
diff --git a/packages/studiocms_dashboard/src/routes/profile.astro b/packages/studiocms_dashboard/src/routes/profile.astro index a1b18c025d..f3863567da 100644 --- a/packages/studiocms_dashboard/src/routes/profile.astro +++ b/packages/studiocms_dashboard/src/routes/profile.astro @@ -19,7 +19,7 @@ const t = useTranslations(lang, '@studiocms/dashboard:index'); // const webVitals = await getWebVitals(); --- - + { // Check if testing and demo mode is enabled @@ -21,26 +15,22 @@ export async function POST(context: APIContext): Promise { return simpleResponse(400, 'Testing and demo mode is enabled, this action is disabled.'); } - // Map Locals - // const locals = context.locals; - const userdata = await getUserData(context); + const userData = await getUserData(context); // Check if user is logged in - if (!userdata.isLoggedIn) { + if (!userData.isLoggedIn) { return simpleResponse(403, 'Unauthorized'); } // Check if user has permission - if (userdata.isLoggedIn) { - const { permissionLevel } = userdata; - if (permissionLevel !== 'admin') { - return simpleResponse(403, 'Unauthorized'); - } + const isAuthorized = await verifyUserPermissionLevel(userData, 'admin'); + if (!isAuthorized) { + return simpleResponse(403, 'Unauthorized'); } // Get form data const formData = await context.request.formData(); - const newUser = formData.get('username')?.toString(); + const newUser = formData.get('userid')?.toString(); const rank = formData.get('rank')?.toString(); // Check if newUser and rank Exists @@ -49,28 +39,30 @@ export async function POST(context: APIContext): Promise { return simpleResponse(400, 'Invalid User or Rank'); } - // Check if User already exists - const currentAdmins = await db.select().from(tsPermissions); - for (const admin of currentAdmins) { - if (admin.user === newUser) { - logger.error('Admin already exists'); - return simpleResponse(400, 'Admin already exists'); - } + // Check userID + const userExists = await studioCMS_SDK.GET.databaseEntry.users.byId(newUser); + + if (!userExists) { + logger.error('User does not exist'); + return simpleResponse(400, 'User does not exist'); } - // Update Database try { - await db.insert(tsPermissions).values({ user: newUser, rank: rank }).returning(); + const userHasRank = await studioCMS_SDK.AUTH.permission.currentStatus(newUser); + + if (userHasRank) { + await studioCMS_SDK.UPDATE.permissions({ user: userHasRank.user, rank: rank }); + logger.info('New Admin Added'); + return simpleResponse(200, 'New Admin Added'); + } + + await studioCMS_SDK.POST.databaseEntry.permissions(newUser, rank); + logger.info('New Admin Added'); + return simpleResponse(200, 'New Admin Added'); } catch (error) { - // Log error if (error instanceof Error) { logger.error(error.message); } - // Return Error Response return simpleResponse(500, 'Error updating Admin list'); } - - // Return Response - logger.info('New Admin Added'); - return simpleResponse(200, 'New Admin Added'); } diff --git a/packages/studiocms_dashboard/src/routes/studiocms_api/config/site.ts b/packages/studiocms_dashboard/src/routes/studiocms_api/config/site.ts index 508c64566a..708c5cd9f1 100644 --- a/packages/studiocms_dashboard/src/routes/studiocms_api/config/site.ts +++ b/packages/studiocms_dashboard/src/routes/studiocms_api/config/site.ts @@ -1,17 +1,12 @@ import { logger } from '@it-astro:logger:studiocms-dashboard'; -// import { authHelper } from 'studiocms:auth/helpers'; -import Config from 'studiocms:config'; +import { getUserData, verifyUserPermissionLevel } from 'studiocms:auth/lib/user'; +import { developerConfig } from 'studiocms:config'; +import studioCMS_SDK from 'studiocms:sdk'; +import { CMSSiteConfigId } from '@studiocms/core/consts'; import type { APIContext } from 'astro'; -import { astroDb } from '../../../utils/astroDb'; import { simpleResponse } from '../../../utils/simpleResponse'; -import { getUserData } from 'studiocms:auth/lib/user'; - -const { - dashboardConfig: { - developerConfig: { testingAndDemoMode }, - }, -} = Config; +const { testingAndDemoMode } = developerConfig; export async function POST(context: APIContext): Promise { // Check if testing and demo mode is enabled @@ -22,35 +17,55 @@ export async function POST(context: APIContext): Promise { // Map Locals // const locals = context.locals; - const userdata = await getUserData(context); + const userData = await getUserData(context); // Check if user is logged in - if (!userdata.isLoggedIn) { + if (!userData.isLoggedIn) { return simpleResponse(403, 'Unauthorized'); } // Check if user has permission - if (userdata.isLoggedIn) { - const { permissionLevel } = userdata; - if (permissionLevel !== 'admin') { - return simpleResponse(403, 'Unauthorized'); - } + const isAuthorized = await verifyUserPermissionLevel(userData, 'owner'); + if (!isAuthorized) { + return simpleResponse(403, 'Unauthorized'); } // Get form data const formData = await context.request.formData(); const title = formData.get('title')?.toString(); const description = formData.get('description')?.toString(); + const defaultOgImage = formData.get('defaultOgImage')?.toString(); + const loginPageBackground = formData.get('loginPageBackground')?.toString(); + const loginPageCustomImage = formData.get('loginPageCustomImage')?.toString(); + const siteIcon = formData.get('siteIcon')?.toString(); // Check if title and description exists - if (!title || !description) { - logger.error('Invalid title or description'); - return simpleResponse(400, 'Invalid title or description'); + if ( + !title || + !description || + !defaultOgImage || + !loginPageBackground || + !loginPageCustomImage || + !siteIcon + ) { + logger.error('Invalid form data'); + return simpleResponse(400, 'Invalid form data'); } // Update Database try { - await astroDb().siteConfig().update({ title, description }); + await studioCMS_SDK.UPDATE.siteConfig({ + title, + description, + id: CMSSiteConfigId, + defaultOgImage, + loginPageBackground, + loginPageCustomImage, + siteIcon, + }); + + logger.info('Site config updated'); + return simpleResponse(200, 'Site config updated'); } catch (error) { // Log error if (error instanceof Error) { @@ -59,8 +74,4 @@ export async function POST(context: APIContext): Promise { // Return Error Response return simpleResponse(500, 'Error updating site config'); } - - // Return Response - logger.info('Site config updated'); - return simpleResponse(200, 'Site config updated'); } diff --git a/packages/studiocms_dashboard/src/routes/user-management.astro b/packages/studiocms_dashboard/src/routes/user-management.astro index a1b18c025d..49d02a3a3a 100644 --- a/packages/studiocms_dashboard/src/routes/user-management.astro +++ b/packages/studiocms_dashboard/src/routes/user-management.astro @@ -19,7 +19,7 @@ const t = useTranslations(lang, '@studiocms/dashboard:index'); // const webVitals = await getWebVitals(); --- - + { - return `studiocms_api/dashboard/${route}`; -}; +const apiRoute = makeAPIRoute('dashboard'); export const injectDashboardAPIRoutes = defineUtility('astro:config:setup')( ( @@ -65,7 +64,7 @@ export const injectDashboardAPIRoutes = defineUtility('astro:config:setup')( } injectRoute({ - pattern: makeStudioCMSDashboardAPIRoute(pattern), + pattern: apiRoute(pattern), entrypoint, prerender: false, }); diff --git a/packages/studiocms_frontend/package.json b/packages/studiocms_frontend/package.json index 4b83ea782c..b20b8eac5d 100644 --- a/packages/studiocms_frontend/package.json +++ b/packages/studiocms_frontend/package.json @@ -37,6 +37,7 @@ "dependencies": { "@matthiesenxyz/integration-utils": "catalog:studiocms-shared", "@studiocms/core": "workspace:*", + "@studiocms/ui": "catalog:", "astro-integration-kit": "catalog:" }, "peerDependencies": { diff --git a/packages/studiocms_frontend/src/routes/404.astro b/packages/studiocms_frontend/src/routes/404.astro index c210aa67f5..b35a85e344 100644 --- a/packages/studiocms_frontend/src/routes/404.astro +++ b/packages/studiocms_frontend/src/routes/404.astro @@ -1,33 +1,110 @@ --- +import { Button } from '@studiocms/ui/components'; import Layout from '../components/Layout.astro'; --- -
-

Error 404

-

Page not found

-

- Sorry, the page you are looking for does not exist. Please check the URL in the address bar and try again. -

-

- If you believe this is an error, please contact the site administrator. -

- -

- -

- -
+
+
+
+ + + + + +
+

Page not found

+

The page you are looking for might have been removed had its name changed or is temporarily unavailable.

+ +
+
- \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b114ac768f..a734deb853 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -731,6 +731,9 @@ importers: '@studiocms/core': specifier: workspace:* version: link:../studiocms_core + '@studiocms/ui': + specifier: 'catalog:' + version: 0.1.0(astro@4.16.14(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3)) astro: specifier: catalog:min version: 4.16.14(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3)