feat(embeds): add embed.classroomio.com public widget host
Serve widgets from short public URLs via tenant-router with R2-backed assets and runtime API_URL injection, so copy-paste snippets hide infra paths. Update widget payloads, CSP, upload purge URLs, and dev-only API inject; document format/stage workflow in AGENTS.md and fix ui-prefix-check YAML syntax.
This commit is contained in:
@@ -7,11 +7,33 @@ import { defineConfig } from 'vite';
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const devApiUrl = process.env.API_URL ?? 'http://localhost:3002';
|
||||
|
||||
function embedApiUrlInjectPlugin() {
|
||||
const bootstrapFiles = [
|
||||
path.resolve(dirname, 'src/widgets/course-widget/bootstrap.ts'),
|
||||
path.resolve(dirname, 'src/widgets/question-type-picker/bootstrap.ts')
|
||||
];
|
||||
|
||||
return {
|
||||
name: 'embed-api-url-inject',
|
||||
transform(code: string, id: string) {
|
||||
if (!bootstrapFiles.includes(id) || !devApiUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const snippet = `window.CIO=window.CIO||{};window.CIO.apiBaseUrl=${JSON.stringify(devApiUrl.replace(/\/$/, ''))};`;
|
||||
return { code: `${snippet}\n${code}`, map: null };
|
||||
}
|
||||
};
|
||||
}
|
||||
const embedEntries = {
|
||||
'question-type-picker': path.resolve(dirname, 'src/widgets/question-type-picker/bootstrap.ts'),
|
||||
'course-widget': path.resolve(dirname, 'src/widgets/course-widget/bootstrap.ts')
|
||||
} as const;
|
||||
const embedDevEntrypoints = {
|
||||
'/question-type-picker': '/src/widgets/question-type-picker/bootstrap.ts',
|
||||
'/course-widget': '/src/widgets/course-widget/bootstrap.ts',
|
||||
'/embeds/question-type-picker/question-type-picker.js': '/src/widgets/question-type-picker/bootstrap.ts',
|
||||
'/embeds/course-widget/course-widget.js': '/src/widgets/course-widget/bootstrap.ts',
|
||||
'/embeds/course-widget/embed.html': '/src/widgets/course-widget/embed.html'
|
||||
@@ -26,10 +48,28 @@ function embedDevEntryPlugin() {
|
||||
server.middlewares.use((req, _res, next) => {
|
||||
const requestUrl = req.url ?? '';
|
||||
const [pathname, search = ''] = requestUrl.split('?');
|
||||
const querySuffix = search ? `?${search}` : '';
|
||||
|
||||
if (pathname === '/course-widget') {
|
||||
const params = new URLSearchParams(search);
|
||||
const target = params.has('key')
|
||||
? '/src/widgets/course-widget/embed.html'
|
||||
: '/src/widgets/course-widget/bootstrap.ts';
|
||||
req.url = `${target}${querySuffix}`;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname === '/question-type-picker') {
|
||||
req.url = `/src/widgets/question-type-picker/bootstrap.ts${querySuffix}`;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
const rewrittenPath = embedDevEntrypoints[pathname as keyof typeof embedDevEntrypoints];
|
||||
|
||||
if (rewrittenPath) {
|
||||
req.url = search ? `${rewrittenPath}?${search}` : rewrittenPath;
|
||||
req.url = `${rewrittenPath}${querySuffix}`;
|
||||
}
|
||||
|
||||
next();
|
||||
@@ -44,7 +84,13 @@ export default defineConfig(({ command, mode }) => {
|
||||
|
||||
return {
|
||||
root: dirname,
|
||||
plugins: [embedDevEntryPlugin(), tailwindcss(), svelte(), cssInjectedByJsPlugin()],
|
||||
plugins: [
|
||||
embedDevEntryPlugin(),
|
||||
...(command === 'serve' ? [embedApiUrlInjectPlugin()] : []),
|
||||
tailwindcss(),
|
||||
svelte(),
|
||||
cssInjectedByJsPlugin()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@cio/ui': path.resolve(dirname, 'node_modules/@cio/ui/src'),
|
||||
@@ -72,7 +118,7 @@ export default defineConfig(({ command, mode }) => {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
inlineDynamicImports: false,
|
||||
chunkFileNames: '[name]-[hash].js'
|
||||
chunkFileNames: buildTarget ? `${buildTarget}-[name]-[hash].js` : '[name]-[hash].js'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user