Simplified: remove Tailwind, standalone static HTML
Some checks failed
Deploy to Easypanel / deploy (push) Has been cancelled
Some checks failed
Deploy to Easypanel / deploy (push) Has been cancelled
This commit is contained in:
@@ -14,3 +14,5 @@
|
||||
{"t":0,"agent":"a6bd471","agent_type":"unknown","event":"agent_stop","success":true}
|
||||
{"t":0,"agent":"aa941d3","agent_type":"unknown","event":"agent_stop","success":true}
|
||||
{"t":0,"agent":"a4954dc","agent_type":"unknown","event":"agent_stop","success":true}
|
||||
{"t":0,"agent":"a405733","agent_type":"unknown","event":"agent_stop","success":true}
|
||||
{"t":0,"agent":"a1c51b0","agent_type":"unknown","event":"agent_stop","success":true}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"tool_name": "WebFetch",
|
||||
"tool_input_preview": "{\"url\":\"https://docs.astro.build/en/guides/deploy/easypanel/\",\"prompt\":\"How to deploy Astro to Easypanel? What type of service should be used? What port or configuration is needed for Easypanel + Astr...",
|
||||
"error": "timeout of 60000ms exceeded",
|
||||
"timestamp": "2026-04-28T00:25:40.810Z",
|
||||
"tool_name": "Bash",
|
||||
"tool_input_preview": "{\"command\":\"docker ps -a --filter \\\"ancestor=astro-tina-test\\\" --format \\\"{{.ID}} {{.Status}}\\\" 2>/dev/null && echo \\\"---\\\" && docker images astro-tina-test --format \\\"{{.ID}} {{.Created}}\\\"\"}",
|
||||
"error": "Exit code 1",
|
||||
"timestamp": "2026-04-28T02:46:35.800Z",
|
||||
"retry_count": 1
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
"total_spawned": 0,
|
||||
"total_completed": 0,
|
||||
"total_failed": 0,
|
||||
"last_updated": "2026-04-28T00:28:25.963Z"
|
||||
"last_updated": "2026-04-28T02:19:58.307Z"
|
||||
}
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,22 +1,14 @@
|
||||
FROM node:22-alpine AS builder
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM node:22-alpine AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV PORT=8080
|
||||
ENV DIST_DIR=./dist
|
||||
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/server.js .
|
||||
|
||||
EXPOSE 8080
|
||||
EXPOSE 4321
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -6,21 +6,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://example.com',
|
||||
integrations: [],
|
||||
vite: {
|
||||
define: {
|
||||
'import.meta.env.TINA_TOKEN': JSON.stringify(process.env.TINA_TOKEN || ''),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
'@components': path.resolve(__dirname, './src/components'),
|
||||
'@layouts': path.resolve(__dirname, './src/layouts'),
|
||||
'@styles': path.resolve(__dirname, './src/styles'),
|
||||
'@content': path.resolve(__dirname, './src/content'),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: 'static',
|
||||
build: {
|
||||
assets: '_assets',
|
||||
|
||||
14
nginx.conf
14
nginx.conf
@@ -1,14 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
root /app/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
17
package.json
17
package.json
@@ -2,14 +2,11 @@
|
||||
"name": "astro-tina-sample",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"description": "Astro 6 + Tina CMS sample",
|
||||
"description": "Astro 6 + Tina CMS sample for Easypanel",
|
||||
"scripts": {
|
||||
"dev": "tinacms dev --port 3001 & astro dev",
|
||||
"dev:astro": "astro dev",
|
||||
"dev:tina": "tinacms dev --port 3001",
|
||||
"dev": "astro dev --host 0.0.0.0",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -18,18 +15,14 @@
|
||||
"astro": "^6.1.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"@tinacms/cli": "^2.2.3",
|
||||
"tinacms": "^2.2.3",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
"@tailwindcss/typography": "^0.5.15"
|
||||
"@types/react-dom": "^18.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import { readFile } from 'node:fs/promises'
|
||||
import { join, extname } from 'node:path'
|
||||
import { existsSync } from 'node:fs'
|
||||
|
||||
const PORT = process.env.PORT || 8080
|
||||
const DIST_DIR = process.env.DIST_DIR || './dist'
|
||||
const PORT = process.env.PORT || 4321
|
||||
const DIST_DIR = './dist'
|
||||
|
||||
const mimeTypes = {
|
||||
'.html': 'text/html',
|
||||
@@ -63,12 +63,10 @@ const server = createServer(async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 404
|
||||
res.writeHead(404)
|
||||
res.end('Not found')
|
||||
})
|
||||
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`Server running at http://0.0.0.0:${PORT}`)
|
||||
console.log(`Serving files from ${DIST_DIR}`)
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
interface Props {
|
||||
siteName?: string
|
||||
}
|
||||
|
||||
const { siteName = "Astro Tina Starter" } = Astro.props
|
||||
---
|
||||
|
||||
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-primary-200">
|
||||
<nav class="max-w-6xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||
<a href="/" class="font-bold text-xl text-primary-900 hover:text-accent-600 transition-colors">
|
||||
{siteName}
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<a href="/" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||
Home
|
||||
</a>
|
||||
<a href="/blog" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||
Blog
|
||||
</a>
|
||||
<a href="/about" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||
About
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -1,167 +0,0 @@
|
||||
---
|
||||
const ga4Id = import.meta.env.PUBLIC_GA4_ID
|
||||
const gtmId = import.meta.env.PUBLIC_GTM_ID
|
||||
const umamiUrl = import.meta.env.PUBLIC_UMAMI_URL
|
||||
const umamiWebsiteId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID
|
||||
const clarityId = import.meta.env.PUBLIC_CLARITY_ID
|
||||
const fbPixelId = import.meta.env.PUBLIC_FB_PIXEL_ID
|
||||
const googleAdsId = import.meta.env.PUBLIC_GOOGLE_ADS_ID
|
||||
const tiktokPixelId = import.meta.env.PUBLIC_TIKTOK_PIXEL_ID
|
||||
const lineChannelId = import.meta.env.PUBLIC_LINE_CHANNEL_ID
|
||||
---
|
||||
|
||||
<!-- Google Analytics 4 -->
|
||||
{ga4Id && (
|
||||
<script
|
||||
data-consent-category="analytics"
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${ga4Id}`}
|
||||
></script>
|
||||
)}
|
||||
{ga4Id && (
|
||||
<script
|
||||
data-consent-category="analytics"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${ga4Id}');
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- Google Tag Manager -->
|
||||
{gtmId && (
|
||||
<script
|
||||
data-consent-category="analytics"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','${gtmId}');
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- Umami Analytics -->
|
||||
{umamiUrl && umamiWebsiteId && (
|
||||
<script
|
||||
data-consent-category="analytics"
|
||||
async
|
||||
src={`${umamiUrl}/script.js`}
|
||||
data-website-id={umamiWebsiteId}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- Microsoft Clarity -->
|
||||
{clarityId && (
|
||||
<script
|
||||
data-consent-category="analytics"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function(c,l,a,r,i,t,y){
|
||||
a[q]=a[q]||function(){(a[q].q=a[q].q||[]).push(arguments)};
|
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||
})(window, document, "clarity", "script", "${clarityId}");
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- Facebook Pixel -->
|
||||
{fbPixelId && (
|
||||
<script
|
||||
data-consent-category="marketing"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '${fbPixelId}');
|
||||
fbq('track', 'PageView');
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
{fbPixelId && (
|
||||
<noscript data-consent-category="marketing">
|
||||
<img
|
||||
height="1"
|
||||
width="1"
|
||||
style="display:none"
|
||||
src={`https://www.facebook.com/tr?id=${fbPixelId}&ev=PageView&noscript=1`}
|
||||
alt=""
|
||||
/>
|
||||
</noscript>
|
||||
)}
|
||||
|
||||
<!-- Google Ads Conversion -->
|
||||
{googleAdsId && (
|
||||
<script
|
||||
data-consent-category="marketing"
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${googleAdsId}`}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- TikTok Pixel -->
|
||||
{tiktokPixelId && (
|
||||
<script
|
||||
data-consent-category="marketing"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
!function (w, d, t) {
|
||||
w.TiktokAnalyticsObject = t;
|
||||
var ttq = w[t] = w[t] || [];
|
||||
ttq.methods = ["page", "track", "identify", "instances", "debug", "on", "off", "once", "ready", "alias", "group", "enableCookie", "disableCookie"];
|
||||
ttq.setAndDefer = function (t, e) { t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } };
|
||||
for (var i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
|
||||
ttq.instance = function (t) {
|
||||
var e = t.slice(0);
|
||||
return ttq.push([e]), ttq
|
||||
};
|
||||
for (var i = 0; i < ttq.methods.length; i++) {
|
||||
var e = ttq.methods[i];
|
||||
ttq[e] = ttq.instance.bind(ttq, e)
|
||||
}
|
||||
ttq.load = function (t, e) {
|
||||
var n = "https://analytics.tiktok.com/i18n/pixel/events.js";
|
||||
n = n + "?sdkid=" + t + "&lib=" + e;
|
||||
var i = d.createElement("script");
|
||||
i.type = "text/javascript";
|
||||
i.src = n;
|
||||
d.getElementsByTagName("head")[0].appendChild(i)
|
||||
};
|
||||
ttq.load("${tiktokPixelId}", "exc");
|
||||
ttq.page()
|
||||
}(window, document, 'ttq');
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
|
||||
<!-- LINE Channel Tag -->
|
||||
{lineChannelId && (
|
||||
<script
|
||||
data-consent-category="marketing"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${lineChannelId}');
|
||||
`
|
||||
}}
|
||||
></script>
|
||||
)}
|
||||
@@ -32,4 +32,4 @@ export const collections = {
|
||||
posts: postCollection,
|
||||
pages: pageCollection,
|
||||
settings: settingsCollection,
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
---
|
||||
import "@/styles/global.css"
|
||||
import TrackingScripts from "@/components/TrackingScripts.astro"
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
description?: string
|
||||
@@ -25,13 +22,8 @@ const consentApiBase = import.meta.env.PUBLIC_CONSENT_API_BASE || 'https://conse
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body class="bg-primary-50 text-primary-900 min-h-screen">
|
||||
<body>
|
||||
<slot />
|
||||
|
||||
<!-- Tracking Scripts (ConsentOS will auto-block until consent) -->
|
||||
<TrackingScripts />
|
||||
|
||||
<!-- ConsentOS - Consent Management -->
|
||||
<script
|
||||
src={`${consentApiBase}/consent-loader.js`}
|
||||
data-site-id={consentSiteId}
|
||||
@@ -39,3 +31,56 @@ const consentApiBase = import.meta.env.PUBLIC_CONSENT_API_BASE || 'https://conse
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f8fafc;
|
||||
color: #0f172a;
|
||||
}
|
||||
main {
|
||||
padding: 4rem 1.5rem;
|
||||
max-width: 64rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
p {
|
||||
font-size: 1.125rem;
|
||||
color: #475569;
|
||||
margin-bottom: 2rem;
|
||||
max-width: 42rem;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
.card {
|
||||
padding: 1.5rem;
|
||||
background: white;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.card h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.card p {
|
||||
font-size: 1rem;
|
||||
color: #475569;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,47 +1,32 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro"
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<main>
|
||||
<section class="px-6 py-24 max-w-4xl mx-auto">
|
||||
<h1 class="text-4xl md:text-5xl font-bold tracking-tight mb-6">
|
||||
Welcome to Astro Tina Starter
|
||||
</h1>
|
||||
<p class="text-lg text-primary-600 mb-8 max-w-2xl">
|
||||
A modern starter template with Astro 6, Tina CMS, Tailwind CSS 4.x,
|
||||
and Thai language support.
|
||||
</p>
|
||||
<h1>Welcome to Astro Tina Starter</h1>
|
||||
<p>A modern starter template with Astro 6, Tina CMS, and Thai language support.</p>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2">
|
||||
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||
<h2 class="text-xl font-semibold mb-3">Tina CMS</h2>
|
||||
<p class="text-primary-600">
|
||||
Self-hosted content management with schema-based editing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||
<h2 class="text-xl font-semibold mb-3">Tailwind v4</h2>
|
||||
<p class="text-primary-600">
|
||||
Latest Tailwind CSS with @tailwindcss/vite plugin.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||
<h2 class="text-xl font-semibold mb-3">ConsentOS</h2>
|
||||
<p class="text-primary-600">
|
||||
PDPA-compliant consent management with auto-blocking tracking.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||
<h2 class="text-xl font-semibold mb-3">Thai Support</h2>
|
||||
<p class="text-primary-600">
|
||||
Ready for Thai language content with Noto Sans Thai.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h2>Tina CMS</h2>
|
||||
<p>Self-hosted content management with schema-based editing.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="card">
|
||||
<h2>Astro 6</h2>
|
||||
<p>Static site generator with excellent performance.</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Static HTML</h2>
|
||||
<p>No framework dependencies at runtime.</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Thai Support</h2>
|
||||
<p>Ready for Thai language content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
</Layout>
|
||||
@@ -1,57 +0,0 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", "Noto Sans Thai", system-ui, sans-serif;
|
||||
--font-serif: "Merriweather", Georgia, serif;
|
||||
|
||||
--color-primary-50: #f8fafc;
|
||||
--color-primary-100: #f1f5f9;
|
||||
--color-primary-200: #e2e8f0;
|
||||
--color-primary-300: #cbd5e1;
|
||||
--color-primary-400: #94a3b8;
|
||||
--color-primary-500: #64748b;
|
||||
--color-primary-600: #475569;
|
||||
--color-primary-700: #334155;
|
||||
--color-primary-800: #1e293b;
|
||||
--color-primary-900: #0f172a;
|
||||
--color-primary-950: #020617;
|
||||
|
||||
--color-accent-50: #eff6ff;
|
||||
--color-accent-100: #dbeafe;
|
||||
--color-accent-200: #bfdbfe;
|
||||
--color-accent-300: #93c5fd;
|
||||
--color-accent-400: #60a5fa;
|
||||
--color-accent-500: #3b82f6;
|
||||
--color-accent-600: #2563eb;
|
||||
--color-accent-700: #1d4ed8;
|
||||
--color-accent-800: #1e40af;
|
||||
--color-accent-900: #1e3a8a;
|
||||
|
||||
--color-success-500: #22c55e;
|
||||
--color-warning-500: #f59e0b;
|
||||
--color-error-500: #ef4444;
|
||||
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--radius-xl: 1rem;
|
||||
--radius-2xl: 1.5rem;
|
||||
--radius-full: 9999px;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--color-accent-200);
|
||||
color: var(--color-primary-900);
|
||||
}
|
||||
Reference in New Issue
Block a user