add gh auth config for decap
This commit is contained in:
76
docs/cloudflare-workers-astro-v6.md
Normal file
76
docs/cloudflare-workers-astro-v6.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# Cloudflare Workers + Astro v6 Deployment Notes
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
- Astro v6 + `@astrojs/cloudflare` adapter (v13+)
|
||||||
|
- Deployed as **Cloudflare Worker** (not Pages)
|
||||||
|
- Build: `npm run build` → Deploy: `npx wrangler deploy`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issue 1 — `wrangler.json` with `pages_build_output_dir` breaks deploy
|
||||||
|
|
||||||
|
**Error:**
|
||||||
|
```
|
||||||
|
The name 'ASSETS' is reserved in Pages projects. Please use a different name for your Assets binding.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause:** Having `pages_build_output_dir` in `wrangler.json` makes Wrangler treat the project as a Pages project. The adapter generates `dist/server/wrangler.json` with an `ASSETS` binding for static assets, which is reserved in Pages context.
|
||||||
|
|
||||||
|
**Fix:** Remove `pages_build_output_dir` from `wrangler.json`, or delete the file entirely. The adapter generates a complete `dist/server/wrangler.json` — Wrangler picks it up automatically via config redirect.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issue 2 — `locals.runtime.env` removed in Astro v6
|
||||||
|
|
||||||
|
**Error:**
|
||||||
|
```
|
||||||
|
Astro.locals.runtime.env has been removed in Astro v6.
|
||||||
|
Use 'import { env } from "cloudflare:workers"' instead.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause:** Astro v6 dropped the `locals.runtime.env` pattern for accessing Cloudflare env vars/bindings.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// ❌ Astro v5 (broken in v6)
|
||||||
|
const runtime = (locals as any).runtime;
|
||||||
|
const apiKey = runtime?.env?.RESEND_API_KEY;
|
||||||
|
|
||||||
|
// ✅ Astro v6
|
||||||
|
import { env } from 'cloudflare:workers';
|
||||||
|
const apiKey = (env as unknown as Record<string, string>).RESEND_API_KEY;
|
||||||
|
```
|
||||||
|
|
||||||
|
Install types and add to `tsconfig.json`:
|
||||||
|
```bash
|
||||||
|
npm install --save-dev @cloudflare/workers-types
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
// tsconfig.json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["@cloudflare/workers-types"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Env vars in Cloudflare Workers dashboard
|
||||||
|
|
||||||
|
- Set vars under **Worker → Settings → Variables and Secrets**
|
||||||
|
- They are accessible via `env` from `cloudflare:workers` at runtime
|
||||||
|
- Do **not** rely on `process.env` or `import.meta.env` for server-side secrets in Workers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wrangler config redirect (how it works)
|
||||||
|
|
||||||
|
When `astro build` runs, `@astrojs/cloudflare` generates `dist/server/wrangler.json`. Wrangler detects this and logs:
|
||||||
|
```
|
||||||
|
Using redirected Wrangler configuration.
|
||||||
|
- Configuration being used: "dist/server/wrangler.json"
|
||||||
|
- Original user's configuration: "wrangler.json"
|
||||||
|
```
|
||||||
|
The generated config includes the worker `main` entry, `assets` binding, `compatibility_date`, and any adapter-detected bindings (e.g. `IMAGES`, `SESSION`).
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
export async function onRequest(context: any) {
|
|
||||||
const {
|
|
||||||
request, // same as existing Worker API
|
|
||||||
env, // same as existing Worker API
|
|
||||||
} = context;
|
|
||||||
|
|
||||||
const client_id = (env as any).GITHUB_CLIENT_ID;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const redirectUrl = new URL('https://github.com/login/oauth/authorize');
|
|
||||||
redirectUrl.searchParams.set('client_id', client_id);
|
|
||||||
redirectUrl.searchParams.set('redirect_uri', url.origin + '/api/callback');
|
|
||||||
redirectUrl.searchParams.set('scope', 'repo user');
|
|
||||||
redirectUrl.searchParams.set(
|
|
||||||
'state',
|
|
||||||
crypto.getRandomValues(new Uint8Array(12)).join(''),
|
|
||||||
);
|
|
||||||
return Response.redirect(redirectUrl.href, 301);
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
return new Response(error.message, {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
function renderBody(status: string, content: any) {
|
|
||||||
const html = `
|
|
||||||
<script>
|
|
||||||
const receiveMessage = (message) => {
|
|
||||||
window.opener.postMessage(
|
|
||||||
'authorization:github:${status}:${JSON.stringify(content)}',
|
|
||||||
message.origin
|
|
||||||
);
|
|
||||||
window.removeEventListener("message", receiveMessage, false);
|
|
||||||
}
|
|
||||||
window.addEventListener("message", receiveMessage, false);
|
|
||||||
window.opener.postMessage("authorizing:github", "*");
|
|
||||||
</script>
|
|
||||||
`;
|
|
||||||
const blob = new Blob([html]);
|
|
||||||
return blob;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function onRequest(context: any) {
|
|
||||||
const {
|
|
||||||
request, // same as existing Worker API
|
|
||||||
env, // same as existing Worker API
|
|
||||||
} = context;
|
|
||||||
|
|
||||||
const client_id = env.GITHUB_CLIENT_ID;
|
|
||||||
const client_secret = env.GITHUB_CLIENT_SECRET;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const code = url.searchParams.get('code');
|
|
||||||
const response = await fetch(
|
|
||||||
'https://github.com/login/oauth/access_token',
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
'user-agent': 'cloudflare-functions-github-oauth-login-demo',
|
|
||||||
'accept': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ client_id, client_secret, code }),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const result = (await response.json()) as any;
|
|
||||||
if (result.error) {
|
|
||||||
return new Response(renderBody('error', result), {
|
|
||||||
headers: {
|
|
||||||
'content-type': 'text/html;charset=UTF-8',
|
|
||||||
},
|
|
||||||
status: 401
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const token = result.access_token;
|
|
||||||
const provider = 'github';
|
|
||||||
const responseBody = renderBody('success', {
|
|
||||||
token,
|
|
||||||
provider,
|
|
||||||
});
|
|
||||||
return new Response(responseBody, {
|
|
||||||
headers: {
|
|
||||||
'content-type': 'text/html;charset=UTF-8',
|
|
||||||
},
|
|
||||||
status: 200
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
return new Response(error.message, {
|
|
||||||
headers: {
|
|
||||||
'content-type': 'text/html;charset=UTF-8',
|
|
||||||
},
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
backend:
|
backend:
|
||||||
name: github
|
name: github
|
||||||
repo: mbcube/cachetdeco # REPLACE with your GitHub org/repo
|
repo: mbcube/cachetdeco
|
||||||
branch: cloudflare
|
branch: cloudflare
|
||||||
site_domaine: https://cachetdeco.br-mouad.workers.dev/
|
base_url: https://cachetdeco.br-mouad.workers.dev
|
||||||
base_url: https://cachetdeco.br-mouad.workers.dev/
|
auth_endpoint: /api/auth
|
||||||
|
|
||||||
locale: fr
|
locale: fr
|
||||||
media_folder: "public/images"
|
media_folder: "public/images"
|
||||||
|
|||||||
20
src/pages/api/auth.ts
Normal file
20
src/pages/api/auth.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { env } from 'cloudflare:workers';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request }) => {
|
||||||
|
const clientId = (env as any).GITHUB_CLIENT_ID;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const redirectUrl = new URL('https://github.com/login/oauth/authorize');
|
||||||
|
redirectUrl.searchParams.set('client_id', clientId);
|
||||||
|
redirectUrl.searchParams.set('redirect_uri', url.origin + '/api/callback');
|
||||||
|
redirectUrl.searchParams.set('scope', 'repo user');
|
||||||
|
redirectUrl.searchParams.set('state', crypto.getRandomValues(new Uint8Array(12)).join(''));
|
||||||
|
return Response.redirect(redirectUrl.href, 302);
|
||||||
|
} catch (error: any) {
|
||||||
|
return new Response(error.message, { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
||||||
57
src/pages/api/callback.ts
Normal file
57
src/pages/api/callback.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { env } from 'cloudflare:workers';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
function renderBody(status: string, content: unknown): string {
|
||||||
|
return `<script>
|
||||||
|
const receiveMessage = (message) => {
|
||||||
|
window.opener.postMessage(
|
||||||
|
'authorization:github:${status}:${JSON.stringify(content)}',
|
||||||
|
message.origin
|
||||||
|
);
|
||||||
|
window.removeEventListener("message", receiveMessage, false);
|
||||||
|
}
|
||||||
|
window.addEventListener("message", receiveMessage, false);
|
||||||
|
window.opener.postMessage("authorizing:github", "*");
|
||||||
|
</script>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request }) => {
|
||||||
|
const clientId = (env as any).GITHUB_CLIENT_ID;
|
||||||
|
const clientSecret = (env as any).GITHUB_CLIENT_SECRET;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const code = url.searchParams.get('code');
|
||||||
|
|
||||||
|
const response = await fetch('https://github.com/login/oauth/access_token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
'user-agent': 'cachetdeco-github-oauth',
|
||||||
|
'accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ client_id: clientId, client_secret: clientSecret, code }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = (await response.json()) as any;
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
return new Response(renderBody('error', result), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(renderBody('success', { token: result.access_token, provider: 'github' }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
return new Response(error.message, {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user