nca-ai-cms-astro-plugin 1.1.5 → 1.1.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nca-ai-cms-astro-plugin",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -19,13 +19,6 @@ const CreateArticleSchema = z.object({
19
19
  // POST /api/articles/create - Generate content + image and save in one call
20
20
  export const POST: APIRoute = async ({ request }) => {
21
21
  try {
22
- // CSRF: reject cross-origin requests
23
- const origin = request.headers.get('origin');
24
- const requestUrl = new URL(request.url);
25
- if (origin && new URL(origin).origin !== requestUrl.origin) {
26
- return jsonError('Forbidden', 403);
27
- }
28
-
29
22
  let body: unknown;
30
23
  try {
31
24
  body = await request.json();
@@ -1,4 +1,5 @@
1
1
  import type { APIContext } from 'astro';
2
+ import { getPublicOrigin } from '../utils/originUtils.js';
2
3
 
3
4
  export function generateRobotsTxt(siteUrl: string): string {
4
5
  const base = siteUrl.replace(/\/+$/, '');
@@ -14,7 +15,7 @@ Sitemap: ${base}/sitemap.xml
14
15
  }
15
16
 
16
17
  export function GET(context: APIContext): Response {
17
- const siteUrl = context.site?.toString() ?? context.url.origin;
18
+ const siteUrl = getPublicOrigin(context);
18
19
  const body = generateRobotsTxt(siteUrl);
19
20
 
20
21
  return new Response(body, {
@@ -1,6 +1,7 @@
1
1
  import type { APIContext } from 'astro';
2
2
  import { ArticleService } from '../services/ArticleService';
3
3
  import type { ArticleData } from '../services/ArticleService';
4
+ import { getPublicOrigin } from '../utils/originUtils.js';
4
5
 
5
6
  const STATIC_PAGES = ['/', '/impressum', '/ueber-ai-cms'];
6
7
 
@@ -43,7 +44,7 @@ ${[...staticEntries, ...articleEntries].join('\n')}
43
44
  }
44
45
 
45
46
  export async function GET(context: APIContext): Promise<Response> {
46
- const siteUrl = context.site?.toString() ?? context.url.origin;
47
+ const siteUrl = getPublicOrigin(context);
47
48
  const service = new ArticleService();
48
49
  const articles = await service.list();
49
50
 
@@ -0,0 +1,15 @@
1
+ import type { APIContext } from 'astro';
2
+
3
+ export function getPublicOrigin(context: APIContext): string {
4
+ if (context.site) {
5
+ return context.site.toString().replace(/\/+$/, '');
6
+ }
7
+
8
+ const proto = context.request.headers.get('x-forwarded-proto');
9
+ const host = context.request.headers.get('x-forwarded-host');
10
+ if (proto && host) {
11
+ return `${proto}://${host}`;
12
+ }
13
+
14
+ return context.url.origin;
15
+ }
package/update.md CHANGED
@@ -1,3 +1,23 @@
1
+ # v1.1.6
2
+
3
+ ## Fix: reverse proxy compatibility
4
+
5
+ ### Removed: broken CSRF check in `/api/articles/create`
6
+ - The origin comparison (`request.headers.get('origin')` vs `request.url`) fails behind reverse proxies (Traefik/Nginx) because `request.url` resolves to the internal container URL
7
+ - Route is already protected by auth middleware — CSRF check was redundant
8
+ - Fixes 403 Forbidden when generating articles in production
9
+
10
+ ### New: `getPublicOrigin()` utility
11
+ - Resolves the correct external URL behind reverse proxies
12
+ - Priority: `context.site` → `X-Forwarded-Proto`/`X-Forwarded-Host` → `context.url.origin`
13
+ - Nginx already sets these headers on all proxy locations
14
+
15
+ ### Fixed: robots.txt and sitemap.xml URLs
16
+ - Both files used `context.url.origin` which returned `http://127.0.0.1:4321` behind proxy
17
+ - Now use `getPublicOrigin()` to produce correct external URLs
18
+
19
+ ---
20
+
1
21
  # v1.1.5
2
22
 
3
23
  ## Refactor: Settings import/export with merge semantics