Implement cross-domain support for OAuth and API requests
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Updated frontend to use `https://cms.gallus-pub.ch` as the API base URL. - Configured cookies with `SameSite=None` and `Secure` for production in `auth.ts`. - Enhanced `fly.toml` to include `FRONTEND_URL`, `CORS_ORIGIN`, and `GITEA_REDIRECT_URI`. - Adjusted `.gitignore` to ignore `/ai/` directory.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ pnpm-debug.log*
|
|||||||
|
|
||||||
# jetbrains setting folder
|
# jetbrains setting folder
|
||||||
.idea/
|
.idea/
|
||||||
|
/ai/
|
||||||
|
|||||||
@ -10,6 +10,10 @@ primary_region = "ams"
|
|||||||
GITEA_URL = "https://git.bookageek.ch"
|
GITEA_URL = "https://git.bookageek.ch"
|
||||||
DATABASE_PATH = "/app/data/gallus_cms.db"
|
DATABASE_PATH = "/app/data/gallus_cms.db"
|
||||||
GIT_WORKSPACE_DIR = "/app/data/workspace"
|
GIT_WORKSPACE_DIR = "/app/data/workspace"
|
||||||
|
# Cross-site frontend and OAuth
|
||||||
|
FRONTEND_URL = "https://gallus-pub.ch"
|
||||||
|
CORS_ORIGIN = "https://gallus-pub.ch"
|
||||||
|
GITEA_REDIRECT_URI = "https://cms.gallus-pub.ch/api/auth/callback"
|
||||||
|
|
||||||
[http_service]
|
[http_service]
|
||||||
internal_port = 8080
|
internal_port = 8080
|
||||||
|
|||||||
@ -122,11 +122,12 @@ const authRoute: FastifyPluginAsync = async (fastify) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Also set token as HttpOnly cookie so subsequent API calls authenticate reliably
|
// Also set token as HttpOnly cookie so subsequent API calls authenticate reliably
|
||||||
|
// Cross-site admin (gallus-pub.ch) -> backend (cms.gallus-pub.ch) requires SameSite=None & Secure in production
|
||||||
reply.setCookie('token', token, {
|
reply.setCookie('token', token, {
|
||||||
path: '/',
|
path: '/',
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
sameSite: 'lax',
|
sameSite: (env.NODE_ENV === 'production' ? 'none' : 'lax'),
|
||||||
secure: !!env.FRONTEND_URL && env.FRONTEND_URL.startsWith('https'),
|
secure: (env.NODE_ENV === 'production') || (!!env.FRONTEND_URL && env.FRONTEND_URL.startsWith('https')),
|
||||||
maxAge: 60 * 60 * 24, // 24h
|
maxAge: 60 * 60 * 24, // 24h
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const title = 'Admin';
|
|||||||
<h2>Authentifizierung</h2>
|
<h2>Authentifizierung</h2>
|
||||||
<div id="auth-status" class="muted">Prüfe Anmeldestatus...</div>
|
<div id="auth-status" class="muted">Prüfe Anmeldestatus...</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a id="login-link" class="btn" href="/api/auth/gitea">Mit Gitea anmelden</a>
|
<a id="login-link" class="btn" href="https://cms.gallus-pub.ch/api/auth/gitea">Mit Gitea anmelden</a>
|
||||||
<button id="btn-relogin">Neu anmelden</button>
|
<button id="btn-relogin">Neu anmelden</button>
|
||||||
<button id="btn-logout">Abmelden</button>
|
<button id="btn-logout">Abmelden</button>
|
||||||
</div>
|
</div>
|
||||||
@ -76,8 +76,11 @@ const title = 'Admin';
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Base-URL des Backends (separate Subdomain)
|
||||||
|
const API_BASE = 'https://cms.gallus-pub.ch';
|
||||||
|
|
||||||
const api = async (path, opts = {}) => {
|
const api = async (path, opts = {}) => {
|
||||||
const res = await fetch(path, { credentials: 'include', ...opts });
|
const res = await fetch(API_BASE + path, { credentials: 'include', ...opts });
|
||||||
if (!res.ok) throw new Error(await res.text());
|
if (!res.ok) throw new Error(await res.text());
|
||||||
const ct = res.headers.get('content-type') || '';
|
const ct = res.headers.get('content-type') || '';
|
||||||
return ct.includes('application/json') ? res.json() : res.text();
|
return ct.includes('application/json') ? res.json() : res.text();
|
||||||
@ -107,13 +110,13 @@ const title = 'Admin';
|
|||||||
loginLink.addEventListener('click', (e) => {
|
loginLink.addEventListener('click', (e) => {
|
||||||
try {
|
try {
|
||||||
// Stelle sicher, dass Navigieren erzwungen wird
|
// Stelle sicher, dass Navigieren erzwungen wird
|
||||||
window.location.assign('/api/auth/gitea');
|
window.location.assign(API_BASE + '/api/auth/gitea');
|
||||||
} catch {}
|
} catch {}
|
||||||
});
|
});
|
||||||
document.getElementById('btn-relogin').addEventListener('click', async () => {
|
document.getElementById('btn-relogin').addEventListener('click', async () => {
|
||||||
try { await api('/api/auth/logout', { method: 'POST' }); } catch {}
|
try { await api('/api/auth/logout', { method: 'POST' }); } catch {}
|
||||||
document.cookie = 'token=; Path=/; Max-Age=0;';
|
document.cookie = 'token=; Path=/; Max-Age=0;';
|
||||||
window.location.assign('/api/auth/gitea');
|
window.location.assign(API_BASE + '/api/auth/gitea');
|
||||||
});
|
});
|
||||||
document.getElementById('btn-logout').addEventListener('click', async () => {
|
document.getElementById('btn-logout').addEventListener('click', async () => {
|
||||||
try { await api('/api/auth/logout', { method: 'POST' }); } catch {}
|
try { await api('/api/auth/logout', { method: 'POST' }); } catch {}
|
||||||
@ -127,7 +130,7 @@ const title = 'Admin';
|
|||||||
fd.append('file', file);
|
fd.append('file', file);
|
||||||
if (altText) fd.append('altText', altText);
|
if (altText) fd.append('altText', altText);
|
||||||
fd.append('displayOrder', '0');
|
fd.append('displayOrder', '0');
|
||||||
const res = await fetch('/api/gallery/upload', { method: 'POST', body: fd, credentials: 'include' });
|
const res = await fetch(API_BASE + '/api/gallery/upload', { method: 'POST', body: fd, credentials: 'include' });
|
||||||
if (!res.ok) throw new Error(await res.text());
|
if (!res.ok) throw new Error(await res.text());
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user