Compare commits
41 Commits
refactor/e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99f7d8f0bb | ||
|
|
85a0a54eb4 | ||
|
|
5c1d3c7ef9 | ||
|
|
da14d0d05d | ||
|
|
5b521661ec | ||
|
|
3469a09a9f | ||
|
|
5d5e12d7e7 | ||
|
|
f5af9e46e1 | ||
|
|
bb7007aa88 | ||
|
|
900ffe0f06 | ||
|
|
bef271c1a2 | ||
|
|
2996812209 | ||
|
|
d5d4574683 | ||
|
|
d93191f675 | ||
|
|
1e4fa53fb2 | ||
|
|
8346a731a6 | ||
|
|
72ace6ff50 | ||
|
|
535aefc0f3 | ||
|
|
15b4b40681 | ||
|
|
4692e89736 | ||
|
|
739e081296 | ||
|
|
5abe1edb71 | ||
|
|
31b0619c90 | ||
|
|
ea36f28c17 | ||
|
|
17f4eb752b | ||
|
|
eb6557eeb3 | ||
|
|
74db1dad77 | ||
|
|
9653fcbaf9 | ||
|
|
d8ff358dd7 | ||
|
|
d7aa7c2013 | ||
|
|
4820289252 | ||
|
|
b7931731f9 | ||
|
|
0bd480d103 | ||
|
|
21a538cbb8 | ||
|
|
d73e48351f | ||
|
|
3efaf4d661 | ||
|
|
02c7b7b29b | ||
|
|
fe442790ab | ||
|
|
bd67810a3f | ||
|
|
b34f8fc2fb | ||
|
|
7c905bdb00 |
34
.dockerignore
Normal 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
|
||||||
1
.gitignore
vendored
@@ -26,3 +26,4 @@ uploads/
|
|||||||
# Generated
|
# Generated
|
||||||
emdash-env.d.ts
|
emdash-env.d.ts
|
||||||
.hermes/
|
.hermes/
|
||||||
|
scripts/__pycache__/
|
||||||
|
|||||||
29
Dockerfile
Normal 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;"]
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { defineConfig } from 'astro/config'
|
import { defineConfig } from 'astro/config'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import react from '@astrojs/react'
|
import react from '@astrojs/react'
|
||||||
|
import sitemap from '@astrojs/sitemap'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://dealplustech.com',
|
site: 'https://dealplustech.co.th',
|
||||||
output: 'static',
|
output: 'static',
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
@@ -22,6 +23,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
react(),
|
react(),
|
||||||
|
sitemap(),
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
assets: '_assets',
|
assets: '_assets',
|
||||||
|
|||||||
109
docs/ci-setup.md
Normal 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
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
package-lock.json
generated
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "dealplustech-emdash",
|
"name": "dealplustech-astroreal",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dealplustech-emdash",
|
"name": "dealplustech-astroreal",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/react": "^5.0.5",
|
"@astrojs/react": "^5.0.5",
|
||||||
|
"@astrojs/sitemap": "^3.7.3",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"astro": "^6.1.7",
|
"astro": "^6.1.7",
|
||||||
@@ -160,6 +161,17 @@
|
|||||||
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@astrojs/sitemap": {
|
||||||
|
"version": "3.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz",
|
||||||
|
"integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sitemap": "^9.0.0",
|
||||||
|
"stream-replace-string": "^2.0.0",
|
||||||
|
"zod": "^4.3.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@astrojs/telemetry": {
|
"node_modules/@astrojs/telemetry": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz",
|
||||||
@@ -2329,8 +2341,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": ">=7.24.0 <7.24.7"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
@@ -2355,6 +2365,15 @@
|
|||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/sax": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
@@ -2562,6 +2581,12 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@@ -5748,6 +5773,40 @@
|
|||||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/sitemap": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^24.9.2",
|
||||||
|
"@types/sax": "^1.2.1",
|
||||||
|
"arg": "^5.0.0",
|
||||||
|
"sax": "^1.4.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sitemap": "dist/esm/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.5",
|
||||||
|
"npm": ">=10.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/@types/node": {
|
||||||
|
"version": "24.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
|
||||||
|
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/smol-toml": {
|
"node_modules/smol-toml": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
||||||
@@ -5779,6 +5838,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stream-replace-string": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
@@ -5980,9 +6045,7 @@
|
|||||||
"version": "7.24.6",
|
"version": "7.24.6",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||||
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "dealplustech-emdash",
|
"name": "dealplustech-astroreal",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Deal Plus Tech - ระบบน้ำคุณภาพสูง ราคาโรงงาน",
|
"description": "Deal Plus Tech - ระบบน้ำคุณภาพสูง ราคาโรงงาน",
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/react": "^5.0.5",
|
"@astrojs/react": "^5.0.5",
|
||||||
|
"@astrojs/sitemap": "^3.7.3",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"astro": "^6.1.7",
|
"astro": "^6.1.7",
|
||||||
|
|||||||
BIN
public/images/aeroflex/gallery-1.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
public/images/aeroflex/gallery-10.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/images/aeroflex/gallery-11.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/images/aeroflex/gallery-12.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/aeroflex/gallery-13.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/images/aeroflex/gallery-14.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/aeroflex/gallery-2.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/images/aeroflex/gallery-3.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/aeroflex/gallery-4.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/images/aeroflex/gallery-5.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/images/aeroflex/gallery-6.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/aeroflex/gallery-8.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/images/aeroflex/gallery-9.jpg
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
public/images/armflex/gallery-1.jpg
Normal file
|
After Width: | Height: | Size: 476 KiB |
BIN
public/images/armflex/gallery-2.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/images/armflex/gallery-3.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
public/images/armflex/gallery-4.jpg
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
public/images/armflex/gallery-5.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/images/blog/10-things-checklist-hero.jpg
Normal file
|
After Width: | Height: | Size: 265 KiB |
BIN
public/images/blog/grilles-eyeball.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/images/blog/grilles-fresh-air-types.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
public/images/blog/grilles-hing-type.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/blog/grilles-industrial.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
public/images/blog/grilles-installation.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/images/blog/grilles-linear-slot.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/blog/grilles-plastic-content.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/images/blog/ppr-pipe-hero.jpg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
public/images/blog/ppr-pipes-green.jpg
Normal file
|
After Width: | Height: | Size: 252 KiB |
BIN
public/images/blog/ppr-vs-hdpe-vs-upvc-hero.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
public/images/blog/ppr-welding-machine.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/blog/source/hdpe-cleaned.png
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
public/images/blog/source/hdpe-source.png
Normal file
|
After Width: | Height: | Size: 231 KiB |
BIN
public/images/blog/source/ppr-cleaned.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
public/images/blog/source/ppr-thaippr-source.png
Normal file
|
After Width: | Height: | Size: 354 KiB |
BIN
public/images/blog/source/ppr-tight.png
Normal file
|
After Width: | Height: | Size: 466 KiB |
BIN
public/images/blog/source/ppr-transparent.png
Normal file
|
After Width: | Height: | Size: 864 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 715 KiB |
BIN
public/images/blog/source/upvc-cleaned.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/images/blog/thermobreak-cross-section.jpg
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
public/images/blog/thermobreak-foil-detail.jpg
Normal file
|
After Width: | Height: | Size: 343 KiB |
BIN
public/images/blog/thermobreak-hero.jpg
Normal file
|
After Width: | Height: | Size: 619 KiB |
BIN
public/images/blog/thermobreak-installation.jpg
Normal file
|
After Width: | Height: | Size: 790 KiB |
BIN
public/images/blog/thermobreak-series-hero.jpg
Normal file
|
After Width: | Height: | Size: 383 KiB |
BIN
public/images/blog/thermobreak-structure.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
public/images/blog/upvc-pipe-clean.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
public/images/grilles/supply-air.jpg
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
public/images/maxflex/gallery-1.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/images/maxflex/gallery-3.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/images/maxflex/gallery-4.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/images/maxflex/gallery-5.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/images/maxflex/gallery-6.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
public/images/maxflex/gallery-7.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
public/images/maxflex/gallery-8.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/maxflex/gallery-9.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
public/images/microfiber/gallery-1.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/images/microfiber/gallery-10.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/images/microfiber/gallery-2.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/images/microfiber/gallery-3.jpg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/images/microfiber/gallery-4.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/images/microfiber/gallery-5.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/microfiber/gallery-6.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/images/microfiber/gallery-7.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/images/microfiber/gallery-8.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/images/microfiber/hero_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/images/microfiber/microfiber-enf.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/images/microfiber/microfiber-fl.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-fld.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/images/microfiber/microfiber-fls.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-fr.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-frd.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-frk.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-fsf.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/images/microfiber/microfiber-glc.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/images/microfiber/microfiber-gts.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/microfiber/microfiber-gwpf.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/images/microfiber/microfiber-hi-temp.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/images/microfiber/microfiber-pfl.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
public/images/microfiber/microfiber-pipe-cover.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/microfiber/microfiber-pln.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/images/microfiber/prod_0.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/microfiber/prod_0_0.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/images/microfiber/prod_0_1.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/images/microfiber/prod_0_2.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
public/images/microfiber/prod_0_3.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
public/images/microfiber/prod_0_4.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/images/microfiber/prod_1.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/images/microfiber/prod_10.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/images/microfiber/prod_11.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/microfiber/prod_1_0.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |