mirror of
https://github.com/PeterMaquiran/tvone.git
synced 2026-04-23 12:35:51 +00:00
move to environment variable
This commit is contained in:
@@ -1,27 +1,28 @@
|
|||||||
import { getCookieDomain } from "@/lib/getDomain";
|
import { getCookieDomain } from "@/lib/getDomain";
|
||||||
|
import { env } from "@/lib/env";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
const BASE_URL = env.APP_URL;
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const code = url.searchParams.get("code");
|
const code = url.searchParams.get("code");
|
||||||
const origin = url.origin;
|
|
||||||
const isHttps = url.protocol === "https:";
|
const isHttps = url.protocol === "https:";
|
||||||
const domain = getCookieDomain(url.hostname); // ← domain only
|
const domain = env.COOKIE_DOMAIN ?? getCookieDomain(url.hostname);
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return NextResponse.redirect(`${origin}/login?error=missing_code`);
|
return NextResponse.redirect(`${BASE_URL}/login?error=missing_code`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirectUri = `${origin}/api/auth/callback`;
|
const redirectUri = `${origin}/api/auth/callback`;
|
||||||
|
|
||||||
const tokenRes = await fetch(
|
const tokenRes = await fetch(
|
||||||
"https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/token",
|
`${env.KEYCLOAK_BASE_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect/token`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
client_id: "tvone-web",
|
client_id: env.KEYCLOAK_CLIENT_ID,
|
||||||
client_secret: "7jQUciQCCf2WRFRe170UANKzGKVWFIkY",
|
client_secret: env.KEYCLOAK_CLIENT_SECRET,
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code,
|
code,
|
||||||
redirect_uri: redirectUri,
|
redirect_uri: redirectUri,
|
||||||
@@ -34,15 +35,15 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
data = JSON.parse(text) as typeof data;
|
data = JSON.parse(text) as typeof data;
|
||||||
} catch {
|
} catch {
|
||||||
return NextResponse.redirect(`${origin}/login?error=token_parse`);
|
return NextResponse.redirect(`${BASE_URL}/login?error=token_parse`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tokenRes.ok || !data.access_token) {
|
if (!tokenRes.ok || !data.access_token) {
|
||||||
console.error("token exchange failed", tokenRes.status, text);
|
console.error("token exchange failed", tokenRes.status, text);
|
||||||
return NextResponse.redirect(`${origin}/login?error=token_exchange`);
|
return NextResponse.redirect(`${BASE_URL}/login?error=token_exchange`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = NextResponse.redirect(`${origin}/admin/create-news`);
|
const res = NextResponse.redirect(`${BASE_URL}/admin/create-news`);
|
||||||
|
|
||||||
// Secure cookies are ignored on http:// (e.g. localhost) — browser drops them.
|
// Secure cookies are ignored on http:// (e.g. localhost) — browser drops them.
|
||||||
res.cookies.set("access_token", data.access_token, {
|
res.cookies.set("access_token", data.access_token, {
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
import { env } from "@/lib/env";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
const BASE_URL = env.APP_URL;
|
||||||
const url = new URL(req.url);
|
|
||||||
const origin = url.origin;
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
const redirect = encodeURIComponent(
|
const redirect = encodeURIComponent(
|
||||||
`${origin}/api/auth/callback`
|
`${BASE_URL}/api/auth/callback`
|
||||||
);
|
);
|
||||||
|
|
||||||
const keycloakUrl =
|
const keycloakUrl =
|
||||||
`https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/auth` +
|
`${env.KEYCLOAK_BASE_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect/auth` +
|
||||||
`?client_id=tvone-web` +
|
`?client_id=${encodeURIComponent(env.KEYCLOAK_CLIENT_ID)}` +
|
||||||
`&response_type=code` +
|
`&response_type=code` +
|
||||||
`&scope=openid` +
|
`&scope=openid` +
|
||||||
`&redirect_uri=${redirect}`;
|
`&redirect_uri=${redirect}`;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { env } from "@/lib/env";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
|
const isHttps = new URL(req.url).protocol === "https:";
|
||||||
const cookie = req.headers.get("cookie");
|
const cookie = req.headers.get("cookie");
|
||||||
|
|
||||||
const refreshToken = cookie
|
const refreshToken = cookie
|
||||||
@@ -13,8 +15,7 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Call your auth server (Keycloak or NestJS)
|
const res = await fetch(`${env.AUTH_API_URL}/auth/refresh`, {
|
||||||
const res = await fetch("http://api.example.com/auth/refresh", {
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -33,19 +34,19 @@ export async function GET(req: Request) {
|
|||||||
// 🍪 Set new access token
|
// 🍪 Set new access token
|
||||||
response.cookies.set("access_token", data.access_token, {
|
response.cookies.set("access_token", data.access_token, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: true,
|
secure: isHttps,
|
||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
path: "/",
|
path: "/",
|
||||||
domain: ".example.com",
|
...(env.COOKIE_DOMAIN ? { domain: env.COOKIE_DOMAIN } : {}),
|
||||||
maxAge: data.expires_in,
|
maxAge: data.expires_in,
|
||||||
});
|
});
|
||||||
|
|
||||||
response.cookies.set("refresh_token", data.refresh_token, {
|
response.cookies.set("refresh_token", data.refresh_token, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: true,
|
secure: isHttps,
|
||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
path: "/",
|
path: "/",
|
||||||
domain: ".example.com",
|
...(env.COOKIE_DOMAIN ? { domain: env.COOKIE_DOMAIN } : {}),
|
||||||
maxAge: data.expires_in,
|
maxAge: data.expires_in,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
import { loadEnvConfig } from "@next/env";
|
||||||
|
|
||||||
|
loadEnvConfig(process.cwd());
|
||||||
|
|
||||||
|
function getRequiredEnv(name: string): string {
|
||||||
|
const value = process.env[name];
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`Missing required environment variable: ${name}`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptionalEnv(name: string): string | undefined {
|
||||||
|
const value = process.env[name];
|
||||||
|
return value && value.trim().length > 0 ? value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env = {
|
||||||
|
APP_URL: getRequiredEnv("APP_URL"),
|
||||||
|
KEYCLOAK_BASE_URL: getRequiredEnv("KEYCLOAK_BASE_URL"),
|
||||||
|
KEYCLOAK_REALM: getRequiredEnv("KEYCLOAK_REALM"),
|
||||||
|
KEYCLOAK_CLIENT_ID: getRequiredEnv("KEYCLOAK_CLIENT_ID"),
|
||||||
|
KEYCLOAK_CLIENT_SECRET: getRequiredEnv("KEYCLOAK_CLIENT_SECRET"),
|
||||||
|
AUTH_API_URL: getRequiredEnv("AUTH_API_URL"),
|
||||||
|
COOKIE_DOMAIN: getOptionalEnv("COOKIE_DOMAIN"),
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
|
"@next/env": "^16.2.4",
|
||||||
"@react-oauth/google": "^0.13.5",
|
"@react-oauth/google": "^0.13.5",
|
||||||
"@tinymce/tinymce-react": "^6.3.0",
|
"@tinymce/tinymce-react": "^6.3.0",
|
||||||
"framer-motion": "^12.38.0",
|
"framer-motion": "^12.38.0",
|
||||||
|
|||||||
Generated
+8
@@ -17,6 +17,9 @@ importers:
|
|||||||
'@dnd-kit/utilities':
|
'@dnd-kit/utilities':
|
||||||
specifier: ^3.2.2
|
specifier: ^3.2.2
|
||||||
version: 3.2.2(react@19.2.4)
|
version: 3.2.2(react@19.2.4)
|
||||||
|
'@next/env':
|
||||||
|
specifier: ^16.2.4
|
||||||
|
version: 16.2.4
|
||||||
'@react-oauth/google':
|
'@react-oauth/google':
|
||||||
specifier: ^0.13.5
|
specifier: ^0.13.5
|
||||||
version: 0.13.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 0.13.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -415,6 +418,9 @@ packages:
|
|||||||
'@next/env@16.2.1':
|
'@next/env@16.2.1':
|
||||||
resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==}
|
resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==}
|
||||||
|
|
||||||
|
'@next/env@16.2.4':
|
||||||
|
resolution: {integrity: sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@16.2.1':
|
'@next/eslint-plugin-next@16.2.1':
|
||||||
resolution: {integrity: sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==}
|
resolution: {integrity: sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==}
|
||||||
|
|
||||||
@@ -2414,6 +2420,8 @@ snapshots:
|
|||||||
|
|
||||||
'@next/env@16.2.1': {}
|
'@next/env@16.2.1': {}
|
||||||
|
|
||||||
|
'@next/env@16.2.4': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@16.2.1':
|
'@next/eslint-plugin-next@16.2.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
|
|||||||
Reference in New Issue
Block a user