Complete MoreminiMore Astro site migration

This commit is contained in:
Kunthawat Greethong
2026-08-09 10:31:15 +07:00
parent 491bf7b3be
commit 2885e198d7
146 changed files with 8139 additions and 2836 deletions

View File

@@ -1,5 +1,11 @@
.env
.env.*
!.env.example
node_modules
dist
.git
.DS_Store
.gitignore
*.log
npm-debug.log*
.DS_Store
.astro

11
.env.example Normal file
View File

@@ -0,0 +1,11 @@
SES_ACCESS_KEY_ID=
SES_SECRET_ACCESS_KEY=
SES_REGION=ap-southeast-1
META_ACCESS_TOKEN=
GA4_API_SECRET=
SES_FROM_EMAIL=kunthawat@moreminimore.com
CONTACT_TO_EMAIL=kunthawat@moreminimore.com
META_PIXEL_ID=418349260078648
GA4_MEASUREMENT_ID=G-74BHREDLC3
TRUST_PROXY=0
PORT=4321

View File

@@ -1,4 +1,3 @@
# Build the static Astro site
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
@@ -6,9 +5,13 @@ RUN npm ci
COPY . .
RUN npm run build
# Serve static files with a small, unprivileged web server
FROM nginxinc/nginx-unprivileged:1.27-alpine AS production
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
FROM node:22-alpine AS production
ENV NODE_ENV=production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
COPY server.js ./server.js
USER node
EXPOSE 4321
CMD ["node", "server.js"]

View File

@@ -1,6 +1,6 @@
# MoreminiMore redesign
A static Astro redesign for MoreminiMore, retaining verified service, case study, portfolio, and contact information from moreminimore.com.
An Astro website for MoreminiMore with four service landing pages and a same-origin Express contact API delivered through Amazon SES.
## Local development
@@ -13,13 +13,18 @@ npm run dev
```sh
npm run build
npm start
```
## Docker deployment
```sh
docker build -t moreminimore-site .
docker run --rm -p 8080:8080 moreminimore-site
docker run --rm --env-file .env -p 4321:4321 moreminimore-site
```
The site is served at `http://localhost:8080`.
The site is served at `http://localhost:4321`. Copy `.env.example` to `.env`. Production email delivery requires `SES_ACCESS_KEY_ID`, `SES_SECRET_ACCESS_KEY`, and `SES_REGION`; the sender and recipient both default to `kunthawat@moreminimore.com` and can be overridden with `SES_FROM_EMAIL` and `CONTACT_TO_EMAIL`.
`META_ACCESS_TOKEN` and `GA4_API_SECRET` are optional. When configured, a successful email submission also sends consent-scoped server conversions. `META_PIXEL_ID` defaults to `418349260078648` and `GA4_MEASUREMENT_ID` defaults to `G-74BHREDLC3`. Provider timeouts or failures never change the successful form response. In non-production mode, valid submissions are accepted without sending email or provider conversions so the flow can be tested safely.
`TRUST_PROXY` defaults to `0`, so forwarded client-IP headers are ignored. Behind a known reverse proxy, set it to the explicit hop count (`1``3`) that matches the deployment path; never set it to `true`. Contact requests are rate-limited per resolved client IP.

36
TRACKING.md Normal file
View File

@@ -0,0 +1,36 @@
# MoreminiMore tracking
Tracking is enabled in production builds and omitted from the Astro development server so local previews do not pollute production data. Production starts with every non-essential Google Consent Mode signal denied. No analytics, advertising, or recorder script is requested until ConsentOS emits the verified `consentos:consent-change` event with the corresponding accepted category.
## Installed tools
- ConsentOS — the loader follows a non-networking denied-default bootstrap and runs before any tracking tag. Its `analytics` category gates GA4, Umami, and the recorder; `marketing` gates Meta Pixel.
- Google Analytics 4 — measurement ID `G-74BHREDLC3`.
- Meta Pixel — pixel ID `418349260078648`, including PageView event IDs.
- Umami Analytics — website ID `b2e87a6c-0b64-43c8-bb09-e406ffca0af1`.
- Umami Recorder — 15% sample rate, strict masking, five-minute maximum sessions. `.contact-form` is excluded through both the recorder block selector and rrweb's `rr-block` class.
If a visitor withdraws analytics or marketing consent after a tag was loaded, the page reloads after ConsentOS stores the new choice. The reloaded page stays tag-free for the withdrawn category. The Meta noscript fallback is intentionally omitted because it cannot be safely consent-gated without JavaScript.
## Events
| Event | Trigger | Properties |
| --- | --- | --- |
| `page_view` / `PageView` | Each production page load | Automatic page data |
| `contact_clicked` / `Contact` | Phone, email, or LINE link | `method`, `location` |
| `service_selected` / `ViewContent` | Service landing-page link | `service_path`, `location` |
| `form_started` | First focus inside a contact form | `form_location` |
| `form_submitted` / `Lead` | Contact API confirms a successful submission | `form_location`, shared random event ID for Meta deduplication |
No names, phone numbers, email addresses, form values, or other personally identifiable information are included in these client-side events.
## Contact-form tracking
Each page uses the same short contact form. `form_started` is emitted once on the first focus. The client creates a UUID event ID for each validated submission and sends a boolean snapshot of the already-applied ConsentOS state. The same ID is retained across network or ambiguous-response retries; it rotates only after confirmed success or an actual edit to the form contents. `form_submitted` and the Meta standard `Lead` browser event are emitted only after `/api/contact` returns success. The browser event contains only the page or service location and reuses the same event ID as Meta CAPI for deduplication.
The contact endpoint sends email through Amazon SES first. Only after SES succeeds:
- With marketing consent and `META_ACCESS_TOKEN`, Meta CAPI receives `Lead`, the shared event ID, server IP/user agent, validated `_fbp`/`_fbc`, and normalized name/contact values hashed with SHA-256. Thai phone numbers are normalized to country code `66` before hashing. Raw form values are not sent to Meta.
- With analytics consent, `GA4_API_SECRET`, and a safely retrieved GA client ID, Google Measurement Protocol receives `form_submitted`, session/context identifiers, explicit advertising consent state, and no PII, hashes, or user properties. The browser helper skips its GA copy to avoid a duplicate event; Umami still receives the PII-free event.
Provider requests have short timeouts and are handled with `Promise.allSettled`, so missing tokens, upstream errors, or timeouts never change a successful form response. There is no public conversion relay route. Event IDs are UUID-v4 validated and retained in a bounded 15-minute pending/completed registry. Concurrent pending requests receive a processing response; a retry of a completed delivery receives success without sending a second email, while failed SES attempts release the ID for retry. Delivery and provider logs contain status only and never include form values, credentials, tokens, or upstream response bodies.

View File

@@ -5,5 +5,5 @@ export default defineConfig({
site: 'https://moreminimore.com',
output: 'static',
integrations: [sitemap()],
build: { format: 'file' },
build: { format: 'directory' },
});

5669
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,23 @@
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {"dev": "astro dev", "build": "astro check && astro build", "preview": "astro preview"},
"dependencies": {"@astrojs/sitemap": "^3.2.1", "astro": "^5.10.0"},
"devDependencies": {"@astrojs/check": "^0.9.4", "typescript": "^5.8.3"}
"scripts": {
"dev": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"start": "node server.js"
},
"dependencies": {
"@aws-sdk/client-sesv2": "^3.883.0",
"@astrojs/sitemap": "^3.2.1",
"astro": "^7.1.1",
"express": "^5.1.0",
"express-rate-limit": "^8.2.1",
"gsap": "^3.15.0",
"nodemailer": "^9.0.3"
},
"devDependencies": {
"@astrojs/check": "^0.9.4",
"typescript": "^5.8.3"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

21
public/llms.txt Normal file
View File

@@ -0,0 +1,21 @@
# MoreminiMore
MoreminiMore ให้คำปรึกษาและบริการด้านเว็บไซต์ การตลาดออนไลน์ ระบบอัตโนมัติ และ AI สำหรับ SME โดยเริ่มจากโจทย์ธุรกิจและข้อมูลจริง
## ข้อมูลหลัก
- หน้าแรก: https://moreminimore.com/
- เกี่ยวกับเรา: https://moreminimore.com/about/
- บริการทั้งหมด: https://moreminimore.com/services/
- Website Development: https://moreminimore.com/services/website-development/
- Marketing Consult: https://moreminimore.com/services/marketing-consult/
- Automation Workflow: https://moreminimore.com/services/automation-workflow/
- AI Consult: https://moreminimore.com/services/ai-consult/
- คำถามที่พบบ่อย: https://moreminimore.com/faq/
- บทความ: https://moreminimore.com/blog/
- นโยบายความเป็นส่วนตัว: https://moreminimore.com/privacy/
- เงื่อนไขการใช้งาน: https://moreminimore.com/terms/
## ติดต่อ
- อีเมล: contact@moreminimore.com
- โทรศัพท์: +66 80 995 5945
- LINE: https://line.me/R/ti/p/@moreminimore

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