Compare commits
57 Commits
82ed104666
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfa8b4ba1a | ||
|
|
636b350878 | ||
|
|
48f60033ee | ||
|
|
377da259e1 | ||
|
|
0313ae80c4 | ||
|
|
b93dda19de | ||
|
|
a13e9b947d | ||
|
|
88de9ff226 | ||
|
|
53188d622b | ||
|
|
e1c36a2434 | ||
|
|
ef0b2a7415 | ||
|
|
5501d70990 | ||
|
|
c96ebb3f2d | ||
|
|
3971fe4aa4 | ||
|
|
ccec8a7c2f | ||
|
|
c643bf3b3a | ||
|
|
c589a62b25 | ||
|
|
d1df5c01d4 | ||
|
|
693c5e0904 | ||
|
|
5159445a51 | ||
|
|
8ae6f412d9 | ||
|
|
7b7c428ff8 | ||
|
|
27a56af25c | ||
|
|
97f066a9b3 | ||
|
|
93e2845b57 | ||
|
|
70298a5ffb | ||
|
|
033acab1ff | ||
|
|
9541b42bbc | ||
|
|
9c1104aa5e | ||
|
|
82bae1ec17 | ||
|
|
b1acd6aaef | ||
|
|
6ecbc30920 | ||
|
|
23574b1038 | ||
|
|
43e376d99b | ||
|
|
4424a30bee | ||
|
|
4bb73d8924 | ||
|
|
e1aaddc9e9 | ||
|
|
a2cd58e434 | ||
|
|
e44f1b176d | ||
|
|
e1d170252b | ||
|
|
df2b00a914 | ||
|
|
82e8a5fda7 | ||
|
|
ec03a10712 | ||
|
|
397bc5a29b | ||
|
|
88fcde1d62 | ||
|
|
a1c9930d49 | ||
|
|
41bf954d80 | ||
|
|
8cce63bba3 | ||
|
|
07cdc0dce3 | ||
|
|
a5b882e212 | ||
|
|
aac2bea8d9 | ||
|
|
8db13220dd | ||
|
|
3935f373e9 | ||
|
|
6d41d59e53 | ||
|
|
9cddd3da57 | ||
|
|
dbbd9e22a2 | ||
|
|
7ee311ab02 |
119
.astro/content.d.ts
vendored
@@ -15,21 +15,13 @@ declare module 'astro:content' {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'astro:content' {
|
||||
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
||||
|
||||
export type CollectionKey = keyof AnyEntryMap;
|
||||
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
|
||||
|
||||
export type ContentCollectionKey = keyof ContentEntryMap;
|
||||
export type DataCollectionKey = keyof DataEntryMap;
|
||||
export type CollectionKey = keyof DataEntryMap;
|
||||
export type CollectionEntry<C extends CollectionKey> = Flatten<DataEntryMap[C]>;
|
||||
|
||||
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
||||
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
|
||||
ContentEntryMap[C]
|
||||
>['slug'];
|
||||
|
||||
export type ReferenceDataEntry<
|
||||
C extends CollectionKey,
|
||||
@@ -38,41 +30,17 @@ declare module 'astro:content' {
|
||||
collection: C;
|
||||
id: E;
|
||||
};
|
||||
export type ReferenceContentEntry<
|
||||
C extends keyof ContentEntryMap,
|
||||
E extends ValidContentEntrySlug<C> | (string & {}) = string,
|
||||
> = {
|
||||
collection: C;
|
||||
slug: E;
|
||||
};
|
||||
|
||||
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
||||
collection: C;
|
||||
id: string;
|
||||
};
|
||||
|
||||
/** @deprecated Use `getEntry` instead. */
|
||||
export function getEntryBySlug<
|
||||
C extends keyof ContentEntryMap,
|
||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||
>(
|
||||
collection: C,
|
||||
// Note that this has to accept a regular string too, for SSR
|
||||
entrySlug: E,
|
||||
): E extends ValidContentEntrySlug<C>
|
||||
? Promise<CollectionEntry<C>>
|
||||
: Promise<CollectionEntry<C> | undefined>;
|
||||
|
||||
/** @deprecated Use `getEntry` instead. */
|
||||
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
|
||||
collection: C,
|
||||
entryId: E,
|
||||
): Promise<CollectionEntry<C>>;
|
||||
|
||||
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
|
||||
export function getCollection<C extends keyof DataEntryMap, E extends CollectionEntry<C>>(
|
||||
collection: C,
|
||||
filter?: (entry: CollectionEntry<C>) => entry is E,
|
||||
): Promise<E[]>;
|
||||
export function getCollection<C extends keyof AnyEntryMap>(
|
||||
export function getCollection<C extends keyof DataEntryMap>(
|
||||
collection: C,
|
||||
filter?: (entry: CollectionEntry<C>) => unknown,
|
||||
): Promise<CollectionEntry<C>[]>;
|
||||
@@ -84,14 +52,6 @@ declare module 'astro:content' {
|
||||
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
||||
>;
|
||||
|
||||
export function getEntry<
|
||||
C extends keyof ContentEntryMap,
|
||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||
>(
|
||||
entry: ReferenceContentEntry<C, E>,
|
||||
): E extends ValidContentEntrySlug<C>
|
||||
? Promise<CollectionEntry<C>>
|
||||
: Promise<CollectionEntry<C> | undefined>;
|
||||
export function getEntry<
|
||||
C extends keyof DataEntryMap,
|
||||
E extends keyof DataEntryMap[C] | (string & {}),
|
||||
@@ -100,15 +60,6 @@ declare module 'astro:content' {
|
||||
): E extends keyof DataEntryMap[C]
|
||||
? Promise<DataEntryMap[C][E]>
|
||||
: Promise<CollectionEntry<C> | undefined>;
|
||||
export function getEntry<
|
||||
C extends keyof ContentEntryMap,
|
||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||
>(
|
||||
collection: C,
|
||||
slug: E,
|
||||
): E extends ValidContentEntrySlug<C>
|
||||
? Promise<CollectionEntry<C>>
|
||||
: Promise<CollectionEntry<C> | undefined>;
|
||||
export function getEntry<
|
||||
C extends keyof DataEntryMap,
|
||||
E extends keyof DataEntryMap[C] | (string & {}),
|
||||
@@ -126,47 +77,52 @@ declare module 'astro:content' {
|
||||
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
||||
|
||||
/** Resolve an array of entry references from the same collection */
|
||||
export function getEntries<C extends keyof ContentEntryMap>(
|
||||
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
|
||||
): Promise<CollectionEntry<C>[]>;
|
||||
export function getEntries<C extends keyof DataEntryMap>(
|
||||
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
||||
): Promise<CollectionEntry<C>[]>;
|
||||
|
||||
export function render<C extends keyof AnyEntryMap>(
|
||||
entry: AnyEntryMap[C][string],
|
||||
export function render<C extends keyof DataEntryMap>(
|
||||
entry: DataEntryMap[C][string],
|
||||
): Promise<RenderResult>;
|
||||
|
||||
export function reference<C extends keyof AnyEntryMap>(
|
||||
export function reference<
|
||||
C extends
|
||||
| keyof DataEntryMap
|
||||
// Allow generic `string` to avoid excessive type errors in the config
|
||||
// if `dev` is not running to update as you edit.
|
||||
// Invalid collection names will be caught at build time.
|
||||
| (string & {}),
|
||||
>(
|
||||
collection: C,
|
||||
): import('astro/zod').ZodEffects<
|
||||
): import('astro/zod').ZodPipe<
|
||||
import('astro/zod').ZodString,
|
||||
C extends keyof ContentEntryMap
|
||||
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
|
||||
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
|
||||
import('astro/zod').ZodTransform<
|
||||
C extends keyof DataEntryMap
|
||||
? {
|
||||
collection: C;
|
||||
id: string;
|
||||
}
|
||||
: never,
|
||||
string
|
||||
>
|
||||
>;
|
||||
// Allow generic `string` to avoid excessive type errors in the config
|
||||
// if `dev` is not running to update as you edit.
|
||||
// Invalid collection names will be caught at build time.
|
||||
export function reference<C extends string>(
|
||||
collection: C,
|
||||
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
|
||||
|
||||
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
||||
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
|
||||
type InferEntrySchema<C extends keyof DataEntryMap> = import('astro/zod').infer<
|
||||
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
||||
>;
|
||||
|
||||
type ContentEntryMap = {
|
||||
|
||||
};
|
||||
type ExtractLoaderConfig<T> = T extends { loader: infer L } ? L : never;
|
||||
type InferLoaderSchema<
|
||||
C extends keyof DataEntryMap,
|
||||
L = ExtractLoaderConfig<ContentConfig['collections'][C]>,
|
||||
> = L extends { schema: import('astro/zod').ZodSchema }
|
||||
? import('astro/zod').infer<L['schema']>
|
||||
: any;
|
||||
|
||||
type DataEntryMap = {
|
||||
"blog": Record<string, {
|
||||
id: string;
|
||||
render(): Render[".md"];
|
||||
slug: string;
|
||||
body: string;
|
||||
body?: string;
|
||||
collection: "blog";
|
||||
data: any;
|
||||
rendered?: RenderedContent;
|
||||
@@ -174,9 +130,7 @@ declare module 'astro:content' {
|
||||
}>;
|
||||
"products": Record<string, {
|
||||
id: string;
|
||||
render(): Render[".md"];
|
||||
slug: string;
|
||||
body: string;
|
||||
body?: string;
|
||||
collection: "products";
|
||||
data: any;
|
||||
rendered?: RenderedContent;
|
||||
@@ -185,8 +139,6 @@ declare module 'astro:content' {
|
||||
|
||||
};
|
||||
|
||||
type AnyEntryMap = ContentEntryMap & DataEntryMap;
|
||||
|
||||
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
||||
infer TData,
|
||||
infer TEntryFilter,
|
||||
@@ -195,7 +147,6 @@ declare module 'astro:content' {
|
||||
>
|
||||
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
||||
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
||||
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
||||
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
||||
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
||||
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
||||
|
||||
0
.astro/content.db
Normal file
@@ -1 +1 @@
|
||||
[["Map",1,2],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.18.1","content-config-digest","2cc56fd17be92673","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://dealplustech.co.th\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"i18n\":{\"defaultLocale\":\"th\",\"locales\":[\"th\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"actionBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}"]
|
||||
[["Map",1,2],"meta::meta",["Map",3,4,5,6,7,8],"astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://dealplustech.co.th\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":false,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/dev\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":{\"backticks\":true,\"closingQuotes\":{\"double\":\"”\",\"single\":\"’\"},\"dashes\":true,\"ellipses\":true,\"openingQuotes\":{\"double\":\"“\",\"single\":\"‘\"},\"quotes\":true}},\"i18n\":{\"defaultLocale\":\"th\",\"locales\":[\"th\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"csp\":false,\"actionBodySizeLimit\":1048576,\"serverIslandBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"prerenderConflictBehavior\":\"warn\",\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"chromeDevtoolsWorkspace\":false,\"svgo\":false,\"rustCompiler\":false,\"queuedRendering\":{\"enabled\":false}},\"legacy\":{\"collectionsBackwardsCompat\":false},\"session\":{\"driver\":{\"entrypoint\":\"unstorage/drivers/fs-lite\",\"config\":{\"base\":\"/Users/kunthawatgreethong/Gitea/dealplustech-new/dealplustech-astro/node_modules/.astro/sessions\"}}}}","astro-version","6.1.2","content-config-digest","2cc56fd17be92673"]
|
||||
4
.astro/integrations/astro_db/db.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// This file is generated by Astro DB
|
||||
declare module 'astro:db' {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1773885716458
|
||||
"lastUpdateCheck": 1774930788095
|
||||
}
|
||||
}
|
||||
20
.gitignore
vendored
@@ -2,6 +2,21 @@
|
||||
public/images/
|
||||
!public/images/logo/
|
||||
!public/images/favicon.*
|
||||
!public/images/products-cropped/
|
||||
!public/images/products-misc/
|
||||
!public/images/thermobreak/
|
||||
!public/images/grilles/
|
||||
!public/images/groove-coupling/
|
||||
!public/images/mech/
|
||||
!public/images/dukelarrsen/
|
||||
!public/images/durgo-avvs/
|
||||
!public/images/pipe-coupling/
|
||||
!public/images/ppr/
|
||||
!public/images/realflex/
|
||||
!public/images/poloplast/
|
||||
!public/images/products-raw/
|
||||
!public/images/*.jpg
|
||||
!public/images/*.png
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
@@ -13,6 +28,11 @@ node_modules/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
90
AGENTS.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# dealplustech-astro/
|
||||
|
||||
**Generated:** 2026-03-27
|
||||
**Branch:** main
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
Astro 5.x Thai industrial e-commerce site (dealplustech.co.th). Migrated from WordPress. PDPA-compliant cookie consent, Umami analytics, 63 pages, 45 Thai-named routes.
|
||||
|
||||
## STRUCTURE
|
||||
|
||||
```
|
||||
dealplustech-astro/
|
||||
├── src/
|
||||
│ ├── components/ # .astro components (common/, consent/, product/, ui/)
|
||||
│ ├── content/ # Astro content collections (blog/, products/) - EMPTY
|
||||
│ ├── layouts/ # BaseLayout.astro (root HTML shell)
|
||||
│ ├── pages/ # 45 Thai-named static routes + index.astro
|
||||
│ ├── styles/ # global.css
|
||||
│ ├── images/ # ppr-tables/ (product reference images)
|
||||
│ └── lib/ # EMPTY
|
||||
├── public/
|
||||
│ ├── images/ # All product/corporate images (dist/, public/ mirrored)
|
||||
│ └── documents/ # PDF price lists (11 PDFs linked from product pages)
|
||||
├── db/ # EMPTY (SQLite/Turso for consent logs at runtime)
|
||||
├── astro.config.mjs # Astro config: site URL, i18n (th only), sitemap
|
||||
├── tailwind.config.js # Kanit font, custom colors (primary green, accent orange)
|
||||
├── Dockerfile # Multi-stage: node:20-alpine → nginx:alpine
|
||||
└── package.json # Scripts: dev, build, preview, db:push, db:seed
|
||||
```
|
||||
|
||||
## WHERE TO LOOK
|
||||
|
||||
| Task | Location | Notes |
|
||||
|------|----------|-------|
|
||||
| Global layout | `src/layouts/BaseLayout.astro` | HTML shell, SEO meta, cookie consent |
|
||||
| Homepage | `src/pages/index.astro` | 13KB - main entry |
|
||||
| Product pages | `src/pages/[thai-url]/index.astro` | 45 static routes |
|
||||
| Header/Footer | `src/components/common/` | Header.astro, Footer.astro |
|
||||
| Cookie consent | `src/components/consent/` | EMPTY - handled via astro-consent |
|
||||
| API routes | `src/pages/api/` | EMPTY - no API routes configured |
|
||||
| Content config | `src/content.config.ts` | Content collections definition |
|
||||
|
||||
## CONVENTIONS
|
||||
|
||||
- **Thai URLs only** — Never use English URLs for routes
|
||||
- **Astro components** — `.astro` files with frontmatter imports
|
||||
- **Kanit font** — Thai-optimized, set in tailwind.config.js
|
||||
- **Cookie consent** — astro-consent package, blocks analytics until accepted
|
||||
- **Umami analytics** — Requires `UMAMI_WEBSITE_ID` env var
|
||||
- **Admin password** — Required for consent log access at `/admin/consent-logs`
|
||||
|
||||
## ANTI-PATTERNS (THIS PROJECT)
|
||||
|
||||
- Do NOT add TypeScript entry points (no `src/main.ts`)
|
||||
- Do NOT use English route names — SEO depends on Thai URLs
|
||||
- Do NOT modify `dist/` directly — it's gitignored build output
|
||||
- Do NOT run crawlers from Python in production environment
|
||||
- Do NOT commit `.env` files
|
||||
|
||||
## UNIQUE STYLES
|
||||
|
||||
- **Custom Tailwind colors**: primary (green #3f8b6d), accent (orange #e35c18)
|
||||
- **PDPA compliance**: 14-disclosure privacy policy, granular cookie consent
|
||||
- **Custom animations**: fade-in, slide-up, slide-down, scale-in
|
||||
- **i18n**: Thai-only site, `prefixDefaultLocale: false`
|
||||
- **Image organization**: Mirrored `public/images/` and `dist/images/`
|
||||
|
||||
## COMMANDS
|
||||
|
||||
```bash
|
||||
cd dealplustech-astro
|
||||
|
||||
npm install # Install dependencies
|
||||
npm run dev # Dev server at localhost:4321
|
||||
npm run build # Production build → dist/
|
||||
npm run preview # Preview built site
|
||||
|
||||
# Database
|
||||
npm run db:push # Push schema to SQLite/Turso
|
||||
npm run db:seed # Seed database
|
||||
```
|
||||
|
||||
## NOTES
|
||||
|
||||
- **No tests** — `package.json` has no test script
|
||||
- **Easypanel deploy** — Auto-deploys on push to main, uses `npm install` then `npm run build`
|
||||
- **Image pipeline** — Python `image_processor.py` processes downloaded images
|
||||
- **Consent logs** — SQLite at `db/consent.db` (gitignored), or Turso if configured
|
||||
- **PDF price lists** — 11 PDFs in `public/documents/`, linked from 7 product pages with "ราคาสินค้า" section
|
||||
24
Dockerfile
@@ -1,11 +1,23 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
RUN npm install --legacy-peer-deps
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine AS runtime
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install --legacy-peer-deps
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
RUN apk add --no-cache sqlite-libs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=3000
|
||||
|
||||
CMD ["node", "dist/server/entry.mjs"]
|
||||
@@ -244,8 +244,8 @@ Edit product pages in `src/pages/[url]/index.astro`
|
||||
|
||||
**Deal Plus Tech**
|
||||
- Phone: 090-555-1415
|
||||
- Line: @dealplustech
|
||||
- Email: info@dealplustech.co.th
|
||||
- Line: @JPPSELECTION
|
||||
- Email: info@JPPSELECTION.co.th
|
||||
|
||||
## 📄 License
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import node from '@astrojs/node';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://dealplustech.co.th',
|
||||
adapter: node({
|
||||
mode: 'standalone'
|
||||
}),
|
||||
integrations: [
|
||||
tailwind({
|
||||
applyBaseStyles: true,
|
||||
}),
|
||||
sitemap(),
|
||||
],
|
||||
output: 'server',
|
||||
i18n: {
|
||||
defaultLocale: 'th',
|
||||
locales: ['th'],
|
||||
@@ -27,4 +33,4 @@ export default defineConfig({
|
||||
cssMinify: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
0
data/.gitkeep
Normal file
BIN
data/consent.db
Normal file
28
nginx.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript application/json;
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# SPA fallback - all paths serve index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
}
|
||||
2181
package-lock.json
generated
@@ -10,15 +10,18 @@
|
||||
"db:seed": "node db/seed.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/db": "^0.14.0",
|
||||
"@astrojs/sitemap": "^3.2.0",
|
||||
"@astrojs/db": "^0.20.1",
|
||||
"@astrojs/node": "^10.0.4",
|
||||
"@astrojs/sitemap": "^3.7.2",
|
||||
"@astrojs/tailwind": "^5.1.4",
|
||||
"astro": "^5.1.1",
|
||||
"astro": "^6.1.2",
|
||||
"astro-consent": "^1.0.0",
|
||||
"better-sqlite3": "^12.8.0",
|
||||
"drizzle-orm": "^0.38.2",
|
||||
"tailwindcss": "^3.4.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/node": "^22.10.2",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
|
||||
BIN
public/documents/16 Price List Armaflex 2567.pdf
Normal file
BIN
public/documents/19 Pricelist Aeroflex (update 2565).pdf
Normal file
BIN
public/documents/2026-New Update Thermobreak Price List.pdf
Normal file
BIN
public/documents/PRICE LIST_SANWA 02.02.69 1.pdf
Normal file
BIN
public/documents/PRICE-LIST_TPPR_V28-2023 [26012023].pdf
Normal file
BIN
public/documents/Price List HDPE TAP.pdf
Normal file
BIN
public/documents/Price List MECH_V13-2021 [260864](1).pdf
Normal file
BIN
public/documents/Price List_novat_18-1-64_210723_094815.pdf
Normal file
BIN
public/documents/เอสซีเจ ปี68.pdf
Normal file
BIN
public/images/HDPE-welding-crop.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-02-03.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-04.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-05.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-06.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-07.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-08.jpg
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-09.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-10.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-11.jpg
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-12.jpg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-13.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-14.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-15.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-16.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-17.jpg
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-18.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-19.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-20.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-21.jpg
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-22.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-23.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-24.jpg
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-26.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-27.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-28.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-29.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-30.jpg
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-31.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-32.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-33.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-34.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-35.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-36.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-37.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-38.jpg
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
public/images/dukelarrsen/dukelarrsen-43.jpg
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
public/images/durgo-avvs/durgo-002.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/images/durgo-avvs/durgo-003.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
public/images/durgo-avvs/durgo-004.jpg
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/images/durgo-avvs/durgo-005.jpg
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/images/durgo-avvs/durgo-006.jpg
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/images/durgo-avvs/durgo-007.jpg
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/durgo-avvs/durgo-008.jpg
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/images/durgo-avvs/durgo-009.jpg
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/durgo-avvs/durgo-010.jpg
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/durgo-avvs/durgo-011.jpg
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/images/durgo-avvs/durgo-012.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/durgo-avvs/durgo-013.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/images/durgo-avvs/durgo-014.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/images/durgo-avvs/durgo-015.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/images/durgo-avvs/durgo-016.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/images/durgo-avvs/durgo-017.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/durgo-avvs/durgo-018.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/images/durgo-avvs/durgo-019.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/durgo-avvs/durgo-020.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
public/images/durgo-avvs/durgo-021.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
public/images/durgo-avvs/durgo-022.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/images/durgo-avvs/durgo-023.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/images/durgo-avvs/durgo-024.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/images/durgo-avvs/durgo-025.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/images/durgo-avvs/durgo-026.jpg
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
public/images/durgo-avvs/durgo-027.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/images/fire-cabinet-1-crop.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/images/fire-cabinet-1-new.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/fire-cabinet-1.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/fire-cabinet-2-crop.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/images/fire-cabinet-2-new.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/images/fire-cabinet-2.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/images/fire-extinguisher-p1.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/images/fire-extinguisher-p2.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
public/images/fire-extinguisher-p3.jpg
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
public/images/grilles/air-grille-content.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |