Complete Astro migration - PDPA compliant website

- Migrated all pages from Next.js to Astro
- Added PDPA-compliant Privacy Policy (Thai)
- Added PDPA-compliant Terms & Conditions (Thai)
- Added Cookie Policy with disclosure (Thai)
- Implemented cookie consent banner (client-side)
- Integrated Umami Analytics placeholder
- Blog system with 3 posts
- Optimized Docker configuration for production
- Static site build (184KB, 11 pages)
- Ready for Easypanel deployment

Backup: /Users/kunthawatgreethong/Gitea/dealplustech-backup-nextjs-20260309.tar.gz
This commit is contained in:
Kunthawat Greethong
2026-03-09 18:28:01 +07:00
parent 668f69048f
commit 6402d885f9
6183 changed files with 463899 additions and 1913 deletions

345
dealplustech-astro/node_modules/astro-consent/dist/cli.cjs generated vendored Executable file
View 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");