@olmocms/front 0.1.5 → 0.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/dist/auth/action.cjs +1 -2
- package/dist/auth/action.cjs.map +1 -1
- package/dist/auth/action.d.cts +1 -0
- package/dist/auth/action.d.ts +1 -0
- package/dist/auth/action.js +1 -2
- package/dist/auth/action.js.map +1 -1
- package/dist/{chunk-RAI2AUWN.js → chunk-UCR7WW3X.js} +2 -1
- package/dist/chunk-UCR7WW3X.js.map +1 -0
- package/dist/components/index.cjs +5 -0
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +7 -2
- package/dist/components/index.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.cjs +1 -0
- package/dist/middleware/index.cjs.map +1 -1
- package/dist/middleware/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-RAI2AUWN.js.map +0 -1
package/dist/auth/action.cjs
CHANGED
|
@@ -25,7 +25,6 @@ __export(action_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(action_exports);
|
|
27
27
|
var import_headers = require("next/headers");
|
|
28
|
-
var import_navigation = require("next/navigation");
|
|
29
28
|
|
|
30
29
|
// src/auth/stage.ts
|
|
31
30
|
var COOKIE_NAME = "olmo_stage_auth";
|
|
@@ -98,7 +97,7 @@ async function stageLoginAction(_prev, formData) {
|
|
|
98
97
|
path: "/",
|
|
99
98
|
expires: new Date(exp)
|
|
100
99
|
});
|
|
101
|
-
|
|
100
|
+
return { redirectTo: safeRedirectTarget(formData.get("from")) };
|
|
102
101
|
}
|
|
103
102
|
// Annotate the CommonJS export names for ESM import in node:
|
|
104
103
|
0 && (module.exports = {
|
package/dist/auth/action.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n // Return the target instead of calling redirect() here. The client-side\n // <StageLogin> reads this in a useEffect and calls router.replace, which\n // guarantees the Set-Cookie has been committed to the browser jar before\n // the next GET fires. This eliminates the cookie-commit vs redirect race\n // that surfaces on Vercel's edge runtime + CDN.\n return { redirectTo: safeRedirectTarget(formData.get('from')) };\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,qBAAwB;;;ACCxB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADtFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,UAAM,wBAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAOD,SAAO,EAAE,YAAY,mBAAmB,SAAS,IAAI,MAAM,CAAC,EAAE;AAChE;","names":[]}
|
package/dist/auth/action.d.cts
CHANGED
package/dist/auth/action.d.ts
CHANGED
package/dist/auth/action.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
// src/auth/action.ts
|
|
4
4
|
import { cookies } from "next/headers";
|
|
5
|
-
import { redirect } from "next/navigation";
|
|
6
5
|
|
|
7
6
|
// src/auth/stage.ts
|
|
8
7
|
var COOKIE_NAME = "olmo_stage_auth";
|
|
@@ -75,7 +74,7 @@ async function stageLoginAction(_prev, formData) {
|
|
|
75
74
|
path: "/",
|
|
76
75
|
expires: new Date(exp)
|
|
77
76
|
});
|
|
78
|
-
|
|
77
|
+
return { redirectTo: safeRedirectTarget(formData.get("from")) };
|
|
79
78
|
}
|
|
80
79
|
export {
|
|
81
80
|
stageLoginAction
|
package/dist/auth/action.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n // Return the target instead of calling redirect() here. The client-side\n // <StageLogin> reads this in a useEffect and calls router.replace, which\n // guarantees the Set-Cookie has been committed to the browser jar before\n // the next GET fires. This eliminates the cookie-commit vs redirect race\n // that surfaces on Vercel's edge runtime + CDN.\n return { redirectTo: safeRedirectTarget(formData.get('from')) };\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;AAEA,SAAS,eAAe;;;ACCxB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADtFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAOD,SAAO,EAAE,YAAY,mBAAmB,SAAS,IAAI,MAAM,CAAC,EAAE;AAChE;","names":[]}
|
|
@@ -155,6 +155,7 @@ var stagegatemiddleware = (next) => {
|
|
|
155
155
|
}
|
|
156
156
|
const url = request.nextUrl.clone();
|
|
157
157
|
url.pathname = LOGIN_PATH;
|
|
158
|
+
url.search = `?from=${encodeURIComponent(pathname + search)}`;
|
|
158
159
|
return withNoindex(NextResponse3.redirect(url, { status: 307 }));
|
|
159
160
|
};
|
|
160
161
|
};
|
|
@@ -165,4 +166,4 @@ export {
|
|
|
165
166
|
redirectmiddleware,
|
|
166
167
|
stagegatemiddleware
|
|
167
168
|
};
|
|
168
|
-
//# sourceMappingURL=chunk-
|
|
169
|
+
//# sourceMappingURL=chunk-UCR7WW3X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/middleware/chain.ts","../src/middleware/headermiddleware.ts","../src/middleware/redirectmiddleware.ts","../src/middleware/stagegatemiddleware.ts","../src/auth/stage.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","url"]}
|
|
@@ -130,8 +130,12 @@ function StageLogin({
|
|
|
130
130
|
missingConfigMessage = "Stage authentication is not configured."
|
|
131
131
|
}) {
|
|
132
132
|
const params = (0, import_navigation.useSearchParams)();
|
|
133
|
+
const router = (0, import_navigation.useRouter)();
|
|
133
134
|
const from = params.get("from") ?? "/";
|
|
134
135
|
const [state, formAction, pending] = (0, import_react4.useActionState)(action, {});
|
|
136
|
+
(0, import_react4.useEffect)(() => {
|
|
137
|
+
if (state.redirectTo) router.replace(state.redirectTo);
|
|
138
|
+
}, [state.redirectTo, router]);
|
|
135
139
|
const errorMessage = state.error === "invalid_credentials" ? invalidCredentialsMessage : state.error === "missing_config" ? missingConfigMessage : null;
|
|
136
140
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-full flex-col justify-center px-6 py-12 lg:px-8", children: [
|
|
137
141
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "sm:mx-auto sm:w-full sm:max-w-[384px]", children: [
|
|
@@ -142,6 +146,7 @@ function StageLogin({
|
|
|
142
146
|
] })
|
|
143
147
|
] }),
|
|
144
148
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { action: formAction, method: "POST", className: "space-y-6", children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "hidden", name: "from", value: from }),
|
|
145
150
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
146
151
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: "username", className: "block text-sm/6 font-medium text-gray-900", children: usernameLabel }),
|
|
147
152
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/index.ts","../../src/components/RouteContext.tsx","../../src/components/RouteSetter.tsx","../../src/components/JsonLd.tsx","../../src/components/GtmPage.tsx","../../src/components/StageLogin.tsx"],"sourcesContent":["export { RouteProvider, useRoute } from './RouteContext.js';\nexport { RouteSetter } from './RouteSetter.js';\nexport { JsonLd } from './JsonLd.js';\nexport { GtmPage, gtmPageView, gtmFormSent, gtmClick } from './GtmPage.js';\nexport { StageLogin } from './StageLogin.js';\nexport type { StageLoginProps } from './StageLogin.js';\n","'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState } from 'react';\nimport { useSearchParams } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/index.ts","../../src/components/RouteContext.tsx","../../src/components/RouteSetter.tsx","../../src/components/JsonLd.tsx","../../src/components/GtmPage.tsx","../../src/components/StageLogin.tsx"],"sourcesContent":["export { RouteProvider, useRoute } from './RouteContext.js';\nexport { RouteSetter } from './RouteSetter.js';\nexport { JsonLd } from './JsonLd.js';\nexport { GtmPage, gtmPageView, gtmFormSent, gtmClick } from './GtmPage.js';\nexport { StageLogin } from './StageLogin.js';\nexport type { StageLoginProps } from './StageLogin.js';\n","'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState, useEffect } from 'react';\nimport { useSearchParams, useRouter } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const router = useRouter();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n // Once the action returns a redirect target the cookie is already in the\n // browser's jar (it shipped as Set-Cookie on the action response). Only\n // then do we navigate, so the next GET carries the session and middleware\n // doesn't bounce the user back to login.\n useEffect(() => {\n if (state.redirectTo) router.replace(state.redirectTo);\n }, [state.redirectTo, router]);\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <input type=\"hidden\" name=\"from\" value={from} />\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
|
@@ -45,6 +45,7 @@ declare function gtmClick({ category, action, label }: GtmClickProps): number;
|
|
|
45
45
|
|
|
46
46
|
type StageLoginState = {
|
|
47
47
|
error?: 'missing_config' | 'invalid_credentials' | null;
|
|
48
|
+
redirectTo?: string | null;
|
|
48
49
|
};
|
|
49
50
|
type StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;
|
|
50
51
|
type StageLoginProps = {
|
|
@@ -45,6 +45,7 @@ declare function gtmClick({ category, action, label }: GtmClickProps): number;
|
|
|
45
45
|
|
|
46
46
|
type StageLoginState = {
|
|
47
47
|
error?: 'missing_config' | 'invalid_credentials' | null;
|
|
48
|
+
redirectTo?: string | null;
|
|
48
49
|
};
|
|
49
50
|
type StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;
|
|
50
51
|
type StageLoginProps = {
|
package/dist/components/index.js
CHANGED
|
@@ -70,8 +70,8 @@ function gtmClick({ category, action, label }) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// src/components/StageLogin.tsx
|
|
73
|
-
import { useActionState } from "react";
|
|
74
|
-
import { useSearchParams } from "next/navigation";
|
|
73
|
+
import { useActionState, useEffect as useEffect3 } from "react";
|
|
74
|
+
import { useSearchParams, useRouter } from "next/navigation";
|
|
75
75
|
import { Fragment as Fragment2, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
76
76
|
function OlmoLogo({ className }) {
|
|
77
77
|
return /* @__PURE__ */ jsx4(
|
|
@@ -97,8 +97,12 @@ function StageLogin({
|
|
|
97
97
|
missingConfigMessage = "Stage authentication is not configured."
|
|
98
98
|
}) {
|
|
99
99
|
const params = useSearchParams();
|
|
100
|
+
const router = useRouter();
|
|
100
101
|
const from = params.get("from") ?? "/";
|
|
101
102
|
const [state, formAction, pending] = useActionState(action, {});
|
|
103
|
+
useEffect3(() => {
|
|
104
|
+
if (state.redirectTo) router.replace(state.redirectTo);
|
|
105
|
+
}, [state.redirectTo, router]);
|
|
102
106
|
const errorMessage = state.error === "invalid_credentials" ? invalidCredentialsMessage : state.error === "missing_config" ? missingConfigMessage : null;
|
|
103
107
|
return /* @__PURE__ */ jsx4(Fragment2, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col justify-center px-6 py-12 lg:px-8", children: [
|
|
104
108
|
/* @__PURE__ */ jsxs("div", { className: "sm:mx-auto sm:w-full sm:max-w-[384px]", children: [
|
|
@@ -109,6 +113,7 @@ function StageLogin({
|
|
|
109
113
|
] })
|
|
110
114
|
] }),
|
|
111
115
|
/* @__PURE__ */ jsx4("div", { className: "mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]", children: /* @__PURE__ */ jsxs("form", { action: formAction, method: "POST", className: "space-y-6", children: [
|
|
116
|
+
/* @__PURE__ */ jsx4("input", { type: "hidden", name: "from", value: from }),
|
|
112
117
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
113
118
|
/* @__PURE__ */ jsx4("label", { htmlFor: "username", className: "block text-sm/6 font-medium text-gray-900", children: usernameLabel }),
|
|
114
119
|
/* @__PURE__ */ jsx4("div", { className: "mt-2", children: /* @__PURE__ */ jsx4(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/RouteContext.tsx","../../src/components/RouteSetter.tsx","../../src/components/JsonLd.tsx","../../src/components/GtmPage.tsx","../../src/components/StageLogin.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState } from 'react';\nimport { useSearchParams } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["jsx","useEffect","jsx","Fragment","jsx","jsx","Fragment"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/RouteContext.tsx","../../src/components/RouteSetter.tsx","../../src/components/JsonLd.tsx","../../src/components/GtmPage.tsx","../../src/components/StageLogin.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState, useEffect } from 'react';\nimport { useSearchParams, useRouter } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const router = useRouter();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n // Once the action returns a redirect target the cookie is already in the\n // browser's jar (it shipped as Set-Cookie on the action response). Only\n // then do we navigate, so the next GET carries the session and middleware\n // doesn't bounce the user back to login.\n useEffect(() => {\n if (state.redirectTo) router.replace(state.redirectTo);\n }, [state.redirectTo, router]);\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <input type=\"hidden\" name=\"from\" value={from} />\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["jsx","useEffect","jsx","useEffect","Fragment","jsx","useEffect","jsx","Fragment"]}
|
package/dist/index.cjs
CHANGED
|
@@ -376,6 +376,7 @@ var stagegatemiddleware = (next) => {
|
|
|
376
376
|
}
|
|
377
377
|
const url = request.nextUrl.clone();
|
|
378
378
|
url.pathname = LOGIN_PATH;
|
|
379
|
+
url.search = `?from=${encodeURIComponent(pathname + search)}`;
|
|
379
380
|
return withNoindex(import_server4.NextResponse.redirect(url, { status: 307 }));
|
|
380
381
|
};
|
|
381
382
|
};
|
|
@@ -477,8 +478,12 @@ function StageLogin({
|
|
|
477
478
|
missingConfigMessage = "Stage authentication is not configured."
|
|
478
479
|
}) {
|
|
479
480
|
const params = (0, import_navigation.useSearchParams)();
|
|
481
|
+
const router = (0, import_navigation.useRouter)();
|
|
480
482
|
const from = params.get("from") ?? "/";
|
|
481
483
|
const [state, formAction, pending] = (0, import_react4.useActionState)(action, {});
|
|
484
|
+
(0, import_react4.useEffect)(() => {
|
|
485
|
+
if (state.redirectTo) router.replace(state.redirectTo);
|
|
486
|
+
}, [state.redirectTo, router]);
|
|
482
487
|
const errorMessage = state.error === "invalid_credentials" ? invalidCredentialsMessage : state.error === "missing_config" ? missingConfigMessage : null;
|
|
483
488
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-full flex-col justify-center px-6 py-12 lg:px-8", children: [
|
|
484
489
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "sm:mx-auto sm:w-full sm:max-w-[384px]", children: [
|
|
@@ -489,6 +494,7 @@ function StageLogin({
|
|
|
489
494
|
] })
|
|
490
495
|
] }),
|
|
491
496
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { action: formAction, method: "POST", className: "space-y-6", children: [
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "hidden", name: "from", value: from }),
|
|
492
498
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
493
499
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: "username", className: "block text-sm/6 font-medium text-gray-900", children: usernameLabel }),
|
|
494
500
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client/index.ts","../src/seo/index.ts","../src/jsonld/index.ts","../src/middleware/chain.ts","../src/middleware/headermiddleware.ts","../src/middleware/redirectmiddleware.ts","../src/middleware/stagegatemiddleware.ts","../src/auth/stage.ts","../src/components/RouteContext.tsx","../src/components/RouteSetter.tsx","../src/components/JsonLd.tsx","../src/components/GtmPage.tsx","../src/components/StageLogin.tsx"],"sourcesContent":["export * from './client/index.js';\nexport * from './seo/index.js';\nexport * from './jsonld/index.js';\nexport * from './middleware/index.js';\nexport * from './components/index.js';\n","// Next.js extends RequestInit with a `next` property for cache tags / revalidation.\ntype NextRequestInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\nconst API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;\nconst API_TOKEN = process.env.NEXT_PUBLIC_OLMO_TOKEN as string;\n\nconst BASE_HEADERS: HeadersInit = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'front-token': API_TOKEN,\n};\n\nexport async function getting<T = unknown>(\n lang: string,\n path: string,\n model = '',\n): Promise<T | undefined> {\n const isPreview = path.includes('?olmopreview');\n\n try {\n const response = await fetch(`${API_BASE_URL}${path}`, {\n method: 'GET',\n headers: BASE_HEADERS,\n cache: isPreview ? 'no-cache' : 'force-cache',\n next: { tags: ['olmo', lang, path, model] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:get] ${response.status} ${response.statusText} — ${path}`);\n return undefined;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n console.error(`[olmo:get] Network error — ${path}:`, error);\n return undefined;\n }\n}\n\nexport async function posting<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n cache: 'force-cache',\n next: { tags: ['olmo', `/${lang}/allmodel/${path}`] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:post] ${response.status} ${response.statusText} — /${lang}/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n throw new Error(`Olmo API returned errors for /${lang}/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data.response as T;\n } catch (error) {\n console.error(`[olmo:post] Error — /${lang}/${path}:`, error);\n throw error;\n }\n}\n\nexport async function filter<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/filters/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n console.error(`[olmo:filter] ${response.status} ${response.statusText} — /${lang}/filters/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:filter] API errors — /${lang}/filters/${path}:`, data.errors);\n throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data as T;\n } catch (error) {\n console.error(`[olmo:filter] Error — /${lang}/filters/${path}:`, error);\n throw error;\n }\n}\n","export interface SeoDataOptions {\n seo: any;\n route: any;\n /**\n * Locale codes for hreflang alternates. Last entry becomes x-default.\n * Defaults to ['it', 'en'] when omitted.\n */\n locales?: string[];\n}\n\nexport const setSeoData = ({ seo, route, locales = ['it', 'en'] }: SeoDataOptions) => {\n if (!seo) return {};\n\n const baseUrl = process.env.NEXT_PUBLIC_API_STORAGE_URL;\n const frontUrl = process.env.NEXT_PUBLIC_BASE_URL;\n const ogImg = seo.og_img?.original;\n const ogUrl = ogImg?.compressed\n ? baseUrl + ogImg.compressed\n : frontUrl + '/images/og-image.jpg';\n\n const languagesMap: Record<string, string> = {};\n for (const loc of locales) {\n if (route.slug?.[loc] !== undefined) {\n languagesMap[loc] = `/${loc}/${route.slug[loc]}`;\n }\n }\n const defaultLocale = locales[locales.length - 1];\n if (route.slug?.[defaultLocale] !== undefined) {\n languagesMap['x-default'] = `/${defaultLocale}/${route.slug[defaultLocale]}`;\n }\n\n return {\n metadataBase: new URL(`${process.env.NEXT_PUBLIC_BASE_URL}`),\n title: seo.meta_title || '',\n description: seo.meta_description || '',\n robots: {\n index: seo.index === 'index',\n follow: seo.follow === 'follow',\n },\n openGraph: {\n title: seo.og_title || '',\n description: seo.og_description || '',\n url: seo.opengraphUrl || '',\n siteName: seo.opengraphSiteName || '',\n images: [\n {\n url: ogUrl,\n width: ogImg?.width || 1200,\n height: ogImg?.height || 630,\n alt: ogImg?.alt || '',\n },\n ],\n locale: route.locale,\n type: seo.opengraphType || 'website',\n },\n alternates: {\n canonical: `${process.env.NEXT_PUBLIC_BASE_URL}/${route.locale}/${route.slug[route.locale]}`,\n languages: languagesMap,\n },\n };\n};\n","const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL!;\nconst STORAGE_URL = process.env.NEXT_PUBLIC_API_STORAGE_URL!;\nconst SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? 'Website';\n\ntype BreadcrumbItem = { name: string; url: string };\n\nexport const buildBreadcrumb = (items: BreadcrumbItem[]) => ({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n item: item.url,\n })),\n});\n\nexport const buildItemList = (items: any[], sectionUrl: string) => ({\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.title_txt_content || item.name_txt_general,\n url: `${sectionUrl}${item.slug_txt_general}/`,\n })),\n});\n\nexport const buildArticle = (\n page: any,\n pathname: string,\n articleType: 'Article' | 'NewsArticle' = 'Article',\n publisherName?: string,\n) => {\n const name = publisherName ?? SITE_NAME;\n const pageUrl = `${BASE_URL}${pathname.endsWith('/') ? pathname : pathname + '/'}`;\n const imageUrl = (page.primary ?? page.cover)?.original?.compressed\n ? `${STORAGE_URL}${(page.primary ?? page.cover).original.compressed}`\n : undefined;\n const description = page.subtitle\n ? page.subtitle.replace(/<[^>]*>/g, '').trim()\n : undefined;\n\n const publisher = {\n '@type': 'Organization',\n name,\n url: BASE_URL,\n logo: { '@type': 'ImageObject', url: `${BASE_URL}/images/logo/logo.png` },\n };\n\n return {\n '@context': 'https://schema.org',\n '@type': articleType,\n headline: page.title,\n ...(description && { description }),\n ...(imageUrl && { image: imageUrl }),\n url: pageUrl,\n author: publisher,\n publisher,\n ...(page.lastmod && { datePublished: page.lastmod, dateModified: page.lastmod }),\n };\n};\n\nexport const buildWebSite = (locale: string, siteName?: string) => ({\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n name: siteName ?? SITE_NAME,\n url: `${BASE_URL}/${locale}/`,\n});\n","import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n // url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n","'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState } from 'react';\nimport { useSearchParams } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["import_server","import_server","import_server","url","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client/index.ts","../src/seo/index.ts","../src/jsonld/index.ts","../src/middleware/chain.ts","../src/middleware/headermiddleware.ts","../src/middleware/redirectmiddleware.ts","../src/middleware/stagegatemiddleware.ts","../src/auth/stage.ts","../src/components/RouteContext.tsx","../src/components/RouteSetter.tsx","../src/components/JsonLd.tsx","../src/components/GtmPage.tsx","../src/components/StageLogin.tsx"],"sourcesContent":["export * from './client/index.js';\nexport * from './seo/index.js';\nexport * from './jsonld/index.js';\nexport * from './middleware/index.js';\nexport * from './components/index.js';\n","// Next.js extends RequestInit with a `next` property for cache tags / revalidation.\ntype NextRequestInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\nconst API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;\nconst API_TOKEN = process.env.NEXT_PUBLIC_OLMO_TOKEN as string;\n\nconst BASE_HEADERS: HeadersInit = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'front-token': API_TOKEN,\n};\n\nexport async function getting<T = unknown>(\n lang: string,\n path: string,\n model = '',\n): Promise<T | undefined> {\n const isPreview = path.includes('?olmopreview');\n\n try {\n const response = await fetch(`${API_BASE_URL}${path}`, {\n method: 'GET',\n headers: BASE_HEADERS,\n cache: isPreview ? 'no-cache' : 'force-cache',\n next: { tags: ['olmo', lang, path, model] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:get] ${response.status} ${response.statusText} — ${path}`);\n return undefined;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n console.error(`[olmo:get] Network error — ${path}:`, error);\n return undefined;\n }\n}\n\nexport async function posting<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n cache: 'force-cache',\n next: { tags: ['olmo', `/${lang}/allmodel/${path}`] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:post] ${response.status} ${response.statusText} — /${lang}/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n throw new Error(`Olmo API returned errors for /${lang}/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data.response as T;\n } catch (error) {\n console.error(`[olmo:post] Error — /${lang}/${path}:`, error);\n throw error;\n }\n}\n\nexport async function filter<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/filters/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n console.error(`[olmo:filter] ${response.status} ${response.statusText} — /${lang}/filters/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:filter] API errors — /${lang}/filters/${path}:`, data.errors);\n throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data as T;\n } catch (error) {\n console.error(`[olmo:filter] Error — /${lang}/filters/${path}:`, error);\n throw error;\n }\n}\n","export interface SeoDataOptions {\n seo: any;\n route: any;\n /**\n * Locale codes for hreflang alternates. Last entry becomes x-default.\n * Defaults to ['it', 'en'] when omitted.\n */\n locales?: string[];\n}\n\nexport const setSeoData = ({ seo, route, locales = ['it', 'en'] }: SeoDataOptions) => {\n if (!seo) return {};\n\n const baseUrl = process.env.NEXT_PUBLIC_API_STORAGE_URL;\n const frontUrl = process.env.NEXT_PUBLIC_BASE_URL;\n const ogImg = seo.og_img?.original;\n const ogUrl = ogImg?.compressed\n ? baseUrl + ogImg.compressed\n : frontUrl + '/images/og-image.jpg';\n\n const languagesMap: Record<string, string> = {};\n for (const loc of locales) {\n if (route.slug?.[loc] !== undefined) {\n languagesMap[loc] = `/${loc}/${route.slug[loc]}`;\n }\n }\n const defaultLocale = locales[locales.length - 1];\n if (route.slug?.[defaultLocale] !== undefined) {\n languagesMap['x-default'] = `/${defaultLocale}/${route.slug[defaultLocale]}`;\n }\n\n return {\n metadataBase: new URL(`${process.env.NEXT_PUBLIC_BASE_URL}`),\n title: seo.meta_title || '',\n description: seo.meta_description || '',\n robots: {\n index: seo.index === 'index',\n follow: seo.follow === 'follow',\n },\n openGraph: {\n title: seo.og_title || '',\n description: seo.og_description || '',\n url: seo.opengraphUrl || '',\n siteName: seo.opengraphSiteName || '',\n images: [\n {\n url: ogUrl,\n width: ogImg?.width || 1200,\n height: ogImg?.height || 630,\n alt: ogImg?.alt || '',\n },\n ],\n locale: route.locale,\n type: seo.opengraphType || 'website',\n },\n alternates: {\n canonical: `${process.env.NEXT_PUBLIC_BASE_URL}/${route.locale}/${route.slug[route.locale]}`,\n languages: languagesMap,\n },\n };\n};\n","const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL!;\nconst STORAGE_URL = process.env.NEXT_PUBLIC_API_STORAGE_URL!;\nconst SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? 'Website';\n\ntype BreadcrumbItem = { name: string; url: string };\n\nexport const buildBreadcrumb = (items: BreadcrumbItem[]) => ({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n item: item.url,\n })),\n});\n\nexport const buildItemList = (items: any[], sectionUrl: string) => ({\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.title_txt_content || item.name_txt_general,\n url: `${sectionUrl}${item.slug_txt_general}/`,\n })),\n});\n\nexport const buildArticle = (\n page: any,\n pathname: string,\n articleType: 'Article' | 'NewsArticle' = 'Article',\n publisherName?: string,\n) => {\n const name = publisherName ?? SITE_NAME;\n const pageUrl = `${BASE_URL}${pathname.endsWith('/') ? pathname : pathname + '/'}`;\n const imageUrl = (page.primary ?? page.cover)?.original?.compressed\n ? `${STORAGE_URL}${(page.primary ?? page.cover).original.compressed}`\n : undefined;\n const description = page.subtitle\n ? page.subtitle.replace(/<[^>]*>/g, '').trim()\n : undefined;\n\n const publisher = {\n '@type': 'Organization',\n name,\n url: BASE_URL,\n logo: { '@type': 'ImageObject', url: `${BASE_URL}/images/logo/logo.png` },\n };\n\n return {\n '@context': 'https://schema.org',\n '@type': articleType,\n headline: page.title,\n ...(description && { description }),\n ...(imageUrl && { image: imageUrl }),\n url: pageUrl,\n author: publisher,\n publisher,\n ...(page.lastmod && { datePublished: page.lastmod, dateModified: page.lastmod }),\n };\n};\n\nexport const buildWebSite = (locale: string, siteName?: string) => ({\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n name: siteName ?? SITE_NAME,\n url: `${BASE_URL}/${locale}/`,\n});\n","import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n","'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState, useEffect } from 'react';\nimport { useSearchParams, useRouter } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const router = useRouter();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n // Once the action returns a redirect target the cookie is already in the\n // browser's jar (it shipped as Set-Cookie on the action response). Only\n // then do we navigate, so the next GET carries the session and middleware\n // doesn't bounce the user back to login.\n useEffect(() => {\n if (state.redirectTo) router.replace(state.redirectTo);\n }, [state.redirectTo, router]);\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <input type=\"hidden\" name=\"from\" value={from} />\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["import_server","import_server","import_server","url","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -50,6 +50,7 @@ declare function gtmClick({ category, action, label }: GtmClickProps): number;
|
|
|
50
50
|
|
|
51
51
|
type StageLoginState = {
|
|
52
52
|
error?: 'missing_config' | 'invalid_credentials' | null;
|
|
53
|
+
redirectTo?: string | null;
|
|
53
54
|
};
|
|
54
55
|
type StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;
|
|
55
56
|
type StageLoginProps = {
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ declare function gtmClick({ category, action, label }: GtmClickProps): number;
|
|
|
50
50
|
|
|
51
51
|
type StageLoginState = {
|
|
52
52
|
error?: 'missing_config' | 'invalid_credentials' | null;
|
|
53
|
+
redirectTo?: string | null;
|
|
53
54
|
};
|
|
54
55
|
type StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;
|
|
55
56
|
type StageLoginProps = {
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
headermiddleware,
|
|
18
18
|
redirectmiddleware,
|
|
19
19
|
stagegatemiddleware
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-UCR7WW3X.js";
|
|
21
21
|
|
|
22
22
|
// src/components/RouteContext.tsx
|
|
23
23
|
import { createContext, useContext, useState } from "react";
|
|
@@ -89,8 +89,8 @@ function gtmClick({ category, action, label }) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// src/components/StageLogin.tsx
|
|
92
|
-
import { useActionState } from "react";
|
|
93
|
-
import { useSearchParams } from "next/navigation";
|
|
92
|
+
import { useActionState, useEffect as useEffect3 } from "react";
|
|
93
|
+
import { useSearchParams, useRouter } from "next/navigation";
|
|
94
94
|
import { Fragment as Fragment2, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
95
95
|
function OlmoLogo({ className }) {
|
|
96
96
|
return /* @__PURE__ */ jsx4(
|
|
@@ -116,8 +116,12 @@ function StageLogin({
|
|
|
116
116
|
missingConfigMessage = "Stage authentication is not configured."
|
|
117
117
|
}) {
|
|
118
118
|
const params = useSearchParams();
|
|
119
|
+
const router = useRouter();
|
|
119
120
|
const from = params.get("from") ?? "/";
|
|
120
121
|
const [state, formAction, pending] = useActionState(action, {});
|
|
122
|
+
useEffect3(() => {
|
|
123
|
+
if (state.redirectTo) router.replace(state.redirectTo);
|
|
124
|
+
}, [state.redirectTo, router]);
|
|
121
125
|
const errorMessage = state.error === "invalid_credentials" ? invalidCredentialsMessage : state.error === "missing_config" ? missingConfigMessage : null;
|
|
122
126
|
return /* @__PURE__ */ jsx4(Fragment2, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col justify-center px-6 py-12 lg:px-8", children: [
|
|
123
127
|
/* @__PURE__ */ jsxs("div", { className: "sm:mx-auto sm:w-full sm:max-w-[384px]", children: [
|
|
@@ -128,6 +132,7 @@ function StageLogin({
|
|
|
128
132
|
] })
|
|
129
133
|
] }),
|
|
130
134
|
/* @__PURE__ */ jsx4("div", { className: "mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]", children: /* @__PURE__ */ jsxs("form", { action: formAction, method: "POST", className: "space-y-6", children: [
|
|
135
|
+
/* @__PURE__ */ jsx4("input", { type: "hidden", name: "from", value: from }),
|
|
131
136
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
132
137
|
/* @__PURE__ */ jsx4("label", { htmlFor: "username", className: "block text-sm/6 font-medium text-gray-900", children: usernameLabel }),
|
|
133
138
|
/* @__PURE__ */ jsx4("div", { className: "mt-2", children: /* @__PURE__ */ jsx4(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/RouteContext.tsx","../src/components/RouteSetter.tsx","../src/components/JsonLd.tsx","../src/components/GtmPage.tsx","../src/components/StageLogin.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState } from 'react';\nimport { useSearchParams } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["jsx","useEffect","jsx","Fragment","jsx","jsx","Fragment"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/RouteContext.tsx","../src/components/RouteSetter.tsx","../src/components/JsonLd.tsx","../src/components/GtmPage.tsx","../src/components/StageLogin.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useState } from 'react';\nimport type { ReactNode } from 'react';\n\ntype LocaleSlugs = Record<string, string>;\n\nconst RouteContext = createContext<{\n slugs: LocaleSlugs;\n setSlugs: (slugs: LocaleSlugs) => void;\n}>({ slugs: {}, setSlugs: () => {} });\n\nexport function RouteProvider({ children }: { children: ReactNode }) {\n const [slugs, setSlugs] = useState<LocaleSlugs>({});\n return (\n <RouteContext.Provider value={{ slugs, setSlugs }}>\n {children}\n </RouteContext.Provider>\n );\n}\n\nexport const useRoute = () => useContext(RouteContext);\n","'use client';\n\nimport { useEffect } from 'react';\nimport { useRoute } from './RouteContext.js';\n\nexport function RouteSetter({ slugs }: { slugs: Record<string, string> }) {\n const { setSlugs } = useRoute();\n\n useEffect(() => {\n const leafSlugs = Object.fromEntries(\n Object.entries(slugs).map(([locale, slug]) => [locale, slug.split('/').pop() ?? slug]),\n );\n setSlugs(leafSlugs);\n return () => setSlugs({});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(slugs)]);\n\n return null;\n}\n","export function JsonLd({ schema }: { schema: object }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n );\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\ninterface GtmPageProps {\n slug: string | null;\n lang: string | null;\n name: string | null;\n template: string | null;\n}\n\ninterface GtmFormProps {\n data: object | null;\n name: string | null;\n}\n\ninterface GtmClickProps {\n category: string | null;\n action: string | null;\n label: string | null;\n}\n\ndeclare global {\n interface Window {\n dataLayer: any[];\n }\n}\n\nexport function GtmPage({ slug, lang, name, template }: GtmPageProps) {\n useEffect(() => {\n if (slug) {\n gtmPageView({\n page_name: name,\n page_category: (template as any)?.name,\n page_language: lang,\n path_clean: slug,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slug]);\n\n return <></>;\n}\n\nexport function gtmPageView(props: Record<string, any>) {\n return window.dataLayer?.push({ event: 'page_view', url: window.location.href, ...props });\n}\n\nexport function gtmFormSent({ data, name }: GtmFormProps) {\n return window.dataLayer?.push({ ...data, event: 'form_sent', form: name });\n}\n\nexport function gtmClick({ category, action, label }: GtmClickProps) {\n const cleanLabel = label?.split('/') ?? [];\n return window.dataLayer?.push({\n event: 'click',\n category,\n action,\n label: cleanLabel[cleanLabel.length - 1],\n });\n}\n","import { useActionState, useEffect } from 'react';\nimport { useSearchParams, useRouter } from 'next/navigation';\n\nfunction OlmoLogo({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n aria-label=\"Olmo\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 225 232\"\n className={className}\n >\n <path d=\"M102.3,12.2c75-6.4,129.5,63.8,108.1,135.4-23.3,77.6-124.6,98.3-176.6,36.2C-18.2,121.7,19.1,19.2,102.4,12.2h-.1ZM87.4,202.3c35.6,9.9,73.6-2.3,95.6-32.1,37.4-50.9,12.4-125.6-49.4-140.8C59.5,11.2,1.3,86.9,32.6,154.9c7.7,16.8,23.8,34.9,41.6,40.8l5.5-20.1c-20-27-17.3-63.2,7.3-86.4,11-10.3,38.6-24.9,53.1-28.9s8.1-.9,10.4,3.2,6.5,23.1,7.4,28.4c4,22.6,2.1,44.6-13,62.8-13.1,15.8-31,22.5-50.8,25.5l-6.6,22.1h0ZM139.2,77c-33.7,10.7-72,38.9-54.1,79.1l11-20.1c4.1-4.6,12.6-23.1,17.7-24.7,6.3-2,10.9,3.5,8.7,9.7s-11.8,18.1-15.5,25.1-9,17.8-8.3,18.5c43-8.8,54.8-49.5,40.5-87.6h0Z\" />\n </svg>\n );\n}\n\ntype StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\ntype StageLoginAction = (prev: StageLoginState, formData: FormData) => Promise<StageLoginState>;\n\nexport type StageLoginProps = {\n action: StageLoginAction;\n title?: string;\n description?: string;\n usernameLabel?: string;\n passwordLabel?: string;\n submitLabel?: string;\n invalidCredentialsMessage?: string;\n missingConfigMessage?: string;\n};\n\nexport function StageLoginOLD({\n action,\n title = 'Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.',\n}: StageLoginProps) {\n const params = useSearchParams();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-neutral-50 px-4 py-12\">\n <form\n action={formAction}\n className=\"relative w-full max-w-md space-y-5 rounded-lg border border-neutral-200 bg-white p-8 shadow-sm\"\n >\n <div className=\"w-full min-w-[200px]\">\n <header className=\"space-y-1 text-center\">\n <h1 className=\"text-xl font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-neutral-600\">{description}</p>\n </header>\n\n <input type=\"hidden\" name=\"from\" value={from} />\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{usernameLabel}</span>\n <input\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-neutral-800\">{passwordLabel}</span>\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n disabled={pending}\n className=\"w-full rounded border border-neutral-300 px-3 py-2 text-sm outline-none focus:border-neutral-900\"\n />\n </label>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null}\n\n <button\n type=\"submit\"\n disabled={pending}\n className=\"w-full rounded bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 disabled:opacity-60\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n </main>\n );\n}\n\n\nexport function StageLogin({\n action,\n title = 'Olmo Staging access',\n description = 'This environment is restricted. Please sign in to continue.',\n usernameLabel = 'Username',\n passwordLabel = 'Password',\n submitLabel = 'Sign in',\n invalidCredentialsMessage = 'Invalid username or password.',\n missingConfigMessage = 'Stage authentication is not configured.', \n}: StageLoginProps) {\n \n const params = useSearchParams();\n const router = useRouter();\n const from = params.get('from') ?? '/';\n const [state, formAction, pending] = useActionState<StageLoginState, FormData>(action, {});\n\n // Once the action returns a redirect target the cookie is already in the\n // browser's jar (it shipped as Set-Cookie on the action response). Only\n // then do we navigate, so the next GET carries the session and middleware\n // doesn't bounce the user back to login.\n useEffect(() => {\n if (state.redirectTo) router.replace(state.redirectTo);\n }, [state.redirectTo, router]);\n\n const errorMessage =\n state.error === 'invalid_credentials'\n ? invalidCredentialsMessage\n : state.error === 'missing_config'\n ? missingConfigMessage\n : null;\n\n return (\n <>\n <div className=\"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8\">\n <div className=\"sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <OlmoLogo className=\"mx-auto h-10 w-auto \" />\n {/* <h2 className=\"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900\">\n Sign in to your account\n </h2> */}\n <header className=\"space-y-1\">\n <h1 className=\"text-xl text-center font-semibold text-neutral-900\">{title}</h1>\n <p className=\"text-sm text-center text-neutral-600\">{description}</p>\n </header> \n </div>\n\n <div className=\"mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]\">\n <form action={formAction} method=\"POST\" className=\"space-y-6\">\n <input type=\"hidden\" name=\"from\" value={from} />\n <div>\n <label htmlFor=\"username\" className=\"block text-sm/6 font-medium text-gray-900\">\n {usernameLabel}\n </label>\n <div className=\"mt-2\">\n <input\n id=\"username\"\n name=\"username\"\n type=\"username\"\n disabled={pending}\n required\n autoComplete=\"username\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"password\" className=\"block text-sm/6 font-medium text-gray-900\">\n {passwordLabel}\n </label>\n </div>\n <div className=\"mt-2\">\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n disabled={pending}\n required\n autoComplete=\"current-password\"\n className=\"block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-black sm:text-sm/6\"\n />\n </div>\n </div>\n\n <div>\n {errorMessage ? (\n <p role=\"alert\" className=\"text-sm text-red-600\">\n {errorMessage}\n </p>\n ) : null} \n <button\n type=\"submit\"\n disabled={pending}\n className=\"cursor-pointer flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm/6 font-semibold text-white shadow-xs hover:bg-black focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black\"\n >\n {pending ? '…' : submitLabel}\n </button>\n </div>\n </form>\n\n </div>\n </div>\n </>\n )\n}\n"],"mappings":"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","names":["jsx","useEffect","jsx","useEffect","Fragment","jsx","useEffect","jsx","Fragment"]}
|
|
@@ -184,6 +184,7 @@ var stagegatemiddleware = (next) => {
|
|
|
184
184
|
}
|
|
185
185
|
const url = request.nextUrl.clone();
|
|
186
186
|
url.pathname = LOGIN_PATH;
|
|
187
|
+
url.search = `?from=${encodeURIComponent(pathname + search)}`;
|
|
187
188
|
return withNoindex(import_server4.NextResponse.redirect(url, { status: 307 }));
|
|
188
189
|
};
|
|
189
190
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/middleware/index.ts","../../src/middleware/chain.ts","../../src/middleware/headermiddleware.ts","../../src/middleware/redirectmiddleware.ts","../../src/middleware/stagegatemiddleware.ts","../../src/auth/stage.ts"],"sourcesContent":["export { chain } from './chain.js';\nexport type { MiddlewareFactory } from './chain.js';\nexport { headermiddleware } from './headermiddleware.js';\nexport { redirectmiddleware } from './redirectmiddleware.js';\nexport { stagegatemiddleware } from './stagegatemiddleware.js';\n","import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n // url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":"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","names":["import_server","import_server","import_server","url"]}
|
|
1
|
+
{"version":3,"sources":["../../src/middleware/index.ts","../../src/middleware/chain.ts","../../src/middleware/headermiddleware.ts","../../src/middleware/redirectmiddleware.ts","../../src/middleware/stagegatemiddleware.ts","../../src/auth/stage.ts"],"sourcesContent":["export { chain } from './chain.js';\nexport type { MiddlewareFactory } from './chain.js';\nexport { headermiddleware } from './headermiddleware.js';\nexport { redirectmiddleware } from './redirectmiddleware.js';\nexport { stagegatemiddleware } from './stagegatemiddleware.js';\n","import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":"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","names":["import_server","import_server","import_server","url"]}
|
package/dist/middleware/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware/chain.ts","../src/middleware/headermiddleware.ts","../src/middleware/redirectmiddleware.ts","../src/middleware/stagegatemiddleware.ts","../src/auth/stage.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.NEXT_PUBLIC_OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n // url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","url"]}
|