From e270c5a894012d0dd2731bd8209b05944f6deb85 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sat, 26 Oct 2024 18:26:03 -0700 Subject: [PATCH 01/11] Refactor image handling and remove unused code --- .changeset/short-islands-call.md | 7 ++ README.md | 10 +- packages/studiocms/package.json | 4 +- packages/studiocms_core/package.json | 1 - .../src/schemas/config/imageService.ts | 63 ---------- .../src/schemas/config/index.ts | 2 + packages/studiocms_core/src/strings.ts | 61 +-------- packages/studiocms_core/tsconfig.json | 1 - packages/studiocms_core/virtuals.d.ts | 4 +- packages/studiocms_imagehandler/package.json | 4 +- .../src/adapters/cloudflare.ts | 79 ------------ .../src/adapters/index.ts | 6 - .../src/adapters/netlify.ts | 115 ----------------- .../src/adapters/node.ts | 104 --------------- .../src/adapters/vercel.ts | 118 ------------------ .../src/components/CustomImage.astro | 44 ++----- .../src/components/plugins/cloudinary.ts | 39 ++++++ .../src/components/props.ts | 6 + .../studiocms_imagehandler/src/integration.ts | 104 ++++----------- .../src/supportedAdapters.ts | 6 - packages/studiocms_imagehandler/tsconfig.json | 1 - packages/studiocms_imagehandler/virtuals.d.ts | 4 +- playgrounds/node/studiocms.config.mjs | 4 - pnpm-workspace.yaml | 2 - 24 files changed, 99 insertions(+), 690 deletions(-) create mode 100644 .changeset/short-islands-call.md delete mode 100644 packages/studiocms_imagehandler/src/adapters/cloudflare.ts delete mode 100644 packages/studiocms_imagehandler/src/adapters/index.ts delete mode 100644 packages/studiocms_imagehandler/src/adapters/netlify.ts delete mode 100644 packages/studiocms_imagehandler/src/adapters/node.ts delete mode 100644 packages/studiocms_imagehandler/src/adapters/vercel.ts create mode 100644 packages/studiocms_imagehandler/src/components/plugins/cloudinary.ts create mode 100644 packages/studiocms_imagehandler/src/components/props.ts delete mode 100644 packages/studiocms_imagehandler/src/supportedAdapters.ts diff --git a/.changeset/short-islands-call.md b/.changeset/short-islands-call.md new file mode 100644 index 000000000..d7d667125 --- /dev/null +++ b/.changeset/short-islands-call.md @@ -0,0 +1,7 @@ +--- +"@studiocms/imagehandler": patch +"@studiocms/core": patch +"studiocms": patch +--- + +Remove Unpic and simplify imageHandler diff --git a/README.md b/README.md index 8ea2be8e0..1e13cea5c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Formatted with Biome](https://img.shields.io/badge/Formatted_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev/) [![Built with Astro](https://astro.badg.es/v2/built-with-astro/tiny.svg)](https://astro.build) -This is an SSR CMS built with AstroDB / Lucia Auth / Unpic Image handler for the Astro Ecosystem. +This is an SSR CMS built with AstroDB for the Astro Ecosystem. To see how to get started, check out the [StudioCMS README](./packages/studiocms/README.md). @@ -27,11 +27,9 @@ We have an active community of developers on the Astrolicious [Discord Server](h ## Our ToolSet -- **Node** - 20.12.x -- **pnpm** - 9.5.0 -- [`BiomeJS`](https://biomejs.dev/) - 1.8.3 -- [`Moon`](https://moonrepo.dev) - 1.27.2 -- [`Proto`](https://moonrepo.dev) - 0.38.3 +For an up-to-date list of our main tools check out our [`.prototools`](.prototools) file + +For more information about Proto checkout [Proto's Website](https://moonrepo.dev/proto) ## This is a [`Moonrepo`](https://moonrepo.dev) diff --git a/packages/studiocms/package.json b/packages/studiocms/package.json index a22e3f88b..cbe8b6377 100644 --- a/packages/studiocms/package.json +++ b/packages/studiocms/package.json @@ -62,7 +62,6 @@ "@studiocms/robotstxt": "workspace:*", "@unocss/astro": "catalog:studiocms-shared", "@unocss/reset": "catalog:studiocms-shared", - "@unpic/astro": "catalog:studiocms-imagehandler", "astro-integration-kit": "catalog:", "arctic": "catalog:studiocms-shared", "daisyui": "catalog:studiocms-shared", @@ -78,8 +77,7 @@ "package-json": "catalog:studiocms", "semver": "catalog:studiocms", "shiki": "catalog:studiocms-shared", - "unocss": "catalog:studiocms-shared", - "unpic": "catalog:studiocms-shared" + "unocss": "catalog:studiocms-shared" }, "peerDependencies": { "@astrojs/db": "catalog:min", diff --git a/packages/studiocms_core/package.json b/packages/studiocms_core/package.json index 0d08c04bf..8260f98ea 100644 --- a/packages/studiocms_core/package.json +++ b/packages/studiocms_core/package.json @@ -57,7 +57,6 @@ "marked": "catalog:studiocms-shared", "mrmime": "catalog:studiocms-shared", "shiki": "catalog:studiocms-shared", - "unpic": "catalog:studiocms-shared", "unified": "catalog:studiocms-shared", "remark-rehype": "catalog:studiocms-core", "mdast-util-to-hast": "catalog:studiocms-core" diff --git a/packages/studiocms_core/src/schemas/config/imageService.ts b/packages/studiocms_core/src/schemas/config/imageService.ts index 2b7b43f95..ba7c9ec93 100644 --- a/packages/studiocms_core/src/schemas/config/imageService.ts +++ b/packages/studiocms_core/src/schemas/config/imageService.ts @@ -1,77 +1,14 @@ import { z } from 'astro/zod'; -import type { CdnOptions, ImageCdn } from 'unpic'; - -// -// UNPIC CONFIG OPTIONS SCHEMA -// -export const unpicConfigSchema = z - .object({ - /** - * The image service to use for local images and when the CDN can't be - * determined from the image src. Value can be any supported image CDN, - * or "sharp" or "squoosh" to use the local image service. - * By default it will either use the local "squoosh" service, or will - * try to detect available services based on the environment. - * This detection currently works on Netlify and Vercel. - * - * Falls back to the value of `astroImageServiceConfig` if not set here - */ - fallbackService: z.union([z.custom(), z.enum(['sharp', 'squoosh'])]).optional(), - /** - * The default placeholder background to use for images. - * Can be `"blurhash"`, `"dominantColor"`, or `"lqip"` - * Local images don't support `"blurhash"`, `"dominantColor"` or `"lqip"`, and will - * not include a background - * Default is no background. - * Note that because the element uses no Javascript, the background will not - * be removed when the image loads, so you should not use it for images that - * have transparency. - * - * @see https://unpic.pics/placeholder - * @default "blurhash" - */ - placeholder: z.enum(['blurhash', 'dominantColor', 'lqip']).optional().default('blurhash'), - /** - * The default layout to use for images. Defaults to "constrained". - * @see https://unpic.pics/img/learn/#layouts - * @default "constrained" - */ - layout: z.enum(['constrained', 'fixed', 'fullWidth']).optional().default('constrained'), - /** - * CDN-specific options. - */ - cdnOptions: z.custom().optional().default({}), - }) - .optional() - .default({}); // // IMAGE SERVICE SCHEMA // export const imageServiceSchema = z .object({ - /** - * OPTIONAL - Allows the user to enable/disable the use of the `@unpic/astro` image optimization service for external images - * @default true - */ - useUnpic: z.boolean().optional().default(true), - /** - * OPTIONAL - Allows the user to customize the `@unpic/astro` image optimization service - */ - unpicConfig: unpicConfigSchema, - /** - * If the user wants to disable the `@unpic/astro` image service, they can specify their desired Astro Built-in Image Service using this option. - * - * Note: This option is only used if `useUnpic` is set to `false` - * @default "squoosh" - */ - astroImageServiceConfig: z.enum(['sharp', 'no-op']).optional().default('sharp'), /** * If the user wants to use a custom Supported CDN Plugin, they can specify it here. * * Currently Supported CDN Plugins: **cloudinary-js** - * - * Note: Enabling this option will disable the use of the `@unpic/astro` image service for external images. For local images and Fallback, the `astroImageServiceConfig` will be used. */ cdnPlugin: z.enum(['cloudinary-js']).optional(), }) diff --git a/packages/studiocms_core/src/schemas/config/index.ts b/packages/studiocms_core/src/schemas/config/index.ts index 977359570..fc7933e7a 100644 --- a/packages/studiocms_core/src/schemas/config/index.ts +++ b/packages/studiocms_core/src/schemas/config/index.ts @@ -75,3 +75,5 @@ export const StudioCMSOptionsSchema = z .default({}); export type StudioCMSOptions = typeof StudioCMSOptionsSchema._input; + +export type StudioCMSConfig = typeof StudioCMSOptionsSchema._output; diff --git a/packages/studiocms_core/src/strings.ts b/packages/studiocms_core/src/strings.ts index be4f9a5a7..2e968f6d7 100644 --- a/packages/studiocms_core/src/strings.ts +++ b/packages/studiocms_core/src/strings.ts @@ -75,65 +75,8 @@ export const imageHandlerStrings = { CloudinaryCDNWarning: 'Using the Cloudinary CDN JS SDK Plugin requires the CMS_CLOUDINARY_CLOUDNAME environment variable to be set. Please add this to your .env file.', CustomImageLog: 'Configuring CustomImage Component...', - NodeAdapter: 'Node Adapter Detected. Using Node Adapter.', - CloudflareAdapter: 'Cloudflare Adapter Detected. Using Cloudflare Adapter.', - VercelAdapter: 'Vercel Adapter Detected. Using Vercel Adapter.', - NetlifyAdapter: 'Netlify Adapter Detected. Using Netlify Adapter.', - UnknownAdapter: { - part1: 'Unknown Adapter Detected: ', - part2: - '. studioCMS Image Handler has not been configured for this adapter. Please open an issue on the studioCMS GitHub Repository. https://github.com/astrolicious/studioCMS/issues', - }, - NoAdapter: - 'No Adapter Detected. studioCMS Image Handler will only be configured with Known SSR Adapters!', -}; - -export const genericAdapterStrings = { - Squoosh: 'Using Squoosh Image Service...', - Sharp: 'Using Sharp Image Service...', - NoOp: 'Using No-Op(Passthrough) Image Service...', - cdnPluginStrings: { - Squoosh: 'Using Squoosh Image Service as Fallback for Cloudinary CDN Plugin', - Sharp: 'Using Sharp Image Service as Fallback for Cloudinary CDN Plugin', - NoOp: 'Using No-Op Image Service as Fallback for Cloudinary CDN Plugin', - }, - unpicStrings: { - default: 'Loading @unpic/astro Image Service for External Images', - NoOp: 'Loading @unpic/astro Image Service for External Images with No-Op Fallback', - disabled: '@unpic/astro Image Service Disabled, using Astro Built-in Image Service.', - }, -}; - -export const vercelImageHandlerStrings = { - VercelBuildImageServerEnabled: 'Vercel Image Service Enabled. Using Vercel Image Service.', - VercelBuildImageServerDisabled: - 'Vercel Image Service Disabled. Using Astro Built-in Image Service.', - ...genericAdapterStrings, -}; - -export const nodeImageHandlerStrings = { - ...genericAdapterStrings, -}; - -export const netlifyImageHandlerStrings = { - NetlifyImageServiceEnabled: 'Netlify Image Service Enabled. Using Netlify Image Service.', - NetlifyImageServiceDisabled: - 'Netlify Image Service Disabled. Using Astro Built-in Image Service.', - ...genericAdapterStrings, -}; - -export const cloudflareImageHandlerStrings = { - CloudflareImageServiceEnabled: - 'Cloudflare Image Service Enabled. Using Cloudflare Image Service.', - CloudflareImageServiceDisabled: - 'Cloudflare Image Service Disabled. Using Astro Built-in Image Service.', - unsupported: { - Squoosh: - "Cloudflare SSR does not support Squoosh Image Service. Using no-op Service as astroImageServiceConfig is set to 'squoosh'", - Sharp: - "Cloudflare SSR does not support Sharp Image Service. Using no-op Service as astroImageServiceConfig is set to 'sharp'", - }, - ...genericAdapterStrings, + updateConfig: + 'Updating Astro Config with Image Service Configuration to allow for remote images...', }; export const AuthProviderLogStrings = { diff --git a/packages/studiocms_core/tsconfig.json b/packages/studiocms_core/tsconfig.json index 19d1e8e7c..50bf89cbd 100644 --- a/packages/studiocms_core/tsconfig.json +++ b/packages/studiocms_core/tsconfig.json @@ -23,7 +23,6 @@ ], "include": [ "./package.json", - "../../playgrounds/node/**/*", "../../playgrounds/node/.astro/**/*", "../studiocms_robotstxt/**/*", "./**/*", diff --git a/packages/studiocms_core/virtuals.d.ts b/packages/studiocms_core/virtuals.d.ts index d97419889..67e5d3205 100644 --- a/packages/studiocms_core/virtuals.d.ts +++ b/packages/studiocms_core/virtuals.d.ts @@ -7,12 +7,12 @@ * * @example * declare module 'virtual:studiocms/config' { - * const Config: import('studiocms').StudioCMSOptions; + * const Config: import('@studiocms/core/schemas').StudioCMSConfig; * export default Config; * } */ declare module 'virtual:studiocms/config' { - const Config: import('./src/schemas').StudioCMSOptions; + const Config: import('./src/schemas').StudioCMSConfig; export default Config; } diff --git a/packages/studiocms_imagehandler/package.json b/packages/studiocms_imagehandler/package.json index 48d9d09b3..bf974b24d 100644 --- a/packages/studiocms_imagehandler/package.json +++ b/packages/studiocms_imagehandler/package.json @@ -38,9 +38,7 @@ "@matthiesenxyz/astrodtsbuilder": "catalog:studiocms-shared", "@matthiesenxyz/integration-utils": "catalog:studiocms-shared", "@studiocms/core": "workspace:*", - "@unpic/astro": "catalog:studiocms-imagehandler", - "astro-integration-kit": "catalog:", - "unpic": "catalog:studiocms-shared" + "astro-integration-kit": "catalog:" }, "peerDependencies": { "astro": "catalog:min" diff --git a/packages/studiocms_imagehandler/src/adapters/cloudflare.ts b/packages/studiocms_imagehandler/src/adapters/cloudflare.ts deleted file mode 100644 index aee8c17ac..000000000 --- a/packages/studiocms_imagehandler/src/adapters/cloudflare.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; -import { cloudflareImageHandlerStrings } from '@studiocms/core/strings'; -import { defineIntegration } from 'astro-integration-kit'; -import { passthroughImageService } from 'astro/config'; -import { name as packageName } from '../../package.json'; -import { StudioCMSImageHandlerOptionsSchema } from '../schema'; - -export default defineIntegration({ - name: `${packageName}:cloudflare`, - optionsSchema: StudioCMSImageHandlerOptionsSchema, - setup({ options }) { - return { - hooks: { - 'astro:config:setup': async (params) => { - const { updateConfig, config, logger } = params; - - const { - imageService: { astroImageServiceConfig, cdnPlugin }, - verbose, - } = options; - - // Setup Image Service - if (config.image?.service.entrypoint === '@astrojs/cloudflare/image-service') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.CloudflareImageServiceEnabled - ); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.CloudflareImageServiceDisabled - ); - if (cdnPlugin === 'cloudinary-js') { - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.cdnPluginStrings.Sharp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.cdnPluginStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.unpicStrings.disabled - ); - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.unsupported.Sharp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - cloudflareImageHandlerStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } - } - }, - }, - }; - }, -}); diff --git a/packages/studiocms_imagehandler/src/adapters/index.ts b/packages/studiocms_imagehandler/src/adapters/index.ts deleted file mode 100644 index 33d86e056..000000000 --- a/packages/studiocms_imagehandler/src/adapters/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import cloudflareImageHandler from './cloudflare'; -import netlifyImageHandler from './netlify'; -import nodeImageHandler from './node'; -import vercelImageHandler from './vercel'; - -export { cloudflareImageHandler, netlifyImageHandler, nodeImageHandler, vercelImageHandler }; diff --git a/packages/studiocms_imagehandler/src/adapters/netlify.ts b/packages/studiocms_imagehandler/src/adapters/netlify.ts deleted file mode 100644 index 3b66c6f35..000000000 --- a/packages/studiocms_imagehandler/src/adapters/netlify.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; -import { netlifyImageHandlerStrings } from '@studiocms/core/strings'; -import { imageService as unpicImageService } from '@unpic/astro/service'; -import { defineIntegration } from 'astro-integration-kit'; -import { passthroughImageService, sharpImageService } from 'astro/config'; -import { name as packageName } from '../../package.json'; -import { StudioCMSImageHandlerOptionsSchema } from '../schema'; - -export default defineIntegration({ - name: `${packageName}:netlify`, - optionsSchema: StudioCMSImageHandlerOptionsSchema, - setup({ options }) { - return { - hooks: { - 'astro:config:setup': async (params) => { - const { updateConfig, config, logger } = params; - - const { - imageService: { - useUnpic, - astroImageServiceConfig, - cdnPlugin, - unpicConfig: { cdnOptions, layout, placeholder, fallbackService }, - }, - verbose, - } = options; - - // Setup Image Service - if (config.image?.service.entrypoint === '@astrojs/netlify/image-service.js') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.NetlifyImageServiceEnabled - ); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.NetlifyImageServiceDisabled - ); - if (cdnPlugin === 'cloudinary-js') { - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.cdnPluginStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.cdnPluginStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } else if (useUnpic && astroImageServiceConfig !== 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.unpicStrings.default - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : astroImageServiceConfig, - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else if (useUnpic && astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.unpicStrings.NoOp - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : 'astro', - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.unpicStrings.disabled - ); - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - netlifyImageHandlerStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } - } - }, - }, - }; - }, -}); diff --git a/packages/studiocms_imagehandler/src/adapters/node.ts b/packages/studiocms_imagehandler/src/adapters/node.ts deleted file mode 100644 index 048102bf3..000000000 --- a/packages/studiocms_imagehandler/src/adapters/node.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; -import { nodeImageHandlerStrings } from '@studiocms/core/strings'; -import { imageService as unpicImageService } from '@unpic/astro/service'; -import { defineIntegration } from 'astro-integration-kit'; -import { passthroughImageService, sharpImageService } from 'astro/config'; -import { name as packageName } from '../../package.json'; -import { StudioCMSImageHandlerOptionsSchema } from '../schema'; - -export default defineIntegration({ - name: `${packageName}:node`, - optionsSchema: StudioCMSImageHandlerOptionsSchema, - setup({ options }) { - return { - hooks: { - 'astro:config:setup': (params) => { - const { updateConfig, logger } = params; - - const { - imageService: { - useUnpic, - astroImageServiceConfig, - cdnPlugin, - unpicConfig: { cdnOptions, layout, placeholder, fallbackService }, - }, - verbose, - } = options; - - // Setup Image Service - if (cdnPlugin === 'cloudinary-js') { - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.cdnPluginStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.cdnPluginStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } else if (useUnpic && astroImageServiceConfig !== 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.unpicStrings.default - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : astroImageServiceConfig, - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else if (useUnpic && astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.unpicStrings.NoOp - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : 'astro', - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.unpicStrings.disabled - ); - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - nodeImageHandlerStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } - }, - }, - }; - }, -}); diff --git a/packages/studiocms_imagehandler/src/adapters/vercel.ts b/packages/studiocms_imagehandler/src/adapters/vercel.ts deleted file mode 100644 index e08186888..000000000 --- a/packages/studiocms_imagehandler/src/adapters/vercel.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; -import { vercelImageHandlerStrings } from '@studiocms/core/strings'; -import { imageService as unpicImageService } from '@unpic/astro/service'; -import { defineIntegration } from 'astro-integration-kit'; -import { passthroughImageService, sharpImageService } from 'astro/config'; -import { name as packageName } from '../../package.json'; -import { StudioCMSImageHandlerOptionsSchema } from '../schema'; - -export default defineIntegration({ - name: `${packageName}:vercel`, - optionsSchema: StudioCMSImageHandlerOptionsSchema, - setup({ options }) { - return { - hooks: { - 'astro:config:setup': async (params) => { - const { updateConfig, command, config, logger } = params; - - const { - imageService: { - useUnpic, - astroImageServiceConfig, - cdnPlugin, - unpicConfig: { cdnOptions, layout, placeholder, fallbackService }, - }, - verbose, - } = options; - - // Setup Image Service - if ( - command === 'build' && - config.image.service.entrypoint === '@astrojs/vercel/build-image-service' - ) { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.VercelBuildImageServerEnabled - ); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.VercelBuildImageServerDisabled - ); - if (cdnPlugin === 'cloudinary-js') { - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.cdnPluginStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.cdnPluginStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } else if (useUnpic && astroImageServiceConfig !== 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.unpicStrings.default - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : astroImageServiceConfig, - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else if (useUnpic && astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.unpicStrings.NoOp - ); - updateConfig({ - image: { - service: unpicImageService({ - placeholder: placeholder, - fallbackService: fallbackService ? fallbackService : 'astro', - layout: layout, - cdnOptions: cdnOptions, - }), - }, - }); - } else { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.unpicStrings.disabled - ); - if (astroImageServiceConfig === 'sharp') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.Sharp - ); - updateConfig({ - image: { service: sharpImageService() }, - }); - } else if (astroImageServiceConfig === 'no-op') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - vercelImageHandlerStrings.NoOp - ); - updateConfig({ - image: { service: passthroughImageService() }, - }); - } - } - } - }, - }, - }; - }, -}); diff --git a/packages/studiocms_imagehandler/src/components/CustomImage.astro b/packages/studiocms_imagehandler/src/components/CustomImage.astro index a28c6019a..d3a21783a 100644 --- a/packages/studiocms_imagehandler/src/components/CustomImage.astro +++ b/packages/studiocms_imagehandler/src/components/CustomImage.astro @@ -1,18 +1,14 @@ --- import { Image } from 'astro:assets'; -import { CMS_CLOUDINARY_CLOUDNAME } from 'astro:env/server'; import Config from 'virtual:studiocms/config'; -import { Cloudinary } from '@cloudinary/url-gen'; -import { fill } from '@cloudinary/url-gen/actions/resize'; +import { cloudinaryPlugin } from './plugins/cloudinary'; +import type { SharedProps } from './props'; -interface Props { - src: string; - alt: string; - width: number; - height: number; -} +interface Props extends SharedProps {} const { src, ...props } = Astro.props; + +// Get the current configured image service const { imageService: { cdnPlugin }, } = Config; @@ -20,31 +16,9 @@ const { // Get the current configured image service async function getImageSrc() { switch (cdnPlugin) { - case 'cloudinary-js': { - // Cloudinary Image Service (JavaScript SDK) - https://cloudinary.com/documentation/javascript_integration#landingpage - - // Initialize Cloudinary - const cld = new Cloudinary({ - cloud: { - cloudName: CMS_CLOUDINARY_CLOUDNAME || 'demo', - }, - }); - - // Configure the image - const cldSrc = cld - .image(src) - .format('auto') - .quality('auto') - .resize(fill('auto').width(props.width).height(props.height)); - - // Set the delivery type to 'fetch' if the image source is an external URL - if (src.startsWith('https://') || src.startsWith('http://')) { - cldSrc.setDeliveryType('fetch'); - } - - // Return the Cloudinary image URL - return cldSrc.toURL(); - } + case 'cloudinary-js': + // Return the Cloudinary image source + return cloudinaryPlugin(src, props); default: // Return the original image source if no External image service plugin is configured return src; @@ -64,7 +38,7 @@ const currentSrcURL = await getImageSrc(); // Set the image properties const imageProps = { - src: await getImageSrc(), + src: currentSrcURL, inferSize: inferSizeNeeded(currentSrcURL), ...props, }; diff --git a/packages/studiocms_imagehandler/src/components/plugins/cloudinary.ts b/packages/studiocms_imagehandler/src/components/plugins/cloudinary.ts new file mode 100644 index 000000000..941c47860 --- /dev/null +++ b/packages/studiocms_imagehandler/src/components/plugins/cloudinary.ts @@ -0,0 +1,39 @@ +import { CMS_CLOUDINARY_CLOUDNAME } from 'astro:env/server'; +import { Cloudinary } from '@cloudinary/url-gen'; +import { fill } from '@cloudinary/url-gen/actions/resize'; +import type { SharedProps } from '../props'; + +/** + * Cloudinary Plugin + * + * This plugin is used to generate Cloudinary URLs for images using `@cloudinary/url-gen` for the StudioCMS `CustomImage` component. + * + * @param src the image name or URL + * @param props the image props + * @returns the Cloudinary image URL for the given source and props + */ +export function cloudinaryPlugin(src: string, props: Omit) { + // Cloudinary Image Service (JavaScript SDK) - https://cloudinary.com/documentation/javascript_integration#landingpage + + // Initialize Cloudinary + const cld = new Cloudinary({ + cloud: { + cloudName: CMS_CLOUDINARY_CLOUDNAME || '', + }, + }); + + // Configure the image + const cldSrc = cld + .image(src) + .format('auto') + .quality('auto') + .resize(fill('auto').width(props.width).height(props.height)); + + // Set the delivery type to 'fetch' if the image source is an external URL + if (src.startsWith('https://') || src.startsWith('http://')) { + cldSrc.setDeliveryType('fetch'); + } + + // Return the Cloudinary image URL + return cldSrc.toURL(); +} diff --git a/packages/studiocms_imagehandler/src/components/props.ts b/packages/studiocms_imagehandler/src/components/props.ts new file mode 100644 index 000000000..dea1454a0 --- /dev/null +++ b/packages/studiocms_imagehandler/src/components/props.ts @@ -0,0 +1,6 @@ +export interface SharedProps { + src: string; + alt: string; + width: number; + height: number; +} diff --git a/packages/studiocms_imagehandler/src/integration.ts b/packages/studiocms_imagehandler/src/integration.ts index 87a50fcfd..89d851e02 100644 --- a/packages/studiocms_imagehandler/src/integration.ts +++ b/packages/studiocms_imagehandler/src/integration.ts @@ -2,24 +2,24 @@ import { integrationLogger } from '@matthiesenxyz/integration-utils/astroUtils'; import { imageHandlerStrings } from '@studiocms/core/strings'; import { addAstroEnvConfig } from '@studiocms/core/utils'; import type { InjectedType } from 'astro'; -import { addIntegration, defineIntegration } from 'astro-integration-kit'; +import { defineIntegration } from 'astro-integration-kit'; import { envField } from 'astro/config'; import { loadEnv } from 'vite'; import { name } from '../package.json'; -import { - cloudflareImageHandler, - netlifyImageHandler, - nodeImageHandler, - vercelImageHandler, -} from './adapters'; import { componentResolver } from './componentResolver'; import { StudioCMSImageHandlerOptionsSchema } from './schema'; -import { supportedAdapters } from './supportedAdapters'; export default defineIntegration({ name, optionsSchema: StudioCMSImageHandlerOptionsSchema, - setup({ name, options }) { + setup({ + name, + options: { + verbose, + imageService: { cdnPlugin }, + overrides: { CustomImageOverride }, + }, + }) { // Load Environment Variables const env = loadEnv('all', process.cwd(), 'CMS'); @@ -30,16 +30,7 @@ export default defineIntegration({ hooks: { 'astro:config:setup': (params) => { // Destructure Params - const { - config: { adapter }, - logger, - } = params; - - // Destructure Options - const { - verbose, - imageService: { cdnPlugin }, - } = options; + const { logger, updateConfig } = params; // Add Astro Environment Configuration addAstroEnvConfig(params, { @@ -70,73 +61,26 @@ export default defineIntegration({ ); const { imageHandlerDtsFile } = componentResolver(params, { name, - CustomImageOverride: options.overrides.CustomImageOverride, + CustomImageOverride, }); - // Return the Custom Image DTS File - dtsFile = imageHandlerDtsFile; - - // Setup and Configure Astro Adapters and Image Services based on the Adapter and Image Service Configurations + // Update the Astro Config with the Image Service Configuration to allow for remote images integrationLogger( { logger, logLevel: 'info', verbose }, - `Determining Astro Adapter Configuration... ${adapter && `Detected Adapter: ${adapter.name}`}` + imageHandlerStrings.updateConfig ); + updateConfig({ + image: { + remotePatterns: [ + { + protocol: 'https', + }, + ], + }, + }); - // - // Check for Astro Adapter and inject the appropriate Image Handler Integration // - // - - // Node Adapter - if (adapter?.name === '@astrojs/node') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - imageHandlerStrings.NodeAdapter - ); - addIntegration(params, { integration: nodeImageHandler(options) }); - } - - // Cloudflare Adapter - else if (adapter?.name === '@astrojs/cloudflare') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - imageHandlerStrings.CloudflareAdapter - ); - addIntegration(params, { integration: cloudflareImageHandler(options) }); - } - - // Vercel Adapter - else if (adapter?.name === '@astrojs/vercel') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - imageHandlerStrings.VercelAdapter - ); - addIntegration(params, { integration: vercelImageHandler(options) }); - } - - // Netlify Adapter - else if (adapter?.name === '@astrojs/netlify') { - integrationLogger( - { logger, logLevel: 'info', verbose }, - imageHandlerStrings.NetlifyAdapter - ); - addIntegration(params, { integration: netlifyImageHandler(options) }); - } - - // Unknown Adapter - else if (adapter?.name !== undefined && !supportedAdapters.includes(adapter.name)) { - integrationLogger( - { logger, logLevel: 'warn', verbose: true }, - imageHandlerStrings.UnknownAdapter.part1 + - adapter.name + - imageHandlerStrings.UnknownAdapter.part2 - ); - } - - // No Adapter Detected - else if (adapter?.name === undefined) { - integrationLogger( - { logger, logLevel: 'warn', verbose: true }, - imageHandlerStrings.NoAdapter - ); - } + // Return the Custom Image DTS File + dtsFile = imageHandlerDtsFile; }, 'astro:config:done': ({ injectTypes }) => { injectTypes(dtsFile); diff --git a/packages/studiocms_imagehandler/src/supportedAdapters.ts b/packages/studiocms_imagehandler/src/supportedAdapters.ts deleted file mode 100644 index 16f42f90a..000000000 --- a/packages/studiocms_imagehandler/src/supportedAdapters.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const supportedAdapters = Object.freeze([ - '@astrojs/node', - '@astrojs/cloudflare', - '@astrojs/vercel', - '@astrojs/netlify', -]); diff --git a/packages/studiocms_imagehandler/tsconfig.json b/packages/studiocms_imagehandler/tsconfig.json index 740b2031f..5f1025bda 100644 --- a/packages/studiocms_imagehandler/tsconfig.json +++ b/packages/studiocms_imagehandler/tsconfig.json @@ -23,7 +23,6 @@ ], "include": [ "./package.json", - "../../playgrounds/node/**/*", "../../playgrounds/node/.astro/**/*", "../studiocms_core/**/*", "./**/*", diff --git a/packages/studiocms_imagehandler/virtuals.d.ts b/packages/studiocms_imagehandler/virtuals.d.ts index 8ba7c0eef..dcb348fe9 100644 --- a/packages/studiocms_imagehandler/virtuals.d.ts +++ b/packages/studiocms_imagehandler/virtuals.d.ts @@ -7,11 +7,11 @@ * * @example * declare module 'virtual:studiocms/config' { - * const Config: import('@studiocms/core').StudioCMSOptions; + * const Config: import('@studiocms/core').StudioCMSConfig; * export default Config; * } */ declare module 'virtual:studiocms/config' { - const Config: import('@studiocms/core/schemas').StudioCMSOptions; + const Config: import('@studiocms/core/schemas').StudioCMSConfig; export default Config; } diff --git a/playgrounds/node/studiocms.config.mjs b/playgrounds/node/studiocms.config.mjs index c78f3e10f..362fcdaaf 100644 --- a/playgrounds/node/studiocms.config.mjs +++ b/playgrounds/node/studiocms.config.mjs @@ -28,10 +28,6 @@ export default defineStudioCMSConfig({ useAstroRobots: true, useInoxSitemap: true, }, - imageService: { - useUnpic: false, - astroImageServiceConfig: 'no-op', - }, dashboardConfig: { AuthConfig: { enabled: true, diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 739889777..59c1cc84a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -75,7 +75,6 @@ catalogs: marked: ^13.0.2 micromatch: ^4.0.8 shiki: ^1.14.1 - unpic: ^3.18.0 mrmime: ^2.0.0 unified: ^11.0.5 studiocms-renderer: @@ -90,7 +89,6 @@ catalogs: rehype-highlight: ^7.0.0 studiocms-imagehandler: '@cloudinary/url-gen': ^1.21.0 - '@unpic/astro': ^0.0.47 packages: - "packages/*" From 1a04fa22d9f48676c6b134e20ad6d9b085350c32 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sat, 26 Oct 2024 18:32:05 -0700 Subject: [PATCH 02/11] update docs --- .../docs/config-reference/image-service.mdx | 60 +------------------ .../src/content/docs/how-it-works/index.mdx | 2 +- www/docs/typedoc.config.ts | 8 +-- 3 files changed, 6 insertions(+), 64 deletions(-) diff --git a/www/docs/src/content/docs/config-reference/image-service.mdx b/www/docs/src/content/docs/config-reference/image-service.mdx index 60ab62d6d..9d2a5426a 100644 --- a/www/docs/src/content/docs/config-reference/image-service.mdx +++ b/www/docs/src/content/docs/config-reference/image-service.mdx @@ -9,76 +9,20 @@ sidebar: ## Usage -```ts twoslash {2-11} title="studiocms.config.mjs" +```ts twoslash {2-4} title="studiocms.config.mjs" import { defineStudioCMSConfig } from 'studiocms'; // ---cut--- export default defineStudioCMSConfig({ imageService: { - useUnpic: false, - unpicConfig: { - fallbackService: "sharp", - placeholder: 'blurhash', - layout: 'constrained', - }, - astroImageServiceConfig: "sharp", cdnPlugin: "cloudinary-js", }, }) ``` -### `useUnpic` - -- **Type:** `boolean | undefined` -- **Default:** `true` - -Allows the user to enable/disable the use of the `@unpic/astro` image optimization service for external images. - -### `unpicConfig` - -- **Type:** `unpicConfigSchema{} | undefined{}` -- **Default:** `undefined{}` - -Allows the user to customize the `@unpic/astro` image optimization service. - -#### `fallbackService` - -- **Type:** `'sharp' | undefined` -- **Default:** `undefined` - -Falls back to the value of `astroImageServiceConfig` if not set here. - -#### `placeholder` - -- **Type:** `'blurhash' | 'dominantColor' | 'lqlip' | undefined` -- **Default:** `'blurhash'` - -The default placeholder background to use for images. - -#### `layout` - -- **Type:** `'constrained' | 'fixed' | 'fullWidth' | undefined` -- **Default:** `'constrained'` - -The default layout to use for images. - -#### `cdnOptions` - -- **Type:** `cdnOptionsSchema{} | undefined{}` -- **Default:** `undefined{}` - -CDN-specific options for the `@unpic/astro` image optimization service. - -### `astroImageServiceConfig` - -- **Type:** `'sharp' | undefined` -- **Default:** `'sharp'` - -If the `useUnpic` option is enabled, this option allows the user to choose between the `sharp` image optimization services. - ### `cdnPlugin` - **Type:** `'cloudinary-js'| undefined` -- **Default:** `'cloudinary-js'` +- **Default:** `undefined` Allows the user to use a custom Supported CDN Plugin, so it can be specified here. diff --git a/www/docs/src/content/docs/how-it-works/index.mdx b/www/docs/src/content/docs/how-it-works/index.mdx index 356e87ba3..17d0d4dc6 100644 --- a/www/docs/src/content/docs/how-it-works/index.mdx +++ b/www/docs/src/content/docs/how-it-works/index.mdx @@ -79,7 +79,7 @@ The StudioCMS Dashboard is a web interface that allows you to manage your Studio ### StudioCMS: ImageHandler -The StudioCMS ImageHandler is an integration that provides a custom image component for your Astro project. This component is used to handle remote images and optimize them in a SSR environment using [@unpic/astro](https://unpic.pics/img/astro/). +The StudioCMS ImageHandler is an integration that provides a custom image component for your Astro project. This component is used to handle remote images and optimize them in a SSR environment. #### Features diff --git a/www/docs/typedoc.config.ts b/www/docs/typedoc.config.ts index 51c4c7a97..5ca24c7b7 100644 --- a/www/docs/typedoc.config.ts +++ b/www/docs/typedoc.config.ts @@ -148,13 +148,11 @@ const TypeDocPlugins = (isProd: boolean): StarlightPlugin[] => { dir: 'studiocms_imagehandler', entryPoints: [ getFilePathToPackage('studiocms_imagehandler', 'src/index.ts'), + getFilePathToPackage('studiocms_imagehandler', 'src/schema.ts'), getFilePathToPackage('studiocms_imagehandler', 'src/integration.ts'), - getFilePathToPackage('studiocms_imagehandler', 'src/supportedAdapters.ts'), getFilePathToPackage('studiocms_imagehandler', 'src/components/index.ts'), - getFilePathToPackage('studiocms_imagehandler', 'src/adapters/cloudflare.ts'), - getFilePathToPackage('studiocms_imagehandler', 'src/adapters/netlify.ts'), - getFilePathToPackage('studiocms_imagehandler', 'src/adapters/node.ts'), - getFilePathToPackage('studiocms_imagehandler', 'src/adapters/vercel.ts'), + getFilePathToPackage('studiocms_imagehandler', 'src/components/props.ts'), + getFilePathToPackage('studiocms_imagehandler', 'src/plugins/cloudinary.ts'), ], }) ), From de2ea695b2e36a72ba7d09fb4c01dc90be360feb Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sat, 26 Oct 2024 18:35:09 -0700 Subject: [PATCH 03/11] update landing page --- www/web/src/content/features/custom-image-component.json | 7 +++++++ www/web/src/content/features/unpic.json | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 www/web/src/content/features/custom-image-component.json delete mode 100644 www/web/src/content/features/unpic.json diff --git a/www/web/src/content/features/custom-image-component.json b/www/web/src/content/features/custom-image-component.json new file mode 100644 index 000000000..5215ab211 --- /dev/null +++ b/www/web/src/content/features/custom-image-component.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../.astro/collections/features.schema.json", + "sortOrder": 7, + "feature": "Custom Image Component", + "description": "A custom image component that allows for easy image handling and optimization.", + "icon": "heroicons:photo" +} diff --git a/www/web/src/content/features/unpic.json b/www/web/src/content/features/unpic.json deleted file mode 100644 index 18eb8444d..000000000 --- a/www/web/src/content/features/unpic.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../../.astro/collections/features.schema.json", - "sortOrder": 7, - "feature": "Unpic Image Service", - "description": "A free and efficient image service, Unpic makes managing external URLs straightforward, with support for major CDNs.", - "icon": "heroicons:photo" -} From 045bfe749ba48fd8c0ca7aafbf8ab8c8f7f2f7bf Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sat, 26 Oct 2024 18:51:22 -0700 Subject: [PATCH 04/11] Update dependencies: Add "sharp" package to package.json --- playgrounds/node/package.json | 1 + pnpm-lock.yaml | 90 ++--------------------------------- 2 files changed, 4 insertions(+), 87 deletions(-) diff --git a/playgrounds/node/package.json b/playgrounds/node/package.json index 951fc16bb..fdb52f174 100644 --- a/playgrounds/node/package.json +++ b/playgrounds/node/package.json @@ -22,6 +22,7 @@ "@astrojs/web-vitals": "catalog:", "astro": "catalog:", "studiocms": "workspace:*", + "sharp": "catalog:", "@studiocms/blog": "workspace:*", "@studiocms/devapps": "workspace:*" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11893a2fe..fb7b1f533 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,9 +132,6 @@ catalogs: '@cloudinary/url-gen': specifier: ^1.21.0 version: 1.21.0 - '@unpic/astro': - specifier: ^0.0.47 - version: 0.0.47 studiocms-renderer: '@mdx-js/mdx': specifier: ^3.0.1 @@ -242,9 +239,6 @@ catalogs: unocss: specifier: ^0.62.3 version: 0.62.3 - unpic: - specifier: ^3.18.0 - version: 3.18.0 importers: @@ -334,9 +328,6 @@ importers: '@unocss/reset': specifier: catalog:studiocms-shared version: 0.62.3 - '@unpic/astro': - specifier: catalog:studiocms-imagehandler - version: 0.0.47(astro@4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3)) arctic: specifier: catalog:studiocms-shared version: 1.9.2 @@ -388,9 +379,6 @@ importers: unocss: specifier: catalog:studiocms-shared version: 0.62.3(postcss@8.4.47)(rollup@4.21.0)(vite@5.4.8(@types/node@22.0.0)) - unpic: - specifier: catalog:studiocms-shared - version: 3.18.0 devDependencies: '@types/micromatch': specifier: catalog:studiocms-shared @@ -564,9 +552,6 @@ importers: unified: specifier: catalog:studiocms-shared version: 11.0.5 - unpic: - specifier: catalog:studiocms-shared - version: 3.18.0 devDependencies: typescript: specifier: 'catalog:' @@ -712,18 +697,12 @@ importers: '@studiocms/core': specifier: workspace:* version: link:../studiocms_core - '@unpic/astro': - specifier: catalog:studiocms-imagehandler - version: 0.0.47(astro@4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3)) astro: specifier: catalog:min version: 4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3) astro-integration-kit: specifier: 'catalog:' version: 0.16.1(astro@4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3)) - unpic: - specifier: catalog:studiocms-shared - version: 3.18.0 devDependencies: typescript: specifier: 'catalog:' @@ -859,6 +838,9 @@ importers: astro: specifier: 'catalog:' version: 4.16.6(@types/node@20.14.13)(rollup@4.21.0)(typescript@5.6.3) + sharp: + specifier: 'catalog:' + version: 0.33.4 studiocms: specifier: workspace:* version: link:../../packages/studiocms @@ -3046,20 +3028,6 @@ packages: peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 - '@unpic/astro@0.0.47': - resolution: {integrity: sha512-V35x+L8hzJ0w+VnoaCNjXOMYbm5B+WTKa3RQxYUw2/hJZ4Jo4a1uqYbiORJO77eGSaJGQBOGB9RT0BWMOmEz9Q==} - peerDependencies: - astro: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - - '@unpic/core@0.0.49': - resolution: {integrity: sha512-tAqeJRMPF2TrZbSQe74OZ9O5DzKDDUoFwFbZUpjvLcgwGQ/8aleDCb2Iy3bHFJfzzYZ9iHN0hN1VpTlAGQd+ZA==} - - '@unpic/pixels@1.2.2': - resolution: {integrity: sha512-zQWLcz/HX6IF1xQPyo1RtxW5aBkK470p8ZpRTtPidOZ26YAoxJhcW8Y59KzwRQSLdQV9B50V4m30TMgdFNxq1A==} - - '@unpic/placeholder@0.1.2': - resolution: {integrity: sha512-O++tS97biojo5sqn5TeTt+jUjl5gWOdIQuOXe8YluTJWq4L0GM6VuTkaspNpsmxHfioJw/6YBirzOpG4t87l8Q==} - '@vitejs/plugin-react@4.3.1': resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3287,9 +3255,6 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - blurhash@2.0.5: - resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -4356,9 +4321,6 @@ packages: resolution: {integrity: sha512-c+PHQZakiQuMKbnhvrjZUvrK6E/AfmTOf4P+E3Y4FNVHcNMX9e/XrnbEvO+m4wS6ZjsvhHh/POQTlfy8uXFc0A==} hasBin: true - jpeg-js@0.4.4: - resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} - js-base64@3.7.7: resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} @@ -4895,9 +4857,6 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - ofetch@1.3.3: - resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==} - ofetch@1.3.4: resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==} @@ -5090,10 +5049,6 @@ packages: pkg-types@1.1.3: resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} - pngjs@7.0.0: - resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} - engines: {node: '>=14.19.0'} - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -5811,9 +5766,6 @@ packages: vite: optional: true - unpic@3.18.0: - resolution: {integrity: sha512-JemzuG3nyKpEQ/DArrYM0l+LDSLLPYiUQvDfGXJY35+r0J0C984vPB4Zh8DyMVip102YSnTeZtZ6Q8OQegQDRQ==} - update-browserslist-db@1.1.0: resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true @@ -8448,28 +8400,6 @@ snapshots: - rollup - supports-color - '@unpic/astro@0.0.47(astro@4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3))': - dependencies: - '@unpic/core': 0.0.49 - '@unpic/pixels': 1.2.2 - '@unpic/placeholder': 0.1.2 - astro: 4.16.2(@types/node@22.0.0)(rollup@4.21.0)(typescript@5.6.3) - blurhash: 2.0.5 - - '@unpic/core@0.0.49': - dependencies: - unpic: 3.18.0 - - '@unpic/pixels@1.2.2': - dependencies: - jpeg-js: 0.4.4 - ofetch: 1.3.3 - pngjs: 7.0.0 - - '@unpic/placeholder@0.1.2': - dependencies: - blurhash: 2.0.5 - '@vitejs/plugin-react@4.3.1(vite@5.4.8(@types/node@22.0.0))': dependencies: '@babel/core': 7.25.7 @@ -9043,8 +8973,6 @@ snapshots: binary-extensions@2.3.0: {} - blurhash@2.0.5: {} - boolbase@1.0.0: {} boxen@8.0.1: @@ -10145,8 +10073,6 @@ snapshots: jiti@2.0.0-beta.2: {} - jpeg-js@0.4.4: {} - js-base64@3.7.7: {} js-tokens@4.0.0: {} @@ -10943,12 +10869,6 @@ snapshots: object-hash@3.0.0: {} - ofetch@1.3.3: - dependencies: - destr: 2.0.3 - node-fetch-native: 1.6.4 - ufo: 1.5.4 - ofetch@1.3.4: dependencies: destr: 2.0.3 @@ -11157,8 +11077,6 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 - pngjs@7.0.0: {} - postcss-import@15.1.0(postcss@8.4.47): dependencies: postcss: 8.4.47 @@ -12083,8 +12001,6 @@ snapshots: - rollup - supports-color - unpic@3.18.0: {} - update-browserslist-db@1.1.0(browserslist@4.23.2): dependencies: browserslist: 4.23.2 From bfaa27a3c258d1b7558ad51fce7f0a03c84be4f2 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 05:22:16 -0700 Subject: [PATCH 05/11] init --- packages/studiocms_core/src/db/config.ts | 4 + packages/studiocms_core/src/db/tables.ts | 25 ++++ packages/studiocms_core/src/db/tsTables.ts | 17 +++ .../studiocms_devapps/src/apps/wp-importer.ts | 1 + .../src/routes/wp-api-importer.ts | 9 +- .../src/schema/wp-api/index.ts | 30 +++++ .../src/utils/wp-api/converters.ts | 107 +++++++++++++++++- .../src/utils/wp-api/index.ts | 73 ++++++++++-- .../src/utils/wp-api/utils.ts | 15 ++- 9 files changed, 266 insertions(+), 15 deletions(-) diff --git a/packages/studiocms_core/src/db/config.ts b/packages/studiocms_core/src/db/config.ts index d32375365..28be127af 100644 --- a/packages/studiocms_core/src/db/config.ts +++ b/packages/studiocms_core/src/db/config.ts @@ -3,6 +3,8 @@ import { defineDb } from 'astro:db'; import { StudioCMSPageContent, StudioCMSPageData, + StudioCMSPageDataCategories, + StudioCMSPageDataTags, StudioCMSPermissions, StudioCMSSessionTable, StudioCMSSiteConfig, @@ -14,6 +16,8 @@ export default defineDb({ tables: { StudioCMSPageContent, StudioCMSPageData, + StudioCMSPageDataCategories, + StudioCMSPageDataTags, StudioCMSPermissions, StudioCMSSessionTable, StudioCMSSiteConfig, diff --git a/packages/studiocms_core/src/db/tables.ts b/packages/studiocms_core/src/db/tables.ts index 6ca1bd86f..2ba60b045 100644 --- a/packages/studiocms_core/src/db/tables.ts +++ b/packages/studiocms_core/src/db/tables.ts @@ -46,6 +46,29 @@ export const StudioCMSPageData = defineTable({ default: 'https://images.unsplash.com/photo-1707343843982-f8275f3994c5?q=80&w=1032&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', }), + catagories: column.json({ default: [], optional: true }), + tags: column.json({ default: [], optional: true }), + }, +}); + +export const StudioCMSPageDataTags = defineTable({ + columns: { + id: column.number({ primaryKey: true }), + description: column.text(), + name: column.text(), + slug: column.text(), + meta: column.json({ default: {} }), + }, +}); + +export const StudioCMSPageDataCategories = defineTable({ + columns: { + id: column.number({ primaryKey: true }), + parent: column.number({ optional: true }), + description: column.text(), + name: column.text(), + slug: column.text(), + meta: column.json({ default: {} }), }, }); @@ -65,6 +88,8 @@ export const StudioCMSSiteConfig = defineTable({ id: column.number({ primaryKey: true }), title: column.text(), description: column.text(), + defaultOgImage: column.text({ optional: true }), + siteIcon: column.text({ optional: true }), }, }); diff --git a/packages/studiocms_core/src/db/tsTables.ts b/packages/studiocms_core/src/db/tsTables.ts index a014bba61..9fac4e3b4 100644 --- a/packages/studiocms_core/src/db/tsTables.ts +++ b/packages/studiocms_core/src/db/tsTables.ts @@ -2,6 +2,8 @@ import { asDrizzleTable } from '@astrojs/db/utils'; import { StudioCMSPageContent, StudioCMSPageData, + StudioCMSPageDataCategories, + StudioCMSPageDataTags, StudioCMSPermissions, StudioCMSSessionTable, StudioCMSSiteConfig, @@ -20,6 +22,21 @@ export const tsPageContent = asDrizzleTable('StudioCMSPageContent', StudioCMSPag */ export const tsPageData = asDrizzleTable('StudioCMSPageData', StudioCMSPageData); +/** + * # StudioCMS - Page Data Categories Table + * @description Exported TypeSafe Table definition for use in StudioCMS Integrations + */ +export const tsPageDataCategories = asDrizzleTable( + 'StudioCMSPageDataCategories', + StudioCMSPageDataCategories +); + +/** + * # StudioCMS - Page Data Tags Table + * @description Exported TypeSafe Table definition for use in StudioCMS Integrations + */ +export const tsPageDataTags = asDrizzleTable('StudioCMSPageDataTags', StudioCMSPageDataTags); + /** * # StudioCMS - Permissions Table * @description Exported TypeSafe Table definition for use in StudioCMS Integrations diff --git a/packages/studiocms_devapps/src/apps/wp-importer.ts b/packages/studiocms_devapps/src/apps/wp-importer.ts index 4847fb901..e27d900a4 100644 --- a/packages/studiocms_devapps/src/apps/wp-importer.ts +++ b/packages/studiocms_devapps/src/apps/wp-importer.ts @@ -139,6 +139,7 @@ export default defineToolbarApp({ diff --git a/packages/studiocms_devapps/src/routes/wp-api-importer.ts b/packages/studiocms_devapps/src/routes/wp-api-importer.ts index 34e38b116..46d68a264 100644 --- a/packages/studiocms_devapps/src/routes/wp-api-importer.ts +++ b/packages/studiocms_devapps/src/routes/wp-api-importer.ts @@ -1,5 +1,9 @@ import type { APIContext, APIRoute } from 'astro'; -import { importPagesFromWPAPI, importPostsFromWPAPI } from '../utils/wp-api'; +import { + importPagesFromWPAPI, + importPostsFromWPAPI, + importSettingsFromWPAPI, +} from '../utils/wp-api'; export const POST: APIRoute = async ({ request }: APIContext) => { const data = await request.formData(); @@ -41,6 +45,9 @@ export const POST: APIRoute = async ({ request }: APIContext) => { case 'posts': await importPostsFromWPAPI(url, useBlogPluginValue); break; + case 'settings': + await importSettingsFromWPAPI(url); + break; default: throw new Error('Invalid import type'); } diff --git a/packages/studiocms_devapps/src/schema/wp-api/index.ts b/packages/studiocms_devapps/src/schema/wp-api/index.ts index cae120d1a..07f3ab429 100644 --- a/packages/studiocms_devapps/src/schema/wp-api/index.ts +++ b/packages/studiocms_devapps/src/schema/wp-api/index.ts @@ -43,5 +43,35 @@ export const PostSchema = PageSchema.extend({ tags: z.array(z.number()), }); +export const TagSchema = z.object({ + id: z.number(), + count: z.number(), + description: z.string(), + link: z.string().url(), + name: z.string(), + slug: z.string(), + taxonomy: z.string(), + meta: z.array(z.any()).or(z.record(z.any())), +}); + +export const CategorySchema = TagSchema.extend({ + parent: z.number(), +}); + +export const SiteSettingsSchema = z.object({ + name: z.string(), + description: z.string(), + url: z.string(), + home: z.string(), + gmt_offset: z.coerce.number(), + timezone_string: z.string(), + site_logo: z.number().optional(), + site_icon: z.number().optional(), + site_icon_url: z.string().optional(), +}); + export type Page = typeof PageSchema._output; export type Post = typeof PostSchema._output; +export type Tag = typeof TagSchema._output; +export type Category = typeof CategorySchema._output; +export type SiteSettings = typeof SiteSettingsSchema._output; diff --git a/packages/studiocms_devapps/src/utils/wp-api/converters.ts b/packages/studiocms_devapps/src/utils/wp-api/converters.ts index ae431b9b1..56fb8d481 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/converters.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/converters.ts @@ -1,8 +1,10 @@ import path from 'node:path'; +import { db, eq } from 'astro:db'; import Config from 'virtual:studiocms-devapps/wp-api/configPath'; +import { tsPageDataCategories, tsPageDataTags } from '@studiocms/core/db/tsTables'; import { decode } from 'html-entities'; import TurndownService from 'turndown'; -import type { Page, Post } from '../../schema/wp-api'; +import type { Category, Page, Post, Tag } from '../../schema/wp-api'; import type { PageContent, PageData } from './index'; import { apiEndpoint, @@ -76,17 +78,114 @@ export const ConvertToPageContent = async ( return pageContent; }; -export const ConvertToPostData = async (post: unknown, useBlogPkg: boolean): Promise => { +export const generateCatagories = async (categories: number[], endpoint: string) => { + const newCatagories: Category[] = []; + + for (const catagoryId of categories) { + // Check if catagory already exists in the database + const catagoryExists = await db + .select() + .from(tsPageDataCategories) + .where(eq(tsPageDataCategories.id, catagoryId)) + .get(); + + if (!catagoryExists) { + const catagoryURL = apiEndpoint(endpoint, 'catagories', `${catagoryId}`); + const response = await fetch(catagoryURL); + const json = await response.json(); + newCatagories.push(json); + } + + if (catagoryExists) { + console.log(`Catagory with id ${catagoryId} already exists in the database`); + } + } + + if (newCatagories.length > 0) { + const catagoryData = newCatagories.map((catagory) => { + const data: typeof tsPageDataCategories.$inferInsert = { + id: catagory.id, + name: catagory.name, + slug: catagory.slug, + description: catagory.description, + meta: JSON.stringify(catagory.meta), + }; + + if (catagory.parent) { + data.parent = catagory.parent; + } + + return data; + }); + + for (const catagory of catagoryData) { + console.log(`Inserting catagory with id ${catagory.id} into the database`); + await db.insert(tsPageDataCategories).values(catagory); + } + } +}; + +export const generateTags = async (tags: number[], endpoint: string) => { + const newTags: Tag[] = []; + + for (const tagId of tags) { + // Check if tag already exists in the database + const tagExists = await db + .select() + .from(tsPageDataTags) + .where(eq(tsPageDataTags.id, tagId)) + .get(); + + if (!tagExists) { + const tagURL = apiEndpoint(endpoint, 'tags', `${tagId}`); + const response = await fetch(tagURL); + const json = await response.json(); + newTags.push(json); + } + + if (tagExists) { + console.log(`Tag with id ${tagId} already exists in the database`); + } + } + + if (newTags.length > 0) { + const tagData = newTags.map((tag) => { + const data: typeof tsPageDataTags.$inferInsert = { + id: tag.id, + name: tag.name, + slug: tag.slug, + description: tag.description, + meta: JSON.stringify(tag.meta), + }; + + return data; + }); + + for (const tag of tagData) { + console.log(`Inserting tag with id ${tag.id} into the database`); + await db.insert(tsPageDataTags).values(tag); + } + } +}; + +export const ConvertToPostData = async ( + post: unknown, + useBlogPkg: boolean, + endpoint: string +): Promise => { const data = post as Post; const titleImageId = data.featured_media; - const titleImageURL = apiEndpoint(`${titleImageId}`, 'media'); + const titleImageURL = apiEndpoint(endpoint, 'media', `${titleImageId}`); const titleImageResponse = await fetch(titleImageURL); const titleImageJson = await titleImageResponse.json(); const titleImage = await downloadPostImage(titleImageJson.source_url, pagesImagesFolder); const pkg = useBlogPkg ? '@studiocms/blog' : 'studiocms'; + await generateCatagories(data.categories, endpoint); + await generateTags(data.tags, endpoint); + const pageData: PageData = { id: crypto.randomUUID(), title: data.title.rendered, @@ -97,6 +196,8 @@ export const ConvertToPostData = async (post: unknown, useBlogPkg: boolean): Pro showOnNav: false, contentLang: 'default', package: pkg, + catagories: JSON.stringify(data.categories), + tags: JSON.stringify(data.tags), }; if (titleImage) { diff --git a/packages/studiocms_devapps/src/utils/wp-api/index.ts b/packages/studiocms_devapps/src/utils/wp-api/index.ts index a5ed7c12f..2ea68e049 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/index.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/index.ts @@ -1,14 +1,18 @@ +import path from 'node:path'; /// -import { db } from 'astro:db'; -import { tsPageContent, tsPageData } from '@studiocms/core/db/tsTables'; -import type { Page } from '../../schema/wp-api'; +import { db, eq } from 'astro:db'; +import Config from 'virtual:studiocms-devapps/wp-api/configPath'; +import { tsPageContent, tsPageData, tsSiteConfig } from '@studiocms/core/db/tsTables'; +import type { Page, SiteSettings } from '../../schema/wp-api'; import { ConvertToPageContent, ConvertToPageData, ConvertToPostContent, ConvertToPostData, } from './converters'; -import { apiEndpoint, fetchAll } from './utils'; +import { apiEndpoint, downloadPostImage, fetchAll } from './utils'; + +const ASTROPUBLICFOLDER = path.resolve(Config.projectRoot, 'public'); export type PageData = typeof tsPageData.$inferInsert; export type PageContent = typeof tsPageContent.$inferInsert; @@ -20,8 +24,8 @@ const generatePageFromData = async (page: unknown) => { return { pageData, pageContent }; }; -const generatePostFromData = async (post: unknown, useBlogPkg: boolean) => { - const pageData = await ConvertToPostData(post, useBlogPkg); +const generatePostFromData = async (post: unknown, useBlogPkg: boolean, endpoint: string) => { + const pageData = await ConvertToPostData(post, useBlogPkg, endpoint); const pageContent = await ConvertToPostContent(pageData, post); return { pageData, pageContent }; @@ -72,8 +76,8 @@ export const importPagesFromWPAPI = async (endpoint: string) => { } }; -const importPost = async (post: unknown, useBlogPkg: boolean) => { - const { pageData, pageContent } = await generatePostFromData(post, useBlogPkg); +const importPost = async (post: unknown, useBlogPkg: boolean, endpoint: string) => { + const { pageData, pageContent } = await generatePostFromData(post, useBlogPkg, endpoint); const pageDataResult = await db .insert(tsPageData) @@ -110,9 +114,60 @@ export const importPostsFromWPAPI = async (endpoint: string, useBlogPkg: boolean try { for (const post of posts) { console.log('importing post: ', post.title.rendered); - await importPost(post, useBlogPkg); + await importPost(post, useBlogPkg, endpoint); } } catch (error) { console.error('Failed to import posts from WP-API: ', error); } }; + +export const importSettingsFromWPAPI = async (endpoint: string) => { + const url = apiEndpoint(endpoint, 'settings'); + + console.log('Fetching site settings from: ', url.origin); + + const response = await fetch(url); + const settings: SiteSettings = await response.json(); + + console.log('Importing site settings: ', settings); + + let siteIcon: string | undefined = undefined; + + if (settings.site_icon_url) { + siteIcon = await downloadPostImage(settings.site_icon_url, ASTROPUBLICFOLDER); + } + + if (!settings.site_icon_url && settings.site_logo) { + const siteLogoURL = apiEndpoint(endpoint, 'media', `${settings.site_logo}`); + const siteLogoResponse = await fetch(siteLogoURL); + const siteLogoJson = await siteLogoResponse.json(); + siteIcon = await downloadPostImage(siteLogoJson.source_url, ASTROPUBLICFOLDER); + } + + const siteConfig: typeof tsSiteConfig.$inferInsert = { + id: 1, + title: settings.name, + description: settings.description, + }; + + if (siteIcon) { + siteConfig.siteIcon = siteIcon; + } + + try { + const insert = await db + .update(tsSiteConfig) + .set(siteConfig) + .where(eq(tsSiteConfig.id, 1)) + .returning({ id: tsSiteConfig.id }) + .get(); + + if (insert) { + console.log('Updated site settings'); + } else { + console.error('Failed to update site settings'); + } + } catch (error) { + console.error('Failed to import site settings from WP-API: ', error); + } +}; diff --git a/packages/studiocms_devapps/src/utils/wp-api/utils.ts b/packages/studiocms_devapps/src/utils/wp-api/utils.ts index dcdc920d8..28e737e8f 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/utils.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/utils.ts @@ -106,7 +106,11 @@ export const downloadAndUpdateImages = async (html: string, pathToFolder: string return $.html(); }; -export const apiEndpoint = (endpoint: string, type: 'posts' | 'pages' | 'media') => { +export const apiEndpoint = ( + endpoint: string, + type: 'posts' | 'pages' | 'media' | 'catagories' | 'tags' | 'settings', + path?: string +) => { if (!endpoint) { throw new AstroError( 'Missing `endpoint` argument.', @@ -115,8 +119,15 @@ export const apiEndpoint = (endpoint: string, type: 'posts' | 'pages' | 'media') } let newEndpoint = endpoint; if (!newEndpoint.endsWith('/')) newEndpoint += '/'; + const apiBase = new URL(newEndpoint); - apiBase.pathname = `wp-json/wp/v2/${type}`; + + if (type === 'settings') { + apiBase.pathname = 'wp-json/'; + return apiBase; + } + + apiBase.pathname = `wp-json/wp/v2/${type}${path ? `/${path}` : ''}`; return apiBase; }; From 987fe71bbc21786ccd302e45af3157d4820d0c46 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 05:27:32 -0700 Subject: [PATCH 06/11] add changeset --- .changeset/clever-yaks-push.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/clever-yaks-push.md diff --git a/.changeset/clever-yaks-push.md b/.changeset/clever-yaks-push.md new file mode 100644 index 000000000..8f0b743b8 --- /dev/null +++ b/.changeset/clever-yaks-push.md @@ -0,0 +1,6 @@ +--- +"@studiocms/devapps": patch +"@studiocms/core": patch +--- + +Expand PageData table schema and add Catagory and Tags schemas, and extend WP-importer From 6a00b066dc936f429d2a3b70978a308ff0b2e4d4 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 06:54:37 -0700 Subject: [PATCH 07/11] refactor as per @jdtjenkins --- .../src/utils/wp-api/converters.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/studiocms_devapps/src/utils/wp-api/converters.ts b/packages/studiocms_devapps/src/utils/wp-api/converters.ts index 56fb8d481..bd773ddc8 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/converters.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/converters.ts @@ -89,16 +89,14 @@ export const generateCatagories = async (categories: number[], endpoint: string) .where(eq(tsPageDataCategories.id, catagoryId)) .get(); - if (!catagoryExists) { - const catagoryURL = apiEndpoint(endpoint, 'catagories', `${catagoryId}`); - const response = await fetch(catagoryURL); - const json = await response.json(); - newCatagories.push(json); - } - if (catagoryExists) { console.log(`Catagory with id ${catagoryId} already exists in the database`); } + + const catagoryURL = apiEndpoint(endpoint, 'catagories', `${catagoryId}`); + const response = await fetch(catagoryURL); + const json = await response.json(); + newCatagories.push(json); } if (newCatagories.length > 0) { @@ -136,16 +134,14 @@ export const generateTags = async (tags: number[], endpoint: string) => { .where(eq(tsPageDataTags.id, tagId)) .get(); - if (!tagExists) { - const tagURL = apiEndpoint(endpoint, 'tags', `${tagId}`); - const response = await fetch(tagURL); - const json = await response.json(); - newTags.push(json); - } - if (tagExists) { console.log(`Tag with id ${tagId} already exists in the database`); } + + const tagURL = apiEndpoint(endpoint, 'tags', `${tagId}`); + const response = await fetch(tagURL); + const json = await response.json(); + newTags.push(json); } if (newTags.length > 0) { From 338f777c050a3f23a43c457890a1d75c693e314b Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 06:55:32 -0700 Subject: [PATCH 08/11] Refactor wp-api converters to skip existing categories and tags --- packages/studiocms_devapps/src/utils/wp-api/converters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/studiocms_devapps/src/utils/wp-api/converters.ts b/packages/studiocms_devapps/src/utils/wp-api/converters.ts index bd773ddc8..2c4344253 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/converters.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/converters.ts @@ -91,6 +91,7 @@ export const generateCatagories = async (categories: number[], endpoint: string) if (catagoryExists) { console.log(`Catagory with id ${catagoryId} already exists in the database`); + continue; } const catagoryURL = apiEndpoint(endpoint, 'catagories', `${catagoryId}`); @@ -136,6 +137,7 @@ export const generateTags = async (tags: number[], endpoint: string) => { if (tagExists) { console.log(`Tag with id ${tagId} already exists in the database`); + continue; } const tagURL = apiEndpoint(endpoint, 'tags', `${tagId}`); From 946a6704f5757b4b92cebd235a2616adbd18e9cf Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 06:57:02 -0700 Subject: [PATCH 09/11] Refactor wp-api index.ts to use CMSSiteConfigId constant for site config ID --- packages/studiocms_devapps/src/utils/wp-api/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/studiocms_devapps/src/utils/wp-api/index.ts b/packages/studiocms_devapps/src/utils/wp-api/index.ts index 2ea68e049..3b04677e2 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/index.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/index.ts @@ -2,6 +2,7 @@ import path from 'node:path'; /// import { db, eq } from 'astro:db'; import Config from 'virtual:studiocms-devapps/wp-api/configPath'; +import { CMSSiteConfigId } from '@studiocms/core/consts'; import { tsPageContent, tsPageData, tsSiteConfig } from '@studiocms/core/db/tsTables'; import type { Page, SiteSettings } from '../../schema/wp-api'; import { @@ -145,7 +146,7 @@ export const importSettingsFromWPAPI = async (endpoint: string) => { } const siteConfig: typeof tsSiteConfig.$inferInsert = { - id: 1, + id: CMSSiteConfigId, title: settings.name, description: settings.description, }; @@ -158,7 +159,7 @@ export const importSettingsFromWPAPI = async (endpoint: string) => { const insert = await db .update(tsSiteConfig) .set(siteConfig) - .where(eq(tsSiteConfig.id, 1)) + .where(eq(tsSiteConfig.id, CMSSiteConfigId)) .returning({ id: tsSiteConfig.id }) .get(); From c52a2a1e2f8046db3750b65b186f82af845c48ad Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Sun, 27 Oct 2024 15:09:51 -0700 Subject: [PATCH 10/11] Refactor wp-api converters to use endpoint parameter in ConvertToPageData and importPage functions --- .../studiocms_devapps/src/utils/wp-api/converters.ts | 4 ++-- packages/studiocms_devapps/src/utils/wp-api/index.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/studiocms_devapps/src/utils/wp-api/converters.ts b/packages/studiocms_devapps/src/utils/wp-api/converters.ts index 2c4344253..13b43ca47 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/converters.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/converters.ts @@ -19,11 +19,11 @@ const WPImportFolder = path.resolve(ASTROPUBLICFOLDER, 'wp-import'); const pagesImagesFolder = path.resolve(WPImportFolder, 'pages'); const postsImagesFolder = path.resolve(WPImportFolder, 'posts'); -export const ConvertToPageData = async (page: unknown): Promise => { +export const ConvertToPageData = async (page: unknown, endpoint: string): Promise => { const data = page as Page; const titleImageId = data.featured_media; - const titleImageURL = apiEndpoint(`${titleImageId}`, 'media'); + const titleImageURL = apiEndpoint(endpoint, 'media', `${titleImageId}`); const titleImageResponse = await fetch(titleImageURL); const titleImageJson = await titleImageResponse.json(); const titleImage = await downloadPostImage(titleImageJson.source_url, pagesImagesFolder); diff --git a/packages/studiocms_devapps/src/utils/wp-api/index.ts b/packages/studiocms_devapps/src/utils/wp-api/index.ts index 3b04677e2..68166bf8a 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/index.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/index.ts @@ -18,8 +18,8 @@ const ASTROPUBLICFOLDER = path.resolve(Config.projectRoot, 'public'); export type PageData = typeof tsPageData.$inferInsert; export type PageContent = typeof tsPageContent.$inferInsert; -const generatePageFromData = async (page: unknown) => { - const pageData = await ConvertToPageData(page); +const generatePageFromData = async (page: unknown, endpoint: string) => { + const pageData = await ConvertToPageData(page, endpoint); const pageContent = await ConvertToPageContent(pageData, page); return { pageData, pageContent }; @@ -32,8 +32,8 @@ const generatePostFromData = async (post: unknown, useBlogPkg: boolean, endpoint return { pageData, pageContent }; }; -const importPage = async (page: unknown) => { - const { pageData, pageContent } = await generatePageFromData(page); +const importPage = async (page: unknown, endpoint: string) => { + const { pageData, pageContent } = await generatePageFromData(page, endpoint); const pageDataResult = await db .insert(tsPageData) @@ -70,7 +70,7 @@ export const importPagesFromWPAPI = async (endpoint: string) => { try { for (const page of pages) { console.log('importing page:', page.title.rendered); - await importPage(page); + await importPage(page, endpoint); } } catch (error) { console.error('Failed to import pages from WP-API:', error); From f5ab8e44b075f90f0e25f934edf247799e1681e5 Mon Sep 17 00:00:00 2001 From: Adam Matthiesen Date: Mon, 28 Oct 2024 16:30:30 -0700 Subject: [PATCH 11/11] Refactor wp-api utils.ts to fix API endpoint path generation --- packages/studiocms_devapps/src/utils/wp-api/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/studiocms_devapps/src/utils/wp-api/utils.ts b/packages/studiocms_devapps/src/utils/wp-api/utils.ts index 28e737e8f..e09486476 100644 --- a/packages/studiocms_devapps/src/utils/wp-api/utils.ts +++ b/packages/studiocms_devapps/src/utils/wp-api/utils.ts @@ -127,7 +127,7 @@ export const apiEndpoint = ( return apiBase; } - apiBase.pathname = `wp-json/wp/v2/${type}${path ? `/${path}` : ''}`; + apiBase.pathname = `wp-json/wp/v2/${type}/${path ? `${path}/` : ''}`; return apiBase; };