23 Commits

Author SHA1 Message Date
hermes
d7aa7c2013 docs(ci): rewrite setup guide for Gitea Webhook deploy trigger
The previous doc assumed a Gitea Actions workflow (which requires a
self-hosted act_runner that we don't have, hence the 'No matching
online runner with label: ubuntu-latest' error).

The actual setup is much simpler: a Gitea Webhook pointing at the
deploy endpoint. No runner, no workflow file. Setup is one-time in
Settings -> Webhooks -> Add Webhook.

Documents:
- The push -> webhook -> deploy -> EasyPanel flow
- Exact payload URL, method, content type, events
- Test Delivery verification step
- Why Gitea Actions doesn't work without act_runner
- Troubleshooting: push not triggering, build failure modes
  (nixpacks 'No start command', node 20 vs 22), and a curl recipe
  for redeploying without a code change.
2026-06-09 14:54:14 +07:00
hermes
4820289252 ci: remove Gitea Actions workflows (rely on EasyPanel auto-deploy)
The workflows were never able to run — Gitea Actions has no managed
runners the way GitHub does, and 'ubuntu-latest' isn't a label this
self-hosted Gitea instance can match. Every push to main showed the
job as 'No matching online runner with label: ubuntu-latest' in
the Actions tab.

EasyPanel already watches the 'main' branch and rebuilds on push
(Dockerfile-based, git source). The CI step was duplicative and
produced noise in the Actions tab without running anything.

If a real CI step is needed later (lint, build artifact, test), a
Gitea act_runner has to be installed on a server, registered with
labels like 'self-hosted:host:linux', and the workflow has to use
those labels. Skipping that for now.

Files removed:
  .gitea/workflows/build-and-deploy.yml
  .gitea/workflows/lint.yml
  .gitea/  (empty after removal)
2026-06-09 14:36:47 +07:00
hermes
b7931731f9 fix(responsive): mobile footer restructure + categories 1-col + overflow fix
Some checks failed
Build & Deploy to EasyPanel / build-and-deploy (push) Has been cancelled
Lint & Test / build-check (push) Has been cancelled
Three mobile issues fixed:

1. Categories section: <640px (mobile) now 1 column. Previously it was
   already 2 columns at the smallest breakpoint which made the tiles feel
   cramped. Uses Tailwind's default sm: (640px) for the 1->2 transition,
   not a custom min-[500px].

2. Footer mobile layout (<1024px): three centered rows inside the same
   max-w-7xl container.
     - Row 1: Company Info (1 col, max-w-md, centered)
     - Row 2: Quick Links + สินค้ายอดนิยม (2 cols, max-w-2xl, centered)
     - Row 3: ติดต่อเรา (1 col, max-w-sm outer, max-w-xs button column)
   The narrow inner max-widths are deliberate: a full-width row of
   contact buttons looks stretched, especially the bottom one with the
   โทรเลย / แอดไลน์ buttons.

3. Horizontal overflow bug: data-animate='fade-left' and 'fade-right'
   children sit at translateX(30px) / translateX(-30px) in their
   initial state, before the IntersectionObserver fires. On small
   viewports this pushes the page 30px wider than the viewport and
   reveals a horizontal scrollbar as soon as the user loads the page.
   Adding overflow-x-clip on <footer> keeps the off-screen translation
   contained inside the footer.
2026-06-09 14:18:54 +07:00
Kunthawat Greethong
0bd480d103 merge: bring Dockerfile Node 22 fix from main 2026-06-09 13:33:30 +07:00
hermes
21a538cbb8 fix(deploy): bump Dockerfile Node from 20-alpine to 22-alpine
Some checks failed
Build & Deploy to EasyPanel / build-and-deploy (push) Has been cancelled
Lint & Test / build-check (push) Has been cancelled
Astro 6.x requires Node >=22.12.0. The previous Dockerfile used
node:20-alpine, which the Astro CLI rejected with:

  Node.js v20.20.2 is not supported by Astro!
  Please upgrade Node.js to a supported version: ">=22.12.0"

EasyPanel pulled the change, ran the build, and failed at
`RUN npm run build`. Bumping to node:22-alpine fixes it.

Also added two 'common failure' sections to docs/ci-setup.md
covering the nixpacks 'No start command' and Node version
mismatch errors we just hit.
2026-06-09 13:33:14 +07:00
hermes
d73e48351f fix(deploy): switch from nixpacks to Dockerfile + change branch to main
Some checks failed
Build & Deploy to EasyPanel / build-and-deploy (push) Has been cancelled
Lint & Test / build-check (push) Has been cancelled
Nixpacks auto-detect could not find a 'start' script in package.json
and bailed out. Astro builds to static files in dist/ — there is no
Node server to start. Switching to a Dockerfile + nginx fixes the
'No start command could be found' error from EasyPanel.

The workflow also pointed at the source-code branch, but the panel's
git source ref for the dealplustech-astro service is 'main', so the
trigger was firing for the wrong ref. Both workflows now run on push
to main.

- Dockerfile: multi-stage node:20-alpine build + nginx:1.27-alpine serve
- nginx.conf: gzip, security headers, 1-year cache for hashed assets,
  try_files fallback for UTF-8 slugs (Astro file-based routing)
- .dockerignore: keep build context small (skip CI, docs, .gitea, IDE)
- build-and-deploy.yml + lint.yml: branch source-code -> main
- docs/ci-setup.md: corrected project + service names (customerwebsite
  / dealplustech-astro), documented the Dockerfile rationale, added a
  note for the 'Failed to sync changes' server-side error
2026-06-09 10:28:46 +07:00
hermes
3efaf4d661 ci: switch deploy trigger from generic webhook to EasyPanel tRPC API
Some checks failed
Build & Deploy to EasyPanel / build-and-deploy (push) Has been cancelled
Lint & Test / build-check (push) Has been cancelled
Replaces the old EASYPANEL_WEBHOOK_URL flow with a direct tRPC call
to the panel, using three minimal secrets the operator fills in:

  EASYPANEL_TOKEN         - bearer token from EasyPanel profile
  EASYPANEL_PROJECT_NAME  - project name in the dashboard
  EASYPANEL_SERVICE_NAME  - service name inside that project

The endpoint (https://panelwebsite.moreminimore.com/api/trpc/services.
app.deployService) is hardcoded because the panel URL does not change.
Payload uses tRPC's wrapped-json shape: {"json":{"projectName":...,
"serviceName":...}}.

The build still runs and the dist/ artifact still uploads when any
secret is empty — only the trigger step is skipped with a warning.

Also adds docs/ci-setup.md explaining the three secrets, the service
type requirement (must be 'app' / Dockerfile-based), and a curl recipe
for testing the payload shape locally before pushing.
2026-06-09 09:47:24 +07:00
hermes
02c7b7b29b ci: add Gitea Actions workflows for build + EasyPanel deploy
Some checks failed
Build & Deploy to EasyPanel / build-and-notify (push) Has been cancelled
Lint & Test / build-check (push) Has been cancelled
Two workflows under .gitea/workflows/:

- build-and-deploy.yml: on push to source-code, install deps, run
  astro build, then POST the dist/ SHA + ref to EASYPANEL_WEBHOOK_URL
  (read from repo secret) so EasyPanel redeploys. Uploads dist/ as a
  7-day artifact as a fallback. Gracefully warns if the secret is empty
  so contributors can run the build job without breaking.

- lint.yml: lightweight build check on every push + PR so syntax /
  missing-image errors surface before the deploy workflow runs.

To enable auto-deploy:
  1. Set EASYPANEL_WEBHOOK_URL secret in Gitea repo settings
  2. Configure EasyPanel service to source from this repo + branch
     source-code, build command 'npm run build', output 'dist'
2026-06-08 23:17:43 +07:00
hermes
fe442790ab feat(homepage): rework homepage + align about-us + footer popular products
## homepage (index.astro)

- Drop '500+ รายการสินค้า' from stat badges and rebuild the trust-badges
  section to show only 15+ ปีประสบการณ์, 400+ ลูกค้าทั่วประเทศ, 98%
  ลูกค้าพึงพอใจ — bigger numbers (text-5xl/6xl), no icons, counter animation
- Add 'ทำไมเลือกเรา' section (4 cards: ส่งฟรี กทม./ปริมณฑล, Lead time 1-3
  วัน, ราคาโรงงาน, ทีมช่างแนะนำ) — no icons, primary/accent border hover
- Add 'หมวดสินค้า' section (8 tiles: 7 categories + 'สินค้าทั้งหมด')
  placed after 'สินค้าแนะนำ' — each tile is a real product photo with
  dark gradient overlay and a CTA link to /all-products?filter=<id>
- Reorder: Hero → ทำไมเลือกเรา → สินค้าแนะนำ → หมวดสินค้า → Stats →
  บทความล่าสุด → CTA

## all-products (filter from URL)

- Script now reads ?filter=<id> on load and applies it without rewriting
  the URL, then on click updates both the filter and the URL via
  history.replaceState so the back/forward buttons work

## footer (BaseLayout.astro)

- Replace productLinks with the curated 6 popular products: ไทยพีพีอาร์,
  เทอร์โมเบรค, กริลแอร์, หัวจ่ายแอร์ Ball Jet, ท่อ HDPE, ท่อ Syler

## about-us

- Stats: 10+/1000+/500+ replaced with 15+ / 400+ + counter animation,
  bigger numbers, rounded-3xl + shadow (matches home)
- Why Choose Us: rebuilt with the same 4 cards + same style as home's
  'ทำไมเลือกเรา' (no icons, pill header, larger h2 + subtitle)
2026-06-08 22:56:52 +07:00
hermes
bd67810a3f fix(products): align all-products images with product-page heroes + drop dates
## all-products.astro

Remove 2 broken product links (Poloplast, PVC + fittings) and align the
remaining 24 products with the header main menu (BaseLayout.astro):

- Reorganise categories to match the 7 header categories (ท่อพีพีอาร์,
  เครื่องเชื่อมท่อ, ระบบน้ำ, อุปกรณ์ปรับอากาศ, อุปกรณ์ดับเพลิง,
  ฉนวนหุ้มท่อ, ระบบรั้ว) instead of the old ad-hoc split
- Add Ball Jet to "อุปกรณ์ปรับอากาศ" so the page matches the menu
- Re-point 6 product images to each product page's actual hero/feature
  image so the cards show what the linked page shows:
  - HDPE, Syler, HDPE welding, PPR welding, Ball Jet, Vineman

## index.astro (homepage)

Drop the <time>...</time> block from the "บทความล่าสุด" article cards
to match the blog index (no dates, just tag chips).
2026-06-08 13:03:38 +07:00
hermes
b34f8fc2fb feat(blog): Phase 5 SEO/GEO content with 5 new blog posts
Add 5 long-form Thai blog posts (1,200-2,500 words each) with SEO + GEO
optimization for the dealplustech water-systems site. Each post targets
a specific audience (contractors, engineers, project managers) and
follows a content-quality workflow: source real product specs, verify
Thai text, dedupe images, link back to product pages.

## New blog posts (src/content/blog/)
- thermobreak-guide.md (Thermobreak closed-cell insulation overview)
- plastic-grilles-guide.md (ABS plastic grilles for HVAC)
- ppr-pipe-guide.md (PPR pipe properties + heat-fusion welding)
- ppr-vs-hdpe-vs-upvc.md (3-way pipe comparison with PE80/PE100)
- thermobreak-series-guide.md (Thermobreak LS vs Solar series)
- 10-things-checklist-pipe-ordering.md (10-point pre-order checklist)

## Removed legacy posts
- pipe-knowledge.md, valve-guide.md, welcome-post.md (orphans)

## Hero images (public/images/blog/)
~20 product photos sourced from manufacturers (Thermobreak, Thai PPR,
thaiconsupply) plus Nano Banana Pro infographics. All resized to
3:2 aspect ratio per user preference. Source folder preserved for
re-derivation.

## Astro layout/SEO work
- src/components/seo/SEO.astro, JsonLd.astro (new SEO components)
- src/layouts/BaseLayout.astro, Layout.astro (OG/Twitter/JSON-LD wiring)
- src/pages/404.astro
- Product pages (8): added #pricelist anchors + schema work
- src/styles/global.css: scroll-padding for sticky-header anchors

## Automation scripts (scripts/)
- build_og_image.py (OG image builder)
- inject_faq_schema.py, inject_product_schema.py (JSON-LD injection)

## Misc
- public/robots.txt, public/images/og/default-og.jpg
- .gitignore: exclude scripts/__pycache__/
2026-06-08 12:45:32 +07:00
Kunthawat Greethong
7c905bdb00 Merge branch 'refactor/emdash-pages' into source-code
Bring in latest changes:
- feat: add price list PDFs and pricelist sections for pipe products
- fix(seo): hardcoded dealplustech.co.th URLs in JSON-LD
- refactor(ball-jet): add product page + cleanup
- fix(grilles): remove redundant hero text
- refactor: migrate blog from EmDash CMS to Astro content collections (no longer using EmDash)
- chore: remove orphan images and PDFs
2026-06-06 17:23:07 +07:00
Kunthawat Greethong
e1e285363f chore: remove orphan images and PDFs (no longer referenced)
Deleted 10 orphan image directories (HDPE, backup_originals,
portfolio/.omc, products-raw/ball-jet, products-raw/pipe-insulation,
products-raw/tevada, dukelarrsen, mech, อุปกรณ์ดับเพลิง,
อุปกรณ์ปรับอากาศ) and 3 orphan PDFs (Maxflex tube, SANWA,
novat-old) — all confirmed to have 0 references in src/.
2026-06-05 19:47:29 +07:00
Kunthawat Greethong
9ed0abb5d4 fix(seo): fix hardcoded dealplustech.co.th URLs in JSON-LD
The JSON-LD Product schema in 6 product pages had hardcoded
URLs to dealplustech.co.th, but astro.config.mjs configures the
site as dealplustech.com. When this project replaces the old
site, those URLs would 404.

Changes:
- Replace `https://dealplustech.co.th/images/...` with relative
  `/images/...` paths (resolved by Astro at build time)
- Replace `https://dealplustech.co.th/<page>` with
  `https://dealplustech.com/<page>` (canonical domain)
- Special case: vineman schema `image` referenced an .svg that
  doesn't exist locally — switch to the .jpg version that does
  (also used by the page body) at
  /images/products-raw/vineman/ระบบรั้วไวน์แมน-Vineman-e1613286324569-1024x880.jpg

Files changed:
- realflex.astro
- วาล์ว-valve.astro
- water-treatment.astro
- เครื่องเชื่อม-hdpe.astro
- ระบบรั้วไวน์แมน.astro
- เทอร์โมเบรค-thermobreak.astro
2026-06-04 19:07:20 +07:00
Kunthawat Greethong
ff4e5ae346 refactor(ball-jet): remove performance section and image
The ตารางสมรรถนะการจ่ายลม section and its image are no longer
needed. The dimensions/specs section alone is sufficient for the
product page.
2026-06-04 12:17:37 +07:00
Kunthawat Greethong
52761c3946 refactor(ball-jet): drop redundant HTML tables
The HTML tables for dimensions and airflow performance are less
complete than the user-supplied spec images (which include more
data: bigger size range, more flow rates, unit annotations).
Remove the tables — the spec images alone are clearer and more
informative for the customer.
2026-06-04 12:11:24 +07:00
Kunthawat Greethong
4abb361897 fix(ball-jet): replace copyright images with user-supplied photos
The 7 product photos in commit 47f28cd were scraped from
sapaengineer.com without copyright clearance. Replace them
with 4 user-supplied photos from the LINE album:

- ball-jet-main.jpg (478x542) — original main product photo
- ball-jet-main-hd.jpg (1764x2000) — 4x upscale for hero
- ball-jet-front.jpg — front view of the diffuser
- ball-jet-specs.jpg — dimensions + parameters table
- ball-jet-performance.jpg — airflow performance table

Page now uses:
- Hero: ball-jet-main-hd (upscaled from user's photo)
- Gallery: ball-jet-main-hd + ball-jet-front
- New section: ขนาดและพารามิเตอร์ (Dimensions & Parameters)
  - Spec image + readable HTML table (sizes 4"-10")
  - D / A / B / C / E dimensions in mm
- New section: ตารางสมรรถนะการจ่ายลม (Airflow Performance)
  - Performance image + HTML table (sizes 4"-10", 5 flow rates each)
  - Columns: Air Volume (CMH), Outlet Velocity (m/s), Pressure
    Loss (Pa), Throw Distance (m), Noise (dB(A))
2026-06-04 12:06:27 +07:00
Kunthawat Greethong
47f28cdf65 feat: add หัวจ่ายแอร์ Ball Jet product page
New product page at /หัวจ่าย-ball-jet covering Ball Spout Jet
Diffuser (JD Series) — long-throw ceiling air diffuser for
high-ceiling commercial/industrial buildings.

Content sourced from sapamaket.com, sapaengineer.com (official),
superaircool.com technical specs, and the user's brief. The page
covers:
- Hero with full feature badges (360° rotation, 45° nozzle, sizes,
  materials)
- Product description, features, technical specs, applications,
  advantages
- Image gallery (7 photos scraped from the official SAPA source at
  sapaengineer.com — public product marketing images, used to
  illustrate the product)
- 5-question FAQ
- Contact CTA

Nav updated: added หัวจ่ายแอร์ Ball Jet as the 3rd subcategory under
'อุปกรณ์ปรับอากาศ' in BaseLayout.astro.

No PDF pricelist (none provided by source). No price data displayed
(per user request — pricing to be confirmed separately).
2026-06-04 10:12:24 +07:00
Kunthawat Greethong
5f4e667c65 fix: remove text label from grilles floating card in homepage hero
The กริลแอร์ floating card image remains in the homepage hero,
but the 'กริลแอร์คุณภาพสูง' text label that previously overlaid the
card has been removed.
2026-06-04 09:46:29 +07:00
Kunthawat Greethong
238e6500a6 Revert "fix: remove 'คุณภาพสูง' from grilles hero description"
This reverts commit be565e416c.
2026-06-04 09:30:30 +07:00
Kunthawat Greethong
be565e416c fix: remove 'คุณภาพสูง' from grilles hero description 2026-06-04 09:29:06 +07:00
Kunthawat Greethong
63a58727f9 feat: add price list PDFs and pricelist sections to nav products
Copy all 12 PDF price lists from dealplustech-emdash-old (pdf-batch3
branch) into public/documents/. These were the actual price lists
referenced by the original site.

Add the missing #pricelist sections and data-price-button links in
the hero to the 4 nav products that were missing them:
- ท่อ-ppr-thai-ppr.astro → PRICE-LIST_TPPR_V28-2023.pdf
- ท่อ-ppr-scg.astro → เอสซีเจ ปี68.pdf
- ท่อ-hdpe.astro → Price List HDPE TAP.pdf
- ท่อ-xy-lent.astro → Price List XYLENT_Novat.pdf

The 6 products that already had pricelists (aeroflex, armflex, maxflex,
เครื่องเชื่อม-hdpe, เทอร์โมเบรค, เม็กกรู๊ฟ-คับปลิ้ง) are unchanged.

The BaseLayout auto-detect script now finds #pricelist on these pages
and unhides the data-price-button (ราคาสินค้า) in the hero.
2026-06-03 19:44:25 +07:00
Kunthawat Greethong
ef4b0f2e89 refactor: move blog from EmDash to Astro content collections
EmDash CMS integration is being removed. The blog content is
moved to native Astro content collections (markdown files in
src/content/blog/) which works with the static output config.

Changes:
- Remove EmDash from astro.config.mjs (revert to static output)
- Remove emdash packages from package.json/package-lock.json
- Remove seed/seed.json (was EmDash-only)
- Remove src/live.config.ts (EmDash Astro loader)
- Add src/content.config.ts (Astro content collection for blog)
- Move 3 blog posts to src/content/blog/*.md
- Update src/pages/index.astro to use getCollection('blog')
- Update src/pages/บทความ/[slug].astro to use render() from astro:content
  (Astro 6 API: render(article), not article.render())
- Update src/pages/บทความ/index.astro (blog list)
- Add .hermes/ to .gitignore

Verified:
- npm run build: 35 pages, complete in 2.50s
- / , /aeroflex, /about-us, /บทความ, /บทความ/welcome-post: all 200
2026-06-03 14:02:41 +07:00
223 changed files with 5167 additions and 5231 deletions

34
.dockerignore Normal file
View File

@@ -0,0 +1,34 @@
# Build artifacts
node_modules/
dist/
.astro/
# CI / Gitea
.gitea/
.github/
# Local dev / IDE
.DS_Store
.vscode/
.idea/
*.log
*.tmp
.env
.env.*
!.env.example
# Git
.git/
.gitignore
# Docker (don't include Docker files in the build context of themselves)
Dockerfile
.dockerignore
docker-compose*.yml
# Docs
docs/
# Misc
coverage/
*.tsbuildinfo

2
.gitignore vendored
View File

@@ -25,3 +25,5 @@ uploads/
# Generated
emdash-env.d.ts
.hermes/
scripts/__pycache__/

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
# =====================================================================
# Stage 1: Build the Astro static site
# =====================================================================
FROM node:22-alpine AS builder
WORKDIR /app
# Install deps with cache layer
COPY package*.json ./
RUN npm ci --no-audit --no-fund
# Build
COPY . .
RUN npm run build
# =====================================================================
# Stage 2: Serve with nginx
# =====================================================================
FROM nginx:1.27-alpine
# Astro outputs to ./dist by default
COPY --from=builder /app/dist /usr/share/nginx/html
# nginx config: SPA-friendly, gzip, cache headers for static assets
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,10 +1,6 @@
import { defineConfig } from 'astro/config'
import tailwindcss from '@tailwindcss/vite'
import node from '@astrojs/node'
import react from '@astrojs/react'
import emdash, { local } from 'emdash/astro'
import { sqlite } from 'emdash/db'
import { google } from 'emdash/auth/providers/google'
import { fileURLToPath } from 'url'
import path from 'path'
@@ -12,10 +8,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
export default defineConfig({
site: 'https://dealplustech.com',
output: 'server',
adapter: node({
mode: 'standalone',
}),
output: 'static',
vite: {
plugins: [tailwindcss()],
resolve: {
@@ -29,14 +22,6 @@ export default defineConfig({
},
integrations: [
react(),
emdash({
database: sqlite({ url: 'file:./data.db' }),
storage: local({
directory: './uploads',
baseUrl: '/_emdash/api/media/file',
}),
authProviders: [google()],
}),
],
build: {
assets: '_assets',

109
docs/ci-setup.md Normal file
View File

@@ -0,0 +1,109 @@
# CI/CD Setup — Deploy via Gitea Webhook
The site auto-rebuilds on every push to `main` via a Gitea **webhook**
(no Actions runner, no `.gitea/workflows/`, no act_runner required).
## How it works
```
git push origin main
Gitea Webhook (Gitea built-in, not Gitea Actions)
│ POST Content-Type: application/json
http://110.164.146.47:3000/api/deploy/<token>
│ HTTP 200 "Deploying..."
EasyPanel pulls repo, builds with Dockerfile, redeploys
```
## One-time webhook setup
In `https://git.moreminimore.com/kunthawat/dealplustech-astroreal/settings/hooks`:
1. Click **Add Webhook → Gitea**
2. Fill in:
- **Payload URL**: `http://110.164.146.47:3000/api/deploy/772d2c3a4a7d8671657c947c059bc1cdc64bd816efb7fbe2`
- **HTTP Method**: `POST`
- **Content Type**: `application/json`
- **Events**: **Push events**
- **Active**: ✓
- **Branch filter**: leave empty (or `main` to restrict)
3. Click **Add Webhook**
4. Test: click the webhook row → **Test Delivery****Push events** → confirm
**Last Response** is HTTP 200.
Done. From now on every push to main redeploys automatically.
## EasyPanel service requirements
The service on the panel side (`project=customerwebsite`,
`service=dealplustech-astro`) must be:
- **Type: `app`**
- **Source: Git**, branch `main`
- **Build type: `dockerfile`**, file `Dockerfile` at repo root
- **Port: `80`**
The Dockerfile is two-stage:
1. `node:22-alpine` — runs `npm ci` and `npm run build` to produce
`dist/`
2. `nginx:1.27-alpine` — copies `dist/` to nginx's web root and serves
it (with gzip, security headers, 1-year cache for hashed assets,
and a `try_files` fallback for Astro's UTF-8 slugs)
## Why not Gitea Actions?
Gitea Actions does **not** ship with managed runners (unlike GitHub
Actions' `ubuntu-latest`). Without a self-hosted `act_runner` registered
with matching labels, any workflow run will fail with
**"No matching online runner with label: ubuntu-latest"**.
The Gitea Webhook mechanism is built into Gitea itself — no runner
required. It fires the same events but POSTs to a URL you control,
which is the simplest possible deploy trigger.
If a real CI step is ever needed (lint, test, build artifact), install
`act_runner` on a box, register it, and write a workflow using
`runs-on: self-hosted`. See `act_runner` docs for setup.
## Troubleshooting
### Push happened, but site didn't update
1. Open the webhook row in Settings → Webhooks
2. Open **Recent Deliveries**
3. Check the most recent one:
- Status 200 → deploy endpoint received it, check EasyPanel logs
- Status != 200 → check the response body
- **No recent delivery** → webhook is not bound to this branch/event
or the push target branch doesn't match the filter
### Build fails inside EasyPanel
1. EasyPanel UI → service `dealplustech-astro` → Logs tab
2. Look for the `nixpacks` or `docker build` step
3. Common failures:
- **"No start command could be found"**: you're on `nixpacks`. Switch
build type to `dockerfile` and point at `Dockerfile` at repo root.
- **"Node.js v20.x is not supported by Astro"**: the Dockerfile is
using `node:20-alpine`. It should be `node:22-alpine` (Astro 6
requires `>=22.12.0`).
- **"Failed to sync changes"** (HTTP 500 from the deploy endpoint):
usually a panel-side state issue. Retry, or open the service in
the panel UI and check container state.
### Want to redeploy without a code change
The webhook URL itself is idempotent. You can hit it directly with:
```bash
curl -X POST \
"http://110.164.146.47:3000/api/deploy/772d2c3a4a7d8671657c947c059bc1cdc64bd816efb7fbe2"
```
A `200` response with body `Deploying...` means the panel accepted the
trigger. The actual rebuild happens in the background — check EasyPanel
UI for the progress.

55
nginx.conf Normal file
View File

@@ -0,0 +1,55 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.html;
# gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
text/xml
application/javascript
application/json
application/xml
application/xml+rss
image/svg+xml
font/ttf
font/otf;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Cache static assets aggressively (Astro hashes filenames)
location ~* \.(?:js|css|woff2?|ttf|otf|eot|svg|jpg|jpeg|png|webp|avif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# Don't cache HTML
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Thai URL slugs (Astro file-based routing produces paths with UTF-8 chars)
location / {
try_files $uri $uri/ $uri.html /index.html;
}
# healthcheck
location /healthz {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
}

4480
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"name": "dealplustech-emdash",
"name": "dealplustech-astroreal",
"type": "module",
"version": "1.0.0",
"description": "Deal Plus Tech - ระบบน้ำคุณภาพสูง ราคาโรงงาน",
@@ -7,17 +7,14 @@
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"start": "node ./dist/server/entry.mjs"
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/node": "^10.1.1",
"@astrojs/react": "^5.0.5",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0",
"astro": "^6.1.7",
"emdash": "^0.14.0",
"marked": "^18.0.3",
"react": "^19.2.6",
"react-dom": "^19.2.6",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400">
<rect width="400" height="400" fill="#2563eb"/>
<text x="200" y="100" text-anchor="middle" fill="white" font-family="sans-serif" font-size="24" font-weight="600">เครื่องเชื่อม HDPE</text>
<rect x="100" y="160" width="200" height="140" rx="15" fill="none" stroke="white" stroke-width="6"/>
<line x1="200" y1="200" x2="200" y2="260" stroke="white" stroke-width="6" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Some files were not shown because too many files have changed in this diff Show More