diff --git a/.gitignore b/.gitignore
index 2575ddd..b65edcc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@ node_modules
.DS_Store
dist
src/*.js
-src/**/*.js
\ No newline at end of file
+src/**/*.js
+public/index.html
+public/main.*.css
\ No newline at end of file
diff --git a/default.conf b/default.conf
new file mode 100644
index 0000000..3f65062
--- /dev/null
+++ b/default.conf
@@ -0,0 +1,18 @@
+server {
+ listen 4173;
+
+ root /app;
+
+ location /app {
+ try_files $uri $uri/ =404;
+
+ # Cache settings for static content
+ expires 7d; # Cache static content for 7 days
+ add_header Cache-Control "public, max-age=604800, immutable";
+ }
+
+ # Additional configurations can be added here if needed
+ # For example, error handling, logging, etc.
+ error_page 404 /index.html;
+ error_page 500 502 503 504 /index.html;
+}
diff --git a/dockerfile b/dockerfile
index e46b829..88d6e52 100644
--- a/dockerfile
+++ b/dockerfile
@@ -1,6 +1,7 @@
# Build site using Node JS
FROM node:21-slim
+# Install nginx
RUN apt-get update && apt-get install -y nginx
ARG BUILD_DATE
@@ -19,6 +20,9 @@ RUN npm i
COPY . .
+# Copy the nginx config to the correct folder
+COPY default.conf /etc/nginx/conf.d/default.conf
+
ENV NODE_ENV production
ENV TITLE "My Website"
@@ -37,4 +41,4 @@ EXPOSE 4173
RUN chmod +x /app/docker-entrypoint.sh
ENTRYPOINT ["/app/docker-entrypoint.sh"]
-CMD ["npm", "run", "start"]
\ No newline at end of file
+CMD ["npm", "run", "build"]
\ No newline at end of file
diff --git a/index.html b/index.html
index 69c016f..9794b27 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
My Website
-
+
diff --git a/package-lock.json b/package-lock.json
index c97f95d..c3c06e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,14 @@
"version": "1.4.0",
"dependencies": {
"@types/node": "^20.10.5",
+ "clean-css": "^5.3.3",
"html-minifier": "^4.0.0",
"prettier": "^3.1.1",
"tailwindcss": "^3.3.6",
"typescript": "^5.3.3"
+ },
+ "devDependencies": {
+ "@types/clean-css": "^4.2.11"
}
},
"node_modules/@alloc/quick-lru": {
@@ -106,6 +110,16 @@
"node": ">= 8"
}
},
+ "node_modules/@types/clean-css": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.11.tgz",
+ "integrity": "sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "source-map": "^0.6.0"
+ }
+ },
"node_modules/@types/node": {
"version": "20.10.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
@@ -224,14 +238,14 @@
}
},
"node_modules/clean-css": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
- "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+ "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
- "node": ">= 4.0"
+ "node": ">= 10.0"
}
},
"node_modules/commander": {
@@ -408,6 +422,17 @@
"node": ">=6"
}
},
+ "node_modules/html-minifier/node_modules/clean-css": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
+ "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 4.0"
+ }
+ },
"node_modules/html-minifier/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
diff --git a/package.json b/package.json
index 8015d79..e72f2c2 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,14 @@
"version": "1.4.0",
"type": "commonjs",
"scripts": {
- "build": "tsc && node ./dist/static.js && npm run html && npm run tailwind",
- "html": "html-minifier --remove-comments --collapse-whitespace --input-dir ./dist --output-dir ./dist --file-ext html",
- "tailwind": "npx tailwindcss -i ./src/tailwind.css -o ./dist/main.css"
+ "build": "tsc && node ./dist/index.js && npm run html && npm run tailwind && npm run css-cache-break",
+ "html": "html-minifier --remove-comments --collapse-whitespace --input-dir ./public --output-dir ./public --file-ext html",
+ "tailwind": "npx tailwindcss -i ./src/tailwind.css -o ./public/main.css",
+ "css-cache-break": "node ./dist/css-cache-break.js"
},
"dependencies": {
"@types/node": "^20.10.5",
+ "clean-css": "^5.3.3",
"html-minifier": "^4.0.0",
"prettier": "^3.1.1",
"tailwindcss": "^3.3.6",
@@ -27,5 +29,8 @@
"tabWidth": 4,
"trailingComma": "es5",
"useTabs": true
+ },
+ "devDependencies": {
+ "@types/clean-css": "^4.2.11"
}
}
diff --git a/src/css-cache-break.ts b/src/css-cache-break.ts
new file mode 100644
index 0000000..ba16e0d
--- /dev/null
+++ b/src/css-cache-break.ts
@@ -0,0 +1,23 @@
+import CleanCSS from "clean-css";
+import * as path from "path";
+import { sbReadFile, sbRename, sbWriteFile } from "./shared/files";
+
+const mainCSSFilename = `main.${new Date().getTime()}.css`;
+
+const cssFileInPath = path.join(__dirname, "../", "./public", "main.css");
+const cssFileOutPath = path.join(__dirname, "../", "./public", mainCSSFilename);
+const indexFileInOutPath = path.join(__dirname, "../", "./public", "index.html");
+
+async function start(): Promise {
+ await sbRename(cssFileInPath, cssFileOutPath);
+ const css = await sbReadFile(cssFileOutPath);
+ await sbWriteFile(cssFileOutPath, new CleanCSS().minify(css).styles);
+
+ const index = await sbReadFile(indexFileInOutPath);
+ const cssLink = ``;
+ const cssLinkReplacement = ``;
+
+ await sbWriteFile(indexFileInOutPath, index.replace(cssLink, cssLinkReplacement));
+}
+
+start();
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..1a7d643
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,21 @@
+import * as path from "path";
+import { IndexPage } from "./pages/index";
+import { sbReadFile, sbWriteFile } from "./shared/files";
+import { PAGEICON, PAGETITLE } from "./variables";
+
+const indexFileInPath = path.join(__dirname, "../", "index.html");
+const indexFileOutPath = path.join(__dirname, "../", "./public", "index.html");
+
+async function start(): Promise {
+ const index = await sbReadFile(indexFileInPath);
+
+ const newText = IndexPage({ icon: PAGEICON, title: PAGETITLE });
+ const rootDiv = '';
+ const rootDivReplacement = '$1
';
+
+ const newIndex = index.replace(rootDiv, rootDivReplacement.replace("$1", newText));
+
+ await sbWriteFile(indexFileOutPath, newIndex);
+}
+
+start();
diff --git a/src/shared/files.ts b/src/shared/files.ts
new file mode 100644
index 0000000..99b868d
--- /dev/null
+++ b/src/shared/files.ts
@@ -0,0 +1,38 @@
+import * as fs from "fs/promises";
+
+export async function sbReadFile(fileName: string): Promise {
+ return new Promise(async (resolve, reject) => {
+ try {
+ const index = await fs.readFile(fileName);
+
+ return resolve(index.toString());
+ } catch (exception) {
+ console.error(`Could not read file: ${fileName}`);
+ reject(exception);
+ }
+ });
+}
+
+export async function sbWriteFile(fileName: string, contents: string): Promise {
+ return new Promise(async (resolve, reject) => {
+ try {
+ await fs.writeFile(fileName, contents);
+ return resolve(true);
+ } catch (exception) {
+ console.error(`Could not write file: ${fileName}`);
+ reject(exception);
+ }
+ });
+}
+
+export async function sbRename(fileNameOld: string, fileNameNew: string): Promise {
+ return new Promise(async (resolve, reject) => {
+ try {
+ await fs.rename(fileNameOld, fileNameNew);
+ return resolve(true);
+ } catch (exception) {
+ console.error(`Could not rename file: ${fileNameOld} to ${fileNameNew}`);
+ reject(exception);
+ }
+ });
+}
diff --git a/src/static.ts b/src/static.ts
deleted file mode 100644
index 8b832aa..0000000
--- a/src/static.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as fs from "fs/promises";
-import * as path from "path";
-import { IndexPage } from "./pages/index";
-import { PAGEICON, PAGETITLE } from "./variables";
-
-const indexFileInPath = path.join(__dirname, "../", "index.html");
-const indexFileOutPath = path.join(__dirname, "../", "./dist", "index.html");
-
-async function readIndexPage(): Promise {
- return new Promise(async (resolve, reject) => {
- try {
- const index = await fs.readFile(indexFileInPath);
-
- return resolve(index.toString());
- } catch (exception) {
- console.error("Could not read index.html file");
- reject(exception);
- }
- });
-}
-
-async function writeIndexPage(contents: string): Promise {
- return new Promise(async (resolve, reject) => {
- try {
- await fs.writeFile(indexFileOutPath, contents);
- return resolve(true);
- } catch (exception) {
- console.error("Could not write index.html file");
- reject(exception);
- }
- });
-}
-
-async function start(): Promise {
- const index = await readIndexPage();
-
- const newText = IndexPage({ icon: PAGEICON, title: PAGETITLE });
- const rootDiv = '';
- const rootDivReplacement = '$1
';
-
- const newIndex = index.replace(rootDiv, rootDivReplacement.replace("$1", newText));
-
- await writeIndexPage(newIndex);
-}
-
-start();
diff --git a/tsconfig.json b/tsconfig.json
index c7edd98..24e9c88 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,5 +8,5 @@
"resolveJsonModule": true,
"module": "CommonJS"
},
- "include": ["src/static.ts"]
+ "include": ["src/index.ts", "src/css-cache-break.ts"]
}