dineway 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -3
- package/src/astro/routes/PluginRegistry.tsx +21 -0
- package/src/astro/routes/admin.astro +83 -0
- package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
- package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
- package/src/astro/routes/api/admin/api-tokens/[id].ts +40 -0
- package/src/astro/routes/api/admin/api-tokens/index.ts +68 -0
- package/src/astro/routes/api/admin/bylines/[id]/index.ts +87 -0
- package/src/astro/routes/api/admin/bylines/index.ts +72 -0
- package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
- package/src/astro/routes/api/admin/comments/[id].ts +64 -0
- package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
- package/src/astro/routes/api/admin/comments/counts.ts +30 -0
- package/src/astro/routes/api/admin/comments/index.ts +46 -0
- package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +91 -0
- package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +110 -0
- package/src/astro/routes/api/admin/oauth-clients/index.ts +71 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +48 -0
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +59 -0
- package/src/astro/routes/api/admin/plugins/index.ts +32 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +62 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +64 -0
- package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +62 -0
- package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
- package/src/astro/routes/api/admin/users/[id]/disable.ts +69 -0
- package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
- package/src/astro/routes/api/admin/users/[id]/index.ts +146 -0
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
- package/src/astro/routes/api/admin/users/index.ts +66 -0
- package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
- package/src/astro/routes/api/auth/invite/accept.ts +52 -0
- package/src/astro/routes/api/auth/invite/complete.ts +86 -0
- package/src/astro/routes/api/auth/invite/index.ts +99 -0
- package/src/astro/routes/api/auth/logout.ts +40 -0
- package/src/astro/routes/api/auth/magic-link/send.ts +89 -0
- package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
- package/src/astro/routes/api/auth/me.ts +60 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +221 -0
- package/src/astro/routes/api/auth/oauth/[provider].ts +120 -0
- package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
- package/src/astro/routes/api/auth/passkey/index.ts +54 -0
- package/src/astro/routes/api/auth/passkey/options.ts +84 -0
- package/src/astro/routes/api/auth/passkey/register/options.ts +88 -0
- package/src/astro/routes/api/auth/passkey/register/verify.ts +119 -0
- package/src/astro/routes/api/auth/passkey/verify.ts +68 -0
- package/src/astro/routes/api/auth/signup/complete.ts +87 -0
- package/src/astro/routes/api/auth/signup/request.ts +77 -0
- package/src/astro/routes/api/auth/signup/verify.ts +53 -0
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +311 -0
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +61 -0
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +33 -0
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +105 -0
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +140 -0
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +30 -0
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +137 -0
- package/src/astro/routes/api/content/[collection]/index.ts +59 -0
- package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
- package/src/astro/routes/api/dashboard.ts +32 -0
- package/src/astro/routes/api/dev/emails.ts +36 -0
- package/src/astro/routes/api/import/probe.ts +47 -0
- package/src/astro/routes/api/import/wordpress/analyze.ts +531 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +296 -0
- package/src/astro/routes/api/import/wordpress/media.ts +338 -0
- package/src/astro/routes/api/import/wordpress/prepare.ts +181 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +393 -0
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
- package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +357 -0
- package/src/astro/routes/api/manifest.ts +63 -0
- package/src/astro/routes/api/mcp.ts +124 -0
- package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
- package/src/astro/routes/api/media/[id].ts +145 -0
- package/src/astro/routes/api/media/file/[...key].ts +79 -0
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +86 -0
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
- package/src/astro/routes/api/media/providers/index.ts +30 -0
- package/src/astro/routes/api/media/upload-url.ts +137 -0
- package/src/astro/routes/api/media.ts +202 -0
- package/src/astro/routes/api/menus/[name]/items.ts +87 -0
- package/src/astro/routes/api/menus/[name]/reorder.ts +33 -0
- package/src/astro/routes/api/menus/[name].ts +65 -0
- package/src/astro/routes/api/menus/index.ts +47 -0
- package/src/astro/routes/api/oauth/authorize.ts +417 -0
- package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
- package/src/astro/routes/api/oauth/device/code.ts +55 -0
- package/src/astro/routes/api/oauth/device/token.ts +69 -0
- package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
- package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
- package/src/astro/routes/api/oauth/token.ts +184 -0
- package/src/astro/routes/api/openapi.json.ts +32 -0
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +92 -0
- package/src/astro/routes/api/redirects/404s/index.ts +72 -0
- package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
- package/src/astro/routes/api/redirects/[id].ts +84 -0
- package/src/astro/routes/api/redirects/index.ts +52 -0
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +76 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +52 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +32 -0
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +80 -0
- package/src/astro/routes/api/schema/collections/index.ts +47 -0
- package/src/astro/routes/api/schema/index.ts +109 -0
- package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
- package/src/astro/routes/api/schema/orphans/index.ts +26 -0
- package/src/astro/routes/api/search/enable.ts +64 -0
- package/src/astro/routes/api/search/index.ts +51 -0
- package/src/astro/routes/api/search/rebuild.ts +72 -0
- package/src/astro/routes/api/search/stats.ts +35 -0
- package/src/astro/routes/api/search/suggest.ts +49 -0
- package/src/astro/routes/api/sections/[slug].ts +84 -0
- package/src/astro/routes/api/sections/index.ts +52 -0
- package/src/astro/routes/api/settings/email.ts +150 -0
- package/src/astro/routes/api/settings.ts +67 -0
- package/src/astro/routes/api/setup/admin-verify.ts +102 -0
- package/src/astro/routes/api/setup/admin.ts +96 -0
- package/src/astro/routes/api/setup/dev-bypass.ts +200 -0
- package/src/astro/routes/api/setup/dev-reset.ts +40 -0
- package/src/astro/routes/api/setup/index.ts +127 -0
- package/src/astro/routes/api/setup/status.ts +122 -0
- package/src/astro/routes/api/snapshot.ts +76 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +95 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +69 -0
- package/src/astro/routes/api/taxonomies/index.ts +59 -0
- package/src/astro/routes/api/themes/preview.ts +78 -0
- package/src/astro/routes/api/typegen.ts +114 -0
- package/src/astro/routes/api/well-known/auth.ts +69 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +45 -0
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +38 -0
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +72 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +127 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +80 -0
- package/src/astro/routes/api/widget-areas/[name].ts +87 -0
- package/src/astro/routes/api/widget-areas/index.ts +99 -0
- package/src/astro/routes/api/widget-components.ts +22 -0
- package/src/astro/routes/robots.txt.ts +81 -0
- package/src/astro/routes/sitemap-[collection].xml.ts +104 -0
- package/src/astro/routes/sitemap.xml.ts +92 -0
- package/src/components/Break.astro +45 -0
- package/src/components/Button.astro +71 -0
- package/src/components/Buttons.astro +49 -0
- package/src/components/Code.astro +59 -0
- package/src/components/Columns.astro +59 -0
- package/src/components/CommentForm.astro +315 -0
- package/src/components/Comments.astro +232 -0
- package/src/components/Cover.astro +128 -0
- package/src/components/DinewayBodyEnd.astro +32 -0
- package/src/components/DinewayBodyStart.astro +32 -0
- package/src/components/DinewayHead.astro +53 -0
- package/src/components/DinewayImage.astro +178 -0
- package/src/components/DinewayMedia.astro +167 -0
- package/src/components/Embed.astro +128 -0
- package/src/components/File.astro +122 -0
- package/src/components/Gallery.astro +93 -0
- package/src/components/HtmlBlock.astro +33 -0
- package/src/components/Image.astro +178 -0
- package/src/components/InlineEditor.astro +27 -0
- package/src/components/InlinePortableTextEditor.tsx +1937 -0
- package/src/components/LiveSearch.astro +614 -0
- package/src/components/PortableText.astro +51 -0
- package/src/components/Pullquote.astro +51 -0
- package/src/components/Table.astro +108 -0
- package/src/components/WidgetArea.astro +22 -0
- package/src/components/WidgetRenderer.astro +72 -0
- package/src/components/index.ts +116 -0
- package/src/components/marks/Link.astro +31 -0
- package/src/components/marks/StrikeThrough.astro +7 -0
- package/src/components/marks/Subscript.astro +7 -0
- package/src/components/marks/Superscript.astro +7 -0
- package/src/components/marks/Underline.astro +7 -0
- package/src/components/widgets/Archives.astro +65 -0
- package/src/components/widgets/Categories.astro +35 -0
- package/src/components/widgets/RecentPosts.astro +51 -0
- package/src/components/widgets/Search.astro +18 -0
- package/src/components/widgets/Tags.astro +38 -0
- package/src/ui.ts +75 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin enable endpoint
|
|
3
|
+
*
|
|
4
|
+
* POST /_dineway/api/admin/plugins/:id/enable - Enable a plugin
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handlePluginEnable } from "#api/index.js";
|
|
12
|
+
import { setCronTasksEnabled } from "#plugins/cron.js";
|
|
13
|
+
|
|
14
|
+
export const prerender = false;
|
|
15
|
+
|
|
16
|
+
export const POST: APIRoute = async ({ params, locals }) => {
|
|
17
|
+
const { dineway, user } = locals;
|
|
18
|
+
const { id } = params;
|
|
19
|
+
|
|
20
|
+
if (!dineway?.db) {
|
|
21
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const denied = requirePerm(user, "plugins:manage");
|
|
25
|
+
if (denied) return denied;
|
|
26
|
+
|
|
27
|
+
if (!id) {
|
|
28
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const result = await handlePluginEnable(dineway.db, dineway.configuredPlugins, id);
|
|
32
|
+
|
|
33
|
+
if (!result.success) return unwrapResult(result);
|
|
34
|
+
|
|
35
|
+
await dineway.setPluginStatus(id, "active");
|
|
36
|
+
await setCronTasksEnabled(dineway.db, id, true);
|
|
37
|
+
|
|
38
|
+
return unwrapResult(result);
|
|
39
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin management single plugin endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins/:id - Get plugin details
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handlePluginGet } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ params, locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
const { id } = params;
|
|
18
|
+
|
|
19
|
+
if (!dineway?.db) {
|
|
20
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const denied = requirePerm(user, "plugins:read");
|
|
24
|
+
if (denied) return denied;
|
|
25
|
+
|
|
26
|
+
if (!id) {
|
|
27
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = await handlePluginGet(
|
|
31
|
+
dineway.db,
|
|
32
|
+
dineway.configuredPlugins,
|
|
33
|
+
id,
|
|
34
|
+
dineway.config.marketplace,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return unwrapResult(result);
|
|
38
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin uninstall endpoint
|
|
3
|
+
*
|
|
4
|
+
* POST /_dineway/api/admin/plugins/:id/uninstall - Uninstall a marketplace plugin
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { requirePerm } from "#api/authorize.js";
|
|
11
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
12
|
+
import { handleMarketplaceUninstall } from "#api/index.js";
|
|
13
|
+
import { isParseError, parseOptionalBody } from "#api/parse.js";
|
|
14
|
+
|
|
15
|
+
export const prerender = false;
|
|
16
|
+
|
|
17
|
+
const uninstallBodySchema = z.object({
|
|
18
|
+
deleteData: z.boolean().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
22
|
+
const { dineway, user } = locals;
|
|
23
|
+
const { id } = params;
|
|
24
|
+
|
|
25
|
+
if (!dineway?.db) {
|
|
26
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const denied = requirePerm(user, "plugins:manage");
|
|
30
|
+
if (denied) return denied;
|
|
31
|
+
|
|
32
|
+
if (!id) {
|
|
33
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const body = await parseOptionalBody(request, uninstallBodySchema, {});
|
|
37
|
+
if (isParseError(body)) return body;
|
|
38
|
+
|
|
39
|
+
const result = await handleMarketplaceUninstall(dineway.db, dineway.storage, id, {
|
|
40
|
+
deleteData: body.deleteData ?? false,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!result.success) return unwrapResult(result);
|
|
44
|
+
|
|
45
|
+
await dineway.syncMarketplacePlugins();
|
|
46
|
+
|
|
47
|
+
return unwrapResult(result);
|
|
48
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin update endpoint
|
|
3
|
+
*
|
|
4
|
+
* POST /_dineway/api/admin/plugins/:id/update - Update a marketplace plugin
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { requirePerm } from "#api/authorize.js";
|
|
11
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
12
|
+
import { handleMarketplaceUpdate } from "#api/index.js";
|
|
13
|
+
import { isParseError, parseOptionalBody } from "#api/parse.js";
|
|
14
|
+
|
|
15
|
+
export const prerender = false;
|
|
16
|
+
|
|
17
|
+
const updateBodySchema = z.object({
|
|
18
|
+
version: z.string().min(1).optional(),
|
|
19
|
+
confirmCapabilityChanges: z.boolean().optional(),
|
|
20
|
+
confirmRouteVisibilityChanges: z.boolean().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
24
|
+
const { dineway, user } = locals;
|
|
25
|
+
const { id } = params;
|
|
26
|
+
|
|
27
|
+
if (!dineway?.db) {
|
|
28
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const denied = requirePerm(user, "plugins:manage");
|
|
32
|
+
if (denied) return denied;
|
|
33
|
+
|
|
34
|
+
if (!id) {
|
|
35
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const body = await parseOptionalBody(request, updateBodySchema, {});
|
|
39
|
+
if (isParseError(body)) return body;
|
|
40
|
+
|
|
41
|
+
const result = await handleMarketplaceUpdate(
|
|
42
|
+
dineway.db,
|
|
43
|
+
dineway.storage,
|
|
44
|
+
dineway.getSandboxRunner(),
|
|
45
|
+
dineway.config.marketplace,
|
|
46
|
+
id,
|
|
47
|
+
{
|
|
48
|
+
version: body.version,
|
|
49
|
+
confirmCapabilityChanges: body.confirmCapabilityChanges,
|
|
50
|
+
confirmRouteVisibilityChanges: body.confirmRouteVisibilityChanges,
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (!result.success) return unwrapResult(result);
|
|
55
|
+
|
|
56
|
+
await dineway.syncMarketplacePlugins();
|
|
57
|
+
|
|
58
|
+
return unwrapResult(result);
|
|
59
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin management list endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins - List all configured plugins with state
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handlePluginList } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const denied = requirePerm(user, "plugins:read");
|
|
23
|
+
if (denied) return denied;
|
|
24
|
+
|
|
25
|
+
const result = await handlePluginList(
|
|
26
|
+
dineway.db,
|
|
27
|
+
dineway.configuredPlugins,
|
|
28
|
+
dineway.config.marketplace,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return unwrapResult(result);
|
|
32
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin icon proxy
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins/marketplace/:id/icon - Proxy icon from marketplace
|
|
5
|
+
*
|
|
6
|
+
* Avoids CORS/auth issues when the marketplace service is behind
|
|
7
|
+
* external auth or on a different origin.
|
|
8
|
+
* The admin UI uses this instead of linking directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { APIRoute } from "astro";
|
|
12
|
+
|
|
13
|
+
import { requirePerm } from "#api/authorize.js";
|
|
14
|
+
import { apiError } from "#api/error.js";
|
|
15
|
+
|
|
16
|
+
export const prerender = false;
|
|
17
|
+
|
|
18
|
+
export const GET: APIRoute = async ({ params, url, locals }) => {
|
|
19
|
+
const { dineway, user } = locals;
|
|
20
|
+
const { id } = params;
|
|
21
|
+
|
|
22
|
+
if (!dineway?.db) {
|
|
23
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const denied = requirePerm(user, "plugins:read");
|
|
27
|
+
if (denied) return denied;
|
|
28
|
+
|
|
29
|
+
const marketplaceUrl = dineway.config.marketplace;
|
|
30
|
+
if (!marketplaceUrl || !id) {
|
|
31
|
+
return apiError("NOT_CONFIGURED", "Marketplace not configured", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const width = url.searchParams.get("w");
|
|
35
|
+
const target = new URL(`/api/v1/plugins/${encodeURIComponent(id)}/icon`, marketplaceUrl);
|
|
36
|
+
if (width) target.searchParams.set("w", width);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const resp = await fetch(target.href);
|
|
40
|
+
if (!resp.ok) {
|
|
41
|
+
// Allowlist: only forward Content-Type from upstream.
|
|
42
|
+
// Never copy all upstream headers (denylist approach leaks
|
|
43
|
+
// headers we haven't anticipated).
|
|
44
|
+
return new Response(resp.body, {
|
|
45
|
+
status: resp.status,
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": resp.headers.get("Content-Type") ?? "application/octet-stream",
|
|
48
|
+
"Cache-Control": "private, no-store",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return new Response(resp.body, {
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": resp.headers.get("Content-Type") ?? "image/png",
|
|
56
|
+
"Cache-Control": "private, no-store",
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
return apiError("PROXY_ERROR", "Failed to fetch icon", 502);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin detail proxy endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins/marketplace/:id - Get plugin details
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handleMarketplaceGetPlugin } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ params, locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
const { id } = params;
|
|
18
|
+
|
|
19
|
+
if (!dineway?.db) {
|
|
20
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const denied = requirePerm(user, "plugins:read");
|
|
24
|
+
if (denied) return denied;
|
|
25
|
+
|
|
26
|
+
if (!id) {
|
|
27
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = await handleMarketplaceGetPlugin(dineway.config.marketplace, id);
|
|
31
|
+
|
|
32
|
+
return unwrapResult(result);
|
|
33
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin install endpoint
|
|
3
|
+
*
|
|
4
|
+
* POST /_dineway/api/admin/plugins/marketplace/:id/install - Install a marketplace plugin
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { requirePerm } from "#api/authorize.js";
|
|
11
|
+
import { apiError, handleError, unwrapResult } from "#api/error.js";
|
|
12
|
+
import { handleMarketplaceInstall } from "#api/index.js";
|
|
13
|
+
import { isParseError, parseOptionalBody } from "#api/parse.js";
|
|
14
|
+
|
|
15
|
+
export const prerender = false;
|
|
16
|
+
|
|
17
|
+
const installBodySchema = z.object({
|
|
18
|
+
version: z.string().min(1).optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
22
|
+
try {
|
|
23
|
+
const { dineway, user } = locals;
|
|
24
|
+
const { id } = params;
|
|
25
|
+
|
|
26
|
+
if (!dineway?.db) {
|
|
27
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const denied = requirePerm(user, "plugins:manage");
|
|
31
|
+
if (denied) return denied;
|
|
32
|
+
|
|
33
|
+
if (!id) {
|
|
34
|
+
return apiError("INVALID_REQUEST", "Plugin ID required", 400);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const body = await parseOptionalBody(request, installBodySchema, {});
|
|
38
|
+
if (isParseError(body)) return body;
|
|
39
|
+
|
|
40
|
+
const configuredPluginIds = new Set<string>(
|
|
41
|
+
dineway.configuredPlugins.map((p: { id: string }) => p.id),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const siteOrigin = new URL(request.url).origin;
|
|
45
|
+
|
|
46
|
+
const result = await handleMarketplaceInstall(
|
|
47
|
+
dineway.db,
|
|
48
|
+
dineway.storage,
|
|
49
|
+
dineway.getSandboxRunner(),
|
|
50
|
+
dineway.config.marketplace,
|
|
51
|
+
id,
|
|
52
|
+
{ version: body.version, configuredPluginIds, siteOrigin },
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!result.success) return unwrapResult(result);
|
|
56
|
+
|
|
57
|
+
await dineway.syncMarketplacePlugins();
|
|
58
|
+
|
|
59
|
+
return unwrapResult(result, 201);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("[marketplace-install] Unhandled error:", error);
|
|
62
|
+
return handleError(error, "Failed to install plugin from marketplace", "INSTALL_FAILED");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace search proxy endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins/marketplace - Search marketplace plugins
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handleMarketplaceSearch } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ url, locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const denied = requirePerm(user, "plugins:read");
|
|
23
|
+
if (denied) return denied;
|
|
24
|
+
|
|
25
|
+
const query = url.searchParams.get("q") ?? undefined;
|
|
26
|
+
const category = url.searchParams.get("category") ?? undefined;
|
|
27
|
+
const cursor = url.searchParams.get("cursor") ?? undefined;
|
|
28
|
+
const limitParam = url.searchParams.get("limit");
|
|
29
|
+
const limit = limitParam ? Math.min(Math.max(1, parseInt(limitParam, 10) || 50), 100) : undefined;
|
|
30
|
+
|
|
31
|
+
const result = await handleMarketplaceSearch(dineway.config.marketplace, query, {
|
|
32
|
+
category,
|
|
33
|
+
cursor,
|
|
34
|
+
limit,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return unwrapResult(result);
|
|
38
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace update check endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/plugins/updates - Check for marketplace plugin updates
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handleMarketplaceUpdateCheck } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const denied = requirePerm(user, "plugins:read");
|
|
23
|
+
if (denied) return denied;
|
|
24
|
+
|
|
25
|
+
const result = await handleMarketplaceUpdateCheck(dineway.db, dineway.config.marketplace);
|
|
26
|
+
|
|
27
|
+
return unwrapResult(result);
|
|
28
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme marketplace detail proxy endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/themes/marketplace/:id - Get theme details
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handleThemeGetDetail } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ params, locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
const { id } = params;
|
|
18
|
+
|
|
19
|
+
if (!dineway?.db) {
|
|
20
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const denied = requirePerm(user, "plugins:read");
|
|
24
|
+
if (denied) return denied;
|
|
25
|
+
|
|
26
|
+
if (!id) {
|
|
27
|
+
return apiError("INVALID_REQUEST", "Theme ID required", 400);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = await handleThemeGetDetail(dineway.config.marketplace, id);
|
|
31
|
+
|
|
32
|
+
return unwrapResult(result);
|
|
33
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme marketplace thumbnail proxy
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/themes/marketplace/:id/thumbnail - Proxy thumbnail from marketplace
|
|
5
|
+
*
|
|
6
|
+
* Avoids CORS/auth issues when the marketplace service is behind
|
|
7
|
+
* external auth or on a different origin.
|
|
8
|
+
* The admin UI uses this instead of linking directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { APIRoute } from "astro";
|
|
12
|
+
|
|
13
|
+
import { requirePerm } from "#api/authorize.js";
|
|
14
|
+
import { apiError } from "#api/error.js";
|
|
15
|
+
|
|
16
|
+
export const prerender = false;
|
|
17
|
+
|
|
18
|
+
export const GET: APIRoute = async ({ params, url, locals }) => {
|
|
19
|
+
const { dineway, user } = locals;
|
|
20
|
+
const { id } = params;
|
|
21
|
+
|
|
22
|
+
if (!dineway?.db) {
|
|
23
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const denied = requirePerm(user, "plugins:read");
|
|
27
|
+
if (denied) return denied;
|
|
28
|
+
|
|
29
|
+
const marketplaceUrl = dineway.config.marketplace;
|
|
30
|
+
if (!marketplaceUrl || !id) {
|
|
31
|
+
return apiError("NOT_CONFIGURED", "Marketplace not configured", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const width = url.searchParams.get("w");
|
|
35
|
+
const target = new URL(`/api/v1/themes/${encodeURIComponent(id)}/thumbnail`, marketplaceUrl);
|
|
36
|
+
if (width) target.searchParams.set("w", width);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const resp = await fetch(target.href);
|
|
40
|
+
if (!resp.ok) {
|
|
41
|
+
// Allowlist: only forward Content-Type from upstream.
|
|
42
|
+
// Never copy all upstream headers (denylist approach leaks
|
|
43
|
+
// headers we haven't anticipated).
|
|
44
|
+
return new Response(resp.body, {
|
|
45
|
+
status: resp.status,
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": resp.headers.get("Content-Type") ?? "application/octet-stream",
|
|
48
|
+
"Cache-Control": "private, no-store",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return new Response(resp.body, {
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": resp.headers.get("Content-Type") ?? "image/png",
|
|
56
|
+
"Cache-Control": "private, no-store",
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
return apiError("PROXY_ERROR", "Failed to fetch thumbnail", 502);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme marketplace search proxy endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/admin/themes/marketplace - Search marketplace themes
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { APIRoute } from "astro";
|
|
8
|
+
|
|
9
|
+
import { requirePerm } from "#api/authorize.js";
|
|
10
|
+
import { apiError, unwrapResult } from "#api/error.js";
|
|
11
|
+
import { handleThemeSearch } from "#api/index.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ url, locals }) => {
|
|
16
|
+
const { dineway, user } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const denied = requirePerm(user, "plugins:read");
|
|
23
|
+
if (denied) return denied;
|
|
24
|
+
|
|
25
|
+
const query = url.searchParams.get("q") ?? undefined;
|
|
26
|
+
const keyword = url.searchParams.get("keyword") ?? undefined;
|
|
27
|
+
const sortParam = url.searchParams.get("sort");
|
|
28
|
+
const validSorts = new Set(["name", "created", "updated"]);
|
|
29
|
+
let sort: "name" | "created" | "updated" | undefined;
|
|
30
|
+
if (sortParam && validSorts.has(sortParam)) {
|
|
31
|
+
sort = sortParam as "name" | "created" | "updated"; // eslint-disable-line typescript-eslint(no-unsafe-type-assertion) -- validated by Set.has()
|
|
32
|
+
}
|
|
33
|
+
const cursor = url.searchParams.get("cursor") ?? undefined;
|
|
34
|
+
const limitParam = url.searchParams.get("limit");
|
|
35
|
+
const limit = limitParam ? Math.min(Math.max(1, parseInt(limitParam, 10) || 50), 100) : undefined;
|
|
36
|
+
|
|
37
|
+
const result = await handleThemeSearch(dineway.config.marketplace, query, {
|
|
38
|
+
keyword,
|
|
39
|
+
sort,
|
|
40
|
+
cursor,
|
|
41
|
+
limit,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return unwrapResult(result);
|
|
45
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User disable endpoint
|
|
3
|
+
*
|
|
4
|
+
* POST /_dineway/api/admin/users/:id/disable - Soft-disable a user
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Role } from "@dineway-ai/auth";
|
|
8
|
+
import { createKyselyAdapter } from "@dineway-ai/auth/adapters/kysely";
|
|
9
|
+
import type { APIRoute } from "astro";
|
|
10
|
+
|
|
11
|
+
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
12
|
+
|
|
13
|
+
export const prerender = false;
|
|
14
|
+
|
|
15
|
+
export const POST: APIRoute = async ({ params, locals }) => {
|
|
16
|
+
const { dineway, user: currentUser } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Database not configured", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!currentUser || currentUser.role < Role.ADMIN) {
|
|
23
|
+
return apiError("FORBIDDEN", "Admin privileges required", 403);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const adapter = createKyselyAdapter(dineway.db);
|
|
27
|
+
|
|
28
|
+
const { id } = params;
|
|
29
|
+
|
|
30
|
+
if (!id) {
|
|
31
|
+
return apiError("VALIDATION_ERROR", "User ID required", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Prevent disabling self
|
|
35
|
+
if (id === currentUser.id) {
|
|
36
|
+
return apiError("VALIDATION_ERROR", "Cannot disable your own account", 400);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Get target user
|
|
41
|
+
const targetUser = await adapter.getUserById(id);
|
|
42
|
+
if (!targetUser) {
|
|
43
|
+
return apiError("NOT_FOUND", "User not found", 404);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check if this would leave no active admins
|
|
47
|
+
if (targetUser.role === Role.ADMIN) {
|
|
48
|
+
const adminCount = await adapter.countAdmins();
|
|
49
|
+
if (adminCount <= 1) {
|
|
50
|
+
return apiError(
|
|
51
|
+
"VALIDATION_ERROR",
|
|
52
|
+
"Cannot disable the last admin. Promote another user first.",
|
|
53
|
+
400,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Disable user
|
|
59
|
+
await adapter.updateUser(id, { disabled: true });
|
|
60
|
+
|
|
61
|
+
// SEC-43: Revoke all OAuth tokens for the disabled user.
|
|
62
|
+
// Without this, existing refresh tokens remain valid for up to 90 days.
|
|
63
|
+
await dineway.db.deleteFrom("_dineway_oauth_tokens").where("user_id", "=", id).execute();
|
|
64
|
+
|
|
65
|
+
return apiSuccess({ success: true });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return handleError(error, "Failed to disable user", "USER_DISABLE_ERROR");
|
|
68
|
+
}
|
|
69
|
+
};
|