feat: Upgrade to Astro with full PDPA compliance
PDPA Features: ✅ Cookie consent banner ✅ Consent logging API ✅ Admin dashboard ✅ Privacy Policy ✅ Terms & Conditions Technical: ✅ Astro 5.x + Tailwind v4 ✅ Docker on port 80 ✅ SQLite database ✅ 15 pages built Ready for Easypanel deployment.
This commit is contained in:
345
node_modules/astro-consent/dist/cli.cjs
generated
vendored
Executable file
345
node_modules/astro-consent/dist/cli.cjs
generated
vendored
Executable file
@@ -0,0 +1,345 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("node:fs"));
|
||||
const path = __importStar(require("node:path"));
|
||||
const process = __importStar(require("node:process"));
|
||||
const CWD = process.cwd();
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0] ?? "install";
|
||||
const ASTRO_CONFIG_FILES = [
|
||||
"astro.config.mjs",
|
||||
"astro.config.ts",
|
||||
"astro.config.js"
|
||||
];
|
||||
function findAstroConfig() {
|
||||
for (const file of ASTRO_CONFIG_FILES) {
|
||||
const fullPath = path.join(CWD, file);
|
||||
if (fs.existsSync(fullPath))
|
||||
return fullPath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function exitWith(message, code = 1) {
|
||||
console.error(`\n❌ ${message}\n`);
|
||||
process.exit(code);
|
||||
}
|
||||
/* ─────────────────────────────────────
|
||||
Locate astro.config
|
||||
|
||||
───────────────────────────────────── */
|
||||
const configPath = findAstroConfig();
|
||||
if (!configPath) {
|
||||
exitWith("No astro.config.(mjs|ts|js) found. Run this in the root of an Astro project.");
|
||||
}
|
||||
let source = fs.readFileSync(configPath, "utf8");
|
||||
/* ─────────────────────────────────────
|
||||
REMOVE MODE
|
||||
───────────────────────────────────── */
|
||||
if (command === "remove") {
|
||||
source = source
|
||||
.replace(/\s*astroConsent\s*\([\s\S]*?\),?/gm, "")
|
||||
.replace(/import\s+astroConsent\s+from\s+["']astro-consent["'];?\n?/, "");
|
||||
fs.writeFileSync(configPath, source.trim() + "\n", "utf8");
|
||||
const cssFile = path.join(CWD, "src", "cookiebanner.css");
|
||||
if (fs.existsSync(cssFile))
|
||||
fs.unlinkSync(cssFile);
|
||||
console.log("\n🧹 astro-consent fully removed\n");
|
||||
process.exit(0);
|
||||
}
|
||||
/* ─────────────────────────────────────
|
||||
INSTALL MODE
|
||||
───────────────────────────────────── */
|
||||
const cssDir = path.join(CWD, "src");
|
||||
const cssFile = path.join(cssDir, "cookiebanner.css");
|
||||
if (!fs.existsSync(cssDir)) {
|
||||
fs.mkdirSync(cssDir, { recursive: true });
|
||||
}
|
||||
/* ─────────────────────────────────────
|
||||
Create CSS file ONCE (theme + structure)
|
||||
───────────────────────────────────── */
|
||||
if (!fs.existsSync(cssFile)) {
|
||||
fs.writeFileSync(cssFile, `/* =========================================================
|
||||
astro-consent — FULL THEME + STRUCTURE
|
||||
This file is NEVER overwritten.
|
||||
========================================================= */
|
||||
|
||||
:root {
|
||||
--cb-font: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
|
||||
--cb-bg: var(--color-surface, #0c1220);
|
||||
--cb-border: var(--color-border, rgba(255,255,255,0.08));
|
||||
--cb-shadow: 0 20px 40px rgba(15, 23, 42, 0.08);
|
||||
|
||||
--cb-text: var(--color-text, #ffffff);
|
||||
--cb-muted: var(--color-muted, #9ca3af);
|
||||
--cb-link: var(--color-primary, #60a5fa);
|
||||
|
||||
--cb-radius: 16px;
|
||||
--cb-btn-radius: 999px;
|
||||
--cb-btn-padding: 10px 18px;
|
||||
|
||||
--cb-accept-bg: var(--color-cta, #22c55e);
|
||||
--cb-accept-text: #ffffff;
|
||||
|
||||
--cb-reject-bg: var(--cb-border);
|
||||
--cb-reject-text: var(--cb-text);
|
||||
|
||||
--cb-manage-bg: transparent;
|
||||
--cb-manage-text: var(--cb-text);
|
||||
--cb-manage-border: var(--cb-border);
|
||||
|
||||
--cb-modal-bg: var(--cb-bg);
|
||||
--cb-modal-backdrop: rgba(15, 23, 42, 0.45);
|
||||
--cb-modal-radius: 18px;
|
||||
--cb-modal-width: 480px;
|
||||
|
||||
--cb-toggle-off-bg: var(--cb-border);
|
||||
--cb-toggle-on-bg: var(--color-accent, #22c55e);
|
||||
--cb-toggle-knob: #ffffff;
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Banner
|
||||
=============================== */
|
||||
|
||||
#astro-consent-banner {
|
||||
position: fixed;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
z-index: 9999;
|
||||
font-family: var(--cb-font);
|
||||
}
|
||||
|
||||
.cb-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 24px;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
background: var(--cb-bg);
|
||||
border-radius: var(--cb-radius);
|
||||
border: 1px solid var(--cb-border);
|
||||
box-shadow: var(--cb-shadow);
|
||||
color: var(--cb-text);
|
||||
}
|
||||
|
||||
.cb-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cb-desc {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
color: var(--cb-muted);
|
||||
}
|
||||
|
||||
.cb-desc a {
|
||||
color: var(--cb-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cb-desc a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Buttons
|
||||
=============================== */
|
||||
|
||||
.cb-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cb-actions button {
|
||||
padding: var(--cb-btn-padding);
|
||||
border-radius: var(--cb-btn-radius);
|
||||
border: 1px solid transparent;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cb-accept {
|
||||
background: var(--cb-accept-bg);
|
||||
color: var(--cb-accept-text);
|
||||
}
|
||||
|
||||
.cb-reject {
|
||||
background: var(--cb-reject-bg);
|
||||
color: var(--cb-reject-text);
|
||||
}
|
||||
|
||||
.cb-manage {
|
||||
background: var(--cb-manage-bg);
|
||||
color: var(--cb-manage-text);
|
||||
border: 1px solid var(--cb-manage-border);
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Modal
|
||||
=============================== */
|
||||
|
||||
#astro-consent-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--cb-modal-backdrop);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.cb-modal {
|
||||
width: 100%;
|
||||
max-width: var(--cb-modal-width);
|
||||
background: var(--cb-modal-bg);
|
||||
border-radius: var(--cb-modal-radius);
|
||||
padding: 28px;
|
||||
color: var(--cb-text);
|
||||
border: 1px solid var(--cb-border);
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Modal rows (SPACING FIXED)
|
||||
=============================== */
|
||||
|
||||
.cb-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid var(--cb-border);
|
||||
}
|
||||
|
||||
.cb-row:last-of-type {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Modal actions
|
||||
=============================== */
|
||||
|
||||
.cb-modal .cb-actions {
|
||||
margin-top: 24px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--cb-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Toggles
|
||||
=============================== */
|
||||
|
||||
.cb-toggle {
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
background: var(--cb-toggle-off-bg);
|
||||
border-radius: 999px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cb-toggle span {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: var(--cb-toggle-knob);
|
||||
border-radius: 50%;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.cb-toggle.active {
|
||||
background: var(--cb-toggle-on-bg);
|
||||
}
|
||||
|
||||
.cb-toggle.active span {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* ===============================
|
||||
Mobile
|
||||
=============================== */
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.cb-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.cb-actions {
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
`, "utf8");
|
||||
console.log("🎨 Created src/cookiebanner.css");
|
||||
}
|
||||
/* ─────────────────────────────────────
|
||||
Inject Astro integration
|
||||
───────────────────────────────────── */
|
||||
if (!source.includes(`from "astro-consent"`)) {
|
||||
source = `import astroConsent from "astro-consent";\n${source}`;
|
||||
}
|
||||
if (!source.includes("astroConsent(")) {
|
||||
source = source.replace(/integrations\s*:\s*\[/, match => `${match}
|
||||
astroConsent({
|
||||
siteName: "My Website",
|
||||
policyUrl: "/privacy",
|
||||
consent: {
|
||||
days: 30,
|
||||
storageKey: "astro-consent"
|
||||
}
|
||||
}),
|
||||
`);
|
||||
}
|
||||
fs.writeFileSync(configPath, source, "utf8");
|
||||
console.log("\n🎉 astro-consent installed successfully");
|
||||
console.log("👉 Style everything via src/cookiebanner.css");
|
||||
console.log("👉 Run `astro-consent remove` to uninstall\n");
|
||||
2
node_modules/astro-consent/dist/cli.d.ts
generated
vendored
Normal file
2
node_modules/astro-consent/dist/cli.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
13
node_modules/astro-consent/dist/config/defaults.d.ts
generated
vendored
Normal file
13
node_modules/astro-consent/dist/config/defaults.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CookieBannerConfig } from "../types/config.js";
|
||||
/**
|
||||
* DEFAULT_CONFIG
|
||||
* ---------------------------------------------------------
|
||||
* Fallback values ONLY.
|
||||
*
|
||||
* These are used:
|
||||
* - when the user omits a value
|
||||
* - during internal normalisation
|
||||
*
|
||||
* User-provided config ALWAYS takes priority.
|
||||
*/
|
||||
export declare const DEFAULT_CONFIG: CookieBannerConfig;
|
||||
40
node_modules/astro-consent/dist/config/defaults.js
generated
vendored
Normal file
40
node_modules/astro-consent/dist/config/defaults.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* DEFAULT_CONFIG
|
||||
* ---------------------------------------------------------
|
||||
* Fallback values ONLY.
|
||||
*
|
||||
* These are used:
|
||||
* - when the user omits a value
|
||||
* - during internal normalisation
|
||||
*
|
||||
* User-provided config ALWAYS takes priority.
|
||||
*/
|
||||
export const DEFAULT_CONFIG = {
|
||||
siteName: "This website",
|
||||
policyUrl: "/privacy",
|
||||
consent: {
|
||||
enabled: true,
|
||||
// Number of days consent remains valid
|
||||
days: 30,
|
||||
// Must match runtime + frontend API
|
||||
storageKey: "astro-consent"
|
||||
},
|
||||
categories: {
|
||||
essential: {
|
||||
label: "Essential",
|
||||
description: "Required for the website to function correctly",
|
||||
enabled: true,
|
||||
readonly: true
|
||||
},
|
||||
analytics: {
|
||||
label: "Analytics",
|
||||
description: "Helps us understand how visitors use the site",
|
||||
enabled: false
|
||||
},
|
||||
marketing: {
|
||||
label: "Marketing",
|
||||
description: "Used to deliver personalised ads",
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
};
|
||||
7
node_modules/astro-consent/dist/config/loadConfig.d.ts
generated
vendored
Normal file
7
node_modules/astro-consent/dist/config/loadConfig.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { CookieBannerConfig } from "../types/config.js";
|
||||
/**
|
||||
* Safely loads the user config file and merges it with defaults.
|
||||
* User values always override defaults.
|
||||
* Cache-busted to ensure updates are picked up during dev.
|
||||
*/
|
||||
export declare function loadUserConfig(projectRoot: string): Promise<CookieBannerConfig>;
|
||||
65
node_modules/astro-consent/dist/config/loadConfig.js
generated
vendored
Normal file
65
node_modules/astro-consent/dist/config/loadConfig.js
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { DEFAULT_CONFIG } from "./defaults.js";
|
||||
/**
|
||||
* Safely loads the user config file and merges it with defaults.
|
||||
* User values always override defaults.
|
||||
* Cache-busted to ensure updates are picked up during dev.
|
||||
*/
|
||||
export async function loadUserConfig(projectRoot) {
|
||||
const configPath = path.join(projectRoot, "src", "astro-consent", "config.ts");
|
||||
let userConfig = {};
|
||||
try {
|
||||
const stat = fs.statSync(configPath);
|
||||
const cacheBuster = `?v=${stat.mtimeMs}`;
|
||||
const imported = await import(
|
||||
/* @vite-ignore */
|
||||
pathToFileURL(configPath).href + cacheBuster);
|
||||
userConfig = imported?.default ?? {};
|
||||
}
|
||||
catch (err) {
|
||||
console.warn("[astro-consent] Failed to load user config, falling back to defaults");
|
||||
}
|
||||
return {
|
||||
siteName: userConfig.siteName ?? DEFAULT_CONFIG.siteName,
|
||||
policyUrl: userConfig.policyUrl ?? DEFAULT_CONFIG.policyUrl,
|
||||
consent: {
|
||||
enabled: userConfig.consent?.enabled ??
|
||||
DEFAULT_CONFIG.consent.enabled,
|
||||
days: userConfig.consent?.days ??
|
||||
DEFAULT_CONFIG.consent.days,
|
||||
storageKey: userConfig.consent?.storageKey ??
|
||||
DEFAULT_CONFIG.consent.storageKey
|
||||
},
|
||||
categories: mergeCategories(userConfig.categories, DEFAULT_CONFIG.categories)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Merge category config safely.
|
||||
* - Defaults are preserved
|
||||
* - User overrides win
|
||||
* - Custom categories are supported
|
||||
*/
|
||||
function mergeCategories(userCategories, defaultCategories) {
|
||||
const merged = {};
|
||||
// Start with defaults
|
||||
for (const key of Object.keys(defaultCategories)) {
|
||||
merged[key] = {
|
||||
...defaultCategories[key],
|
||||
...(userCategories?.[key] ?? {})
|
||||
};
|
||||
}
|
||||
// Add user-defined custom categories safely
|
||||
if (userCategories) {
|
||||
for (const key of Object.keys(userCategories)) {
|
||||
if (!merged[key]) {
|
||||
merged[key] = {
|
||||
...userCategories[key],
|
||||
enabled: userCategories[key].enabled ?? false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
15
node_modules/astro-consent/dist/index.d.ts
generated
vendored
Normal file
15
node_modules/astro-consent/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { AstroIntegration } from "astro";
|
||||
export interface AstroConsentOptions {
|
||||
siteName?: string;
|
||||
policyUrl?: string;
|
||||
consent?: {
|
||||
days?: number;
|
||||
storageKey?: string;
|
||||
};
|
||||
categories?: {
|
||||
essential?: boolean;
|
||||
analytics?: boolean;
|
||||
marketing?: boolean;
|
||||
};
|
||||
}
|
||||
export default function astroConsent(options?: AstroConsentOptions): AstroIntegration;
|
||||
173
node_modules/astro-consent/dist/index.js
generated
vendored
Normal file
173
node_modules/astro-consent/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
export default function astroConsent(options = {}) {
|
||||
const siteName = options.siteName ?? "This website";
|
||||
const policyUrl = options.policyUrl ?? "/privacy";
|
||||
const consentDays = options.consent?.days ?? 30;
|
||||
const storageKey = options.consent?.storageKey ?? "astro-consent";
|
||||
const defaultCategories = {
|
||||
essential: true,
|
||||
analytics: false,
|
||||
marketing: false,
|
||||
...options.categories
|
||||
};
|
||||
const ttl = consentDays * 24 * 60 * 60 * 1000;
|
||||
return {
|
||||
name: "astro-consent",
|
||||
hooks: {
|
||||
"astro:config:setup": ({ injectScript }) => {
|
||||
/* ─────────────────────────────────────
|
||||
LOAD USER CSS (required)
|
||||
───────────────────────────────────── */
|
||||
injectScript("head-inline", `
|
||||
(() => {
|
||||
const id = "astro-consent-css";
|
||||
if (document.getElementById(id)) return;
|
||||
const link = document.createElement("link");
|
||||
link.id = id;
|
||||
link.rel = "stylesheet";
|
||||
link.href = "/src/cookiebanner.css";
|
||||
document.head.appendChild(link);
|
||||
})();
|
||||
`);
|
||||
/* ─────────────────────────────────────
|
||||
Consent runtime (NO CSS)
|
||||
───────────────────────────────────── */
|
||||
injectScript("page", `
|
||||
(() => {
|
||||
const KEY = "${storageKey}";
|
||||
const TTL = ${ttl};
|
||||
|
||||
function now() { return Date.now(); }
|
||||
|
||||
function read() {
|
||||
try {
|
||||
const raw = localStorage.getItem(KEY);
|
||||
if (!raw) return null;
|
||||
const data = JSON.parse(raw);
|
||||
if (data.expiresAt < now()) {
|
||||
localStorage.removeItem(KEY);
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function write(categories) {
|
||||
localStorage.setItem(KEY, JSON.stringify({
|
||||
updatedAt: now(),
|
||||
expiresAt: now() + TTL,
|
||||
categories
|
||||
}));
|
||||
}
|
||||
|
||||
window.astroConsent = {
|
||||
get: read,
|
||||
set: write,
|
||||
reset() {
|
||||
localStorage.removeItem(KEY);
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
})();
|
||||
`);
|
||||
/* ─────────────────────────────────────
|
||||
Banner + modal UI (NO CSS)
|
||||
───────────────────────────────────── */
|
||||
injectScript("page", `
|
||||
(() => {
|
||||
if (window.astroConsent.get()) return;
|
||||
|
||||
const state = { ...${JSON.stringify(defaultCategories)} };
|
||||
|
||||
const banner = document.createElement("div");
|
||||
banner.id = "astro-consent-banner";
|
||||
|
||||
banner.innerHTML = \`
|
||||
<div class="cb-container">
|
||||
<div>
|
||||
<div class="cb-title">${siteName} uses cookies</div>
|
||||
<div class="cb-desc">
|
||||
Choose how your data is used.
|
||||
<a href="${policyUrl}">Learn more</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cb-actions">
|
||||
<button class="cb-manage">Manage</button>
|
||||
<button class="cb-reject">Reject</button>
|
||||
<button class="cb-accept">Accept all</button>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
|
||||
document.body.appendChild(banner);
|
||||
|
||||
banner.querySelector(".cb-accept").onclick = () => {
|
||||
window.astroConsent.set({ essential: true, analytics: true, marketing: true });
|
||||
banner.remove();
|
||||
};
|
||||
|
||||
banner.querySelector(".cb-reject").onclick = () => {
|
||||
window.astroConsent.set({ essential: true });
|
||||
banner.remove();
|
||||
};
|
||||
|
||||
banner.querySelector(".cb-manage").onclick = openModal;
|
||||
|
||||
function openModal() {
|
||||
const modal = document.createElement("div");
|
||||
modal.id = "astro-consent-modal";
|
||||
|
||||
modal.innerHTML = \`
|
||||
<div class="cb-modal">
|
||||
<h3>Cookie preferences</h3>
|
||||
|
||||
<div class="cb-row">
|
||||
<span>Essential</span>
|
||||
<strong>Always on</strong>
|
||||
</div>
|
||||
|
||||
<div class="cb-row">
|
||||
<span>Analytics</span>
|
||||
<div class="cb-toggle" data-key="analytics"><span></span></div>
|
||||
</div>
|
||||
|
||||
<div class="cb-row">
|
||||
<span>Marketing</span>
|
||||
<div class="cb-toggle" data-key="marketing"><span></span></div>
|
||||
</div>
|
||||
|
||||
<div class="cb-actions">
|
||||
<button class="cb-accept">Save preferences</button>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
modal.querySelectorAll(".cb-toggle").forEach(toggle => {
|
||||
const key = toggle.getAttribute("data-key");
|
||||
if (state[key]) toggle.classList.add("active");
|
||||
|
||||
toggle.onclick = () => {
|
||||
state[key] = !state[key];
|
||||
toggle.classList.toggle("active");
|
||||
};
|
||||
});
|
||||
|
||||
modal.querySelector(".cb-accept").onclick = () => {
|
||||
window.astroConsent.set({ essential: true, ...state });
|
||||
modal.remove();
|
||||
banner.remove();
|
||||
};
|
||||
|
||||
modal.onclick = e => {
|
||||
if (e.target === modal) modal.remove();
|
||||
};
|
||||
}
|
||||
})();
|
||||
`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
1
node_modules/astro-consent/dist/templates/cssTemplate.d.ts
generated
vendored
Normal file
1
node_modules/astro-consent/dist/templates/cssTemplate.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const DEFAULT_CSS = "\n:root {\n /* Layout */\n --cb-z-index: 9999;\n --cb-max-width: 960px;\n --cb-padding: 16px;\n --cb-gap: 12px;\n --cb-radius: 10px;\n\n /* Colours */\n --cb-bg: #111827;\n --cb-surface: #1f2933;\n --cb-text: #ffffff;\n --cb-muted: #9ca3af;\n --cb-border: #374151;\n --cb-accent: #6366f1;\n\n /* Buttons */\n --cb-btn-bg: var(--cb-accent);\n --cb-btn-text: #ffffff;\n --cb-btn-secondary-bg: transparent;\n --cb-btn-secondary-text: var(--cb-text);\n --cb-btn-border: var(--cb-border);\n\n /* Typography */\n --cb-font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n --cb-title-size: 1rem;\n --cb-text-size: 0.875rem;\n}\n\n#astro-consent-banner {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: var(--cb-z-index);\n background: var(--cb-bg);\n color: var(--cb-text);\n font-family: var(--cb-font-family);\n border-top: 1px solid var(--cb-border);\n}\n\n#astro-consent-banner > * {\n max-width: var(--cb-max-width);\n margin: 0 auto;\n padding: var(--cb-padding);\n}\n\n#astro-consent-banner h2 {\n margin: 0 0 4px;\n font-size: var(--cb-title-size);\n}\n\n#astro-consent-banner p {\n margin: 0;\n font-size: var(--cb-text-size);\n color: var(--cb-muted);\n}\n\n#astro-consent-banner a {\n color: var(--cb-accent);\n text-decoration: underline;\n}\n\n.astro-consent-categories {\n display: grid;\n gap: var(--cb-gap);\n margin-top: var(--cb-gap);\n}\n\n.astro-consent-category {\n background: var(--cb-surface);\n padding: 12px;\n border-radius: var(--cb-radius);\n border: 1px solid var(--cb-border);\n}\n\n.astro-consent-label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n}\n\n.astro-consent-description {\n margin: 4px 0 0 26px;\n font-size: 0.8rem;\n color: var(--cb-muted);\n}\n\n.astro-consent-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n margin-top: var(--cb-gap);\n flex-wrap: wrap;\n}\n\n.astro-consent-actions button {\n padding: 8px 14px;\n border-radius: var(--cb-radius);\n font-size: 0.875rem;\n cursor: pointer;\n border: 1px solid var(--cb-btn-border);\n}\n\n.astro-consent-actions button:first-child {\n background: var(--cb-btn-secondary-bg);\n color: var(--cb-btn-secondary-text);\n}\n\n.astro-consent-actions button:last-child {\n background: var(--cb-btn-bg);\n color: var(--cb-btn-text);\n border-color: var(--cb-btn-bg);\n}\n";
|
||||
117
node_modules/astro-consent/dist/templates/cssTemplate.js
generated
vendored
Normal file
117
node_modules/astro-consent/dist/templates/cssTemplate.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
export const DEFAULT_CSS = `
|
||||
:root {
|
||||
/* Layout */
|
||||
--cb-z-index: 9999;
|
||||
--cb-max-width: 960px;
|
||||
--cb-padding: 16px;
|
||||
--cb-gap: 12px;
|
||||
--cb-radius: 10px;
|
||||
|
||||
/* Colours */
|
||||
--cb-bg: #111827;
|
||||
--cb-surface: #1f2933;
|
||||
--cb-text: #ffffff;
|
||||
--cb-muted: #9ca3af;
|
||||
--cb-border: #374151;
|
||||
--cb-accent: #6366f1;
|
||||
|
||||
/* Buttons */
|
||||
--cb-btn-bg: var(--cb-accent);
|
||||
--cb-btn-text: #ffffff;
|
||||
--cb-btn-secondary-bg: transparent;
|
||||
--cb-btn-secondary-text: var(--cb-text);
|
||||
--cb-btn-border: var(--cb-border);
|
||||
|
||||
/* Typography */
|
||||
--cb-font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--cb-title-size: 1rem;
|
||||
--cb-text-size: 0.875rem;
|
||||
}
|
||||
|
||||
#astro-consent-banner {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: var(--cb-z-index);
|
||||
background: var(--cb-bg);
|
||||
color: var(--cb-text);
|
||||
font-family: var(--cb-font-family);
|
||||
border-top: 1px solid var(--cb-border);
|
||||
}
|
||||
|
||||
#astro-consent-banner > * {
|
||||
max-width: var(--cb-max-width);
|
||||
margin: 0 auto;
|
||||
padding: var(--cb-padding);
|
||||
}
|
||||
|
||||
#astro-consent-banner h2 {
|
||||
margin: 0 0 4px;
|
||||
font-size: var(--cb-title-size);
|
||||
}
|
||||
|
||||
#astro-consent-banner p {
|
||||
margin: 0;
|
||||
font-size: var(--cb-text-size);
|
||||
color: var(--cb-muted);
|
||||
}
|
||||
|
||||
#astro-consent-banner a {
|
||||
color: var(--cb-accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.astro-consent-categories {
|
||||
display: grid;
|
||||
gap: var(--cb-gap);
|
||||
margin-top: var(--cb-gap);
|
||||
}
|
||||
|
||||
.astro-consent-category {
|
||||
background: var(--cb-surface);
|
||||
padding: 12px;
|
||||
border-radius: var(--cb-radius);
|
||||
border: 1px solid var(--cb-border);
|
||||
}
|
||||
|
||||
.astro-consent-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.astro-consent-description {
|
||||
margin: 4px 0 0 26px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--cb-muted);
|
||||
}
|
||||
|
||||
.astro-consent-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
margin-top: var(--cb-gap);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.astro-consent-actions button {
|
||||
padding: 8px 14px;
|
||||
border-radius: var(--cb-radius);
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--cb-btn-border);
|
||||
}
|
||||
|
||||
.astro-consent-actions button:first-child {
|
||||
background: var(--cb-btn-secondary-bg);
|
||||
color: var(--cb-btn-secondary-text);
|
||||
}
|
||||
|
||||
.astro-consent-actions button:last-child {
|
||||
background: var(--cb-btn-bg);
|
||||
color: var(--cb-btn-text);
|
||||
border-color: var(--cb-btn-bg);
|
||||
}
|
||||
`;
|
||||
56
node_modules/astro-consent/dist/types/config.d.ts
generated
vendored
Normal file
56
node_modules/astro-consent/dist/types/config.d.ts
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
export interface CookieCategory {
|
||||
/**
|
||||
* Whether this category is enabled by default
|
||||
* (may be overridden by stored consent)
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* If true, this category cannot be disabled by the user
|
||||
* and should always be treated as enabled
|
||||
*/
|
||||
readonly?: boolean;
|
||||
/**
|
||||
* Human-readable category label
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* Optional explanation shown to the user
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Optional UI hint:
|
||||
* hide this category from the preferences UI
|
||||
*/
|
||||
hidden?: boolean;
|
||||
}
|
||||
export interface CookieBannerConfig {
|
||||
/**
|
||||
* Human-readable site name used in banner copy
|
||||
*/
|
||||
siteName: string;
|
||||
/**
|
||||
* URL to privacy / cookie policy
|
||||
*/
|
||||
policyUrl: string;
|
||||
/**
|
||||
* Consent storage configuration
|
||||
*/
|
||||
consent: {
|
||||
/**
|
||||
* Globally enable or disable the consent system
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Number of days consent remains valid
|
||||
*/
|
||||
days: number;
|
||||
/**
|
||||
* LocalStorage key used to store consent
|
||||
*/
|
||||
storageKey: string;
|
||||
};
|
||||
/**
|
||||
* Cookie categories (built-in or user-defined)
|
||||
*/
|
||||
categories: Record<string, CookieCategory>;
|
||||
}
|
||||
1
node_modules/astro-consent/dist/types/config.js
generated
vendored
Normal file
1
node_modules/astro-consent/dist/types/config.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
Reference in New Issue
Block a user