Reorganize repository: Move Hugo site to docker/showerloop/public, clean up root directory, update Docker configuration to use Caddy, add Woodpecker CI config
|
@ -1,3 +1,4 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
public
|
||||
public/public
|
||||
temp_backup
|
|
@ -0,0 +1,113 @@
|
|||
# build:1
|
||||
labels:
|
||||
location: manager
|
||||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
partial: false
|
||||
depth: 1
|
||||
when:
|
||||
branch: [main, production]
|
||||
steps:
|
||||
# Build Step for staging Branch
|
||||
build-staging:
|
||||
name: build-staging
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USER
|
||||
REGISTRY_PASSWORD:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- echo "Building application for staging branch"
|
||||
- echo "$${REGISTRY_PASSWORD}" | docker login -u "$${REGISTRY_USER}" --password-stdin git.nixc.us
|
||||
- echo compose build
|
||||
- docker compose -f docker-compose.staging.yml pull --ignore-buildable
|
||||
- docker compose -f docker-compose.staging.yml build --pull
|
||||
when:
|
||||
event: push
|
||||
|
||||
deploy-new:
|
||||
name: deploy-new
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USER
|
||||
REGISTRY_PASSWORD:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- echo "$${REGISTRY_PASSWORD}" | docker login -u "$${REGISTRY_USER}" --password-stdin git.nixc.us
|
||||
- echo compose push
|
||||
- docker compose -f docker-compose.staging.yml push
|
||||
- docker stack deploy --with-registry-auth -c ./stack.staging.yml $${CI_REPO_NAME}-staging
|
||||
when:
|
||||
event: push
|
||||
|
||||
cleanup-staging:
|
||||
name: cleanup-staging
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USER
|
||||
REGISTRY_PASSWORD:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- for i in {1..5}; do docker stack rm ${CI_REPO_NAME}-staging && break || sleep 10; done
|
||||
- docker compose -f docker-compose.staging.yml down
|
||||
- docker compose -f docker-compose.staging.yml rm -f
|
||||
when:
|
||||
event: push
|
||||
|
||||
build-push-production:
|
||||
name: build-push-production
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USER
|
||||
REGISTRY_PASSWORD:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- echo "Building application for production branch"
|
||||
- echo "$${REGISTRY_PASSWORD}" | docker login -u "$${REGISTRY_USER}" --password-stdin git.nixc.us
|
||||
- echo compose build
|
||||
- docker compose -f docker-compose.production.yml pull --ignore-buildable
|
||||
- docker compose -f docker-compose.production.yml build --pull
|
||||
- docker compose -f docker-compose.production.yml push
|
||||
when:
|
||||
branch: main
|
||||
event: push
|
||||
|
||||
deploy-production:
|
||||
name: deploy-production
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USER
|
||||
REGISTRY_PASSWORD:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- echo "$${REGISTRY_PASSWORD}" | docker login -u "$${REGISTRY_USER}" --password-stdin git.nixc.us
|
||||
- docker stack deploy --with-registry-auth -c ./stack.production.yml $${CI_REPO_NAME}
|
||||
when:
|
||||
branch: main
|
||||
event: push
|
||||
|
||||
post-deploy-smoke-tests-git-nixc-us:
|
||||
name: run-post-deploy-smoke-tests-git-nixc-us
|
||||
image: git.nixc.us/colin/playwright:latest
|
||||
environment:
|
||||
BASE_URL: "https://git.nixc.us"
|
||||
when:
|
||||
branch: production
|
||||
event: push
|
|
@ -1,68 +0,0 @@
|
|||
# CSS and JavaScript Optimization Guide
|
||||
|
||||
This document explains how to eliminate unused CSS and JavaScript from the ShowerLoop website.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install --legacy-peer-deps
|
||||
```
|
||||
|
||||
2. Build the production version with optimizations:
|
||||
```bash
|
||||
./build-production.sh
|
||||
```
|
||||
|
||||
## What This Does
|
||||
|
||||
### CSS Optimization
|
||||
|
||||
The build script optimizes CSS in two ways:
|
||||
|
||||
1. **CSSO Optimization**: Uses CSSO to remove unused selectors, merge similar rules, and minify the CSS.
|
||||
- Typically reduces CSS by 5-30% depending on the file
|
||||
- Maintains full functionality while eliminating bloat
|
||||
- Works without requiring a running website
|
||||
|
||||
2. **File Path Updates**: Automatically updates all HTML files to reference the optimized CSS versions.
|
||||
|
||||
### JavaScript Optimization
|
||||
|
||||
JavaScript optimization happens through:
|
||||
|
||||
1. **Tree Shaking**: Rollup analyzes your code to detect which parts are actually used and removes dead code.
|
||||
- Eliminates unused imports and functions
|
||||
- Reduces JavaScript file sizes significantly
|
||||
|
||||
2. **Code Splitting**: JavaScript is split into separate bundles:
|
||||
- app.modern.min.js - Main application code
|
||||
- video-init.modern.min.js - Video player initialization
|
||||
- skip-to-content.modern.min.js - Accessibility features
|
||||
|
||||
3. **Minification**: All JavaScript is minified to reduce file size.
|
||||
|
||||
## Optimization Results
|
||||
|
||||
In our tests, the optimization achieves:
|
||||
|
||||
| File Type | Size Reduction |
|
||||
|-----------|---------------|
|
||||
| CSS | 5-30% |
|
||||
| JavaScript| 30-60% |
|
||||
|
||||
## How to Keep It Optimized
|
||||
|
||||
1. **Maintain Source Files**: Keep original JavaScript source files in `src/js/`
|
||||
|
||||
2. **Run Build Before Deployment**: Always run `./build-production.sh` before deploying to production
|
||||
|
||||
3. **Add New Components Wisely**: When adding new CSS/JS, consider if it's truly needed or if existing code can be reused
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Missing Styles**: If elements look unstyled after optimization, check the original CSS files and update your HTML accordingly.
|
||||
|
||||
- **JavaScript Errors**: If functionality breaks, check the browser console for errors and ensure all required code is in your source files.
|
||||
|
||||
- **Build Errors**: Make sure all dependencies are installed with `npm install --legacy-peer-deps` before running the build scripts.
|
21
LICENSE
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Shower-Loop
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
35
README.md
|
@ -1,27 +1,34 @@
|
|||
# website
|
||||
https://showerloop.cc new site for https://showerloop.org [planned]
|
||||
# ShowerLoop Website
|
||||
|
||||
## Local Development
|
||||
This repository contains the source code for the ShowerLoop website, built with Hugo and served via Docker.
|
||||
|
||||
To test this site locally:
|
||||
## Repository Structure
|
||||
|
||||
1. Make sure you have [Hugo](https://gohugo.io/installation/) installed on your system
|
||||
2. Clone this repository
|
||||
3. Run the development server using the script:
|
||||
- `/docker/showerloop/` - Container configuration for the website
|
||||
- `/docker/showerloop/public/` - Hugo website source files
|
||||
- `/docker/showerloop/Dockerfile` - Docker image definition
|
||||
- `/docker/showerloop/Caddyfile` - Caddy server configuration
|
||||
|
||||
## Development
|
||||
|
||||
To run the Hugo development server:
|
||||
|
||||
```bash
|
||||
cd docker/showerloop/public
|
||||
./run-hugo-server.sh
|
||||
```
|
||||
|
||||
Or run Hugo directly:
|
||||
The development server will be available at http://localhost:1313/
|
||||
|
||||
## Building
|
||||
|
||||
To build the production site:
|
||||
|
||||
```bash
|
||||
hugo server -D --disableFastRender
|
||||
cd docker/showerloop/public
|
||||
./build-production.sh
|
||||
```
|
||||
|
||||
4. Open your browser and go to http://localhost:1313/ to view the site
|
||||
5. The site will automatically reload when you make changes to the content or templates
|
||||
## Deployment
|
||||
|
||||
### Notes
|
||||
- The `-D` flag enables draft content to be visible in the development environment
|
||||
- `--disableFastRender` ensures full rebuilds for more reliable preview
|
||||
The site is automatically built and deployed using Woodpecker CI when changes are pushed to the master branch.
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Categories on</title><link>http://localhost:54386/categories/</link><description>Recent content in Categories on</description><generator>Hugo</generator><language>en-us</language><atom:link href="http://localhost:54386/categories/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
|
@ -1,29 +0,0 @@
|
|||
<!doctype html><html lang=en><head><script src="/livereload.js?mindelay=10&v=2&port=53498&path=livereload" data-no-instant defer></script><title>Support the Project | ShowerLoop</title>
|
||||
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="ShowerLoop - Real-time filtration, purification, recycling & heat recovery system for showers. Open source and sustainable water conservation technology."><script>let liveReloadSocket=null;const OriginalWebSocket=window.WebSocket;window.WebSocket=function(e,t){if(e.includes("/__livereload")){if(document.readyState==="complete"){liveReloadSocket=new OriginalWebSocket(e,t);const n={onmessage:null,onclose:null};return Object.defineProperty(liveReloadSocket,"onmessage",{set:function(e){n.onmessage=e},get:function(){return n.onmessage}}),Object.defineProperty(liveReloadSocket,"onclose",{set:function(e){n.onclose=e},get:function(){return n.onclose}}),liveReloadSocket.addEventListener("message",function(e){n.onmessage&&n.onmessage(e)}),liveReloadSocket.addEventListener("close",function(e){liveReloadSocket=null,n.onclose&&n.onclose(e)}),liveReloadSocket}return{send:function(){},close:function(){},addEventListener:function(){},removeEventListener:function(){},set onmessage(e){},set onclose(e){}}}return new OriginalWebSocket(e,t)};for(const e in OriginalWebSocket)OriginalWebSocket.hasOwnProperty(e)&&(window.WebSocket[e]=OriginalWebSocket[e]);window.WebSocket.prototype=OriginalWebSocket.prototype,document.addEventListener("pageshow",function(e){if(e.persisted){console.log("Page restored from bfcache");const e=window.location.protocol==="https:"?"wss:":"ws:";liveReloadSocket=new OriginalWebSocket(`${e}//${window.location.host}/__livereload`),liveReloadSocket.onmessage=function(e){e.data==="reload"&&window.location.reload()}}}),window.addEventListener("pagehide",function(){liveReloadSocket&&(liveReloadSocket.onclose=null,liveReloadSocket.close(),liveReloadSocket=null)})</script><link rel=preload href=/css/vendor/material-icons.css as=style><link rel=preload href=/images/logo2.webp as=image><link rel=stylesheet href=/css/vendor/material-icons.css><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css></noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css></noscript><script type=module>
|
||||
|
||||
import * as utils from '/js/utils.modern.min.js';
|
||||
window.utilsModule = utils;
|
||||
</script><script type=module src=/js/app.modern.min.js defer></script><script type=module src=/js/skip-to-content.modern.min.js defer></script><script type=module src=/js/material.modern.min.js defer></script><script nomodule src=/js/app.min.js defer></script><script nomodule src=/js/skip-to-content.min.js defer></script><script nomodule src=/js/material.min.js defer></script><link rel=stylesheet type=text/css href=/css/app.min.css><link rel=stylesheet type=text/css href=/css/custom.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/custom.css></noscript></head><body class=page><div class=skip-to-content role=button tabindex=0>Skip to Content</div><style>.mdl-navigation .mdl-button.mdl-navigation__link{display:flex;align-items:center;justify-content:center;height:36px;line-height:36px;padding:0 16px;margin:8px 0}.logo{height:50px;width:auto;max-width:150px;transition:none!important}</style><div class="mdl-layout mdl-js-layout
|
||||
mdl-layout--fixed-header"><header class="mdl-layout__header site-header"><div class=mdl-layout__header-row><a href=/ class=mdl-layout-title><img class=logo src=/images/logo2.webp height=50 width=auto alt="ShowerLoop Logo"></a><div class=mdl-layout-spacer></div><nav class="mdl-navigation mdl-layout--large-screen-only"><a class=mdl-navigation__link href=/ title=Home>Home</a>
|
||||
<a class=mdl-navigation__link href=/how-it-works/ title="How It Works">How It Works</a>
|
||||
<a class=mdl-navigation__link href=/research/ title=Research>Research</a>
|
||||
<a class=mdl-navigation__link href=/posts/ title=Posts>Posts</a>
|
||||
<a class=mdl-navigation__link href=/components/ title=Components>Components</a>
|
||||
<a class="mdl-navigation__link mdl-button mdl-js-button mdl-button--raised mdl-button--colored" href=/make-it/ title="Make It">Make It</a></nav></div></header><div class=mdl-layout__drawer><span class=mdl-layout-title><strong>ShowerLoop</strong></span><nav class=mdl-navigation><a class=mdl-navigation__link href=/ title=Home tabindex=0>Home</a>
|
||||
<a class=mdl-navigation__link href=/how-it-works/ title="How It Works" tabindex=0>How It Works</a>
|
||||
<a class=mdl-navigation__link href=/research/ title=Research tabindex=0>Research</a>
|
||||
<a class=mdl-navigation__link href=/posts/ title=Posts tabindex=0>Posts</a>
|
||||
<a class=mdl-navigation__link href=/components/ title=Components tabindex=0>Components</a>
|
||||
<a class="mdl-navigation__link mdl-button mdl-js-button mdl-button--raised mdl-button--colored" href=/make-it/ title="Make It" tabindex=0>Make It</a></nav></div><main aria-role=main><div class=subpage-content><div class=chocolate-container><h1>Support the Project</h1><div class=mdl-grid><section class="mdl-cell mdl-cell--12-col"><p>If you want to support the ShowerLoop project, check out our <a href=/make-it>Make-It Button</a> where you can contribute to our ongoing development.</p><pre><code> <p>We work with limited resources and recycle many materials. With access to workspace and tools (thank you, Fablab!), we're able to continue developing this sustainable technology.</p>
|
||||
|
||||
<p>We're collaborating with people around the world to bring ShowerLoop to various environments - boats, RVs, houses in remote locations, Pacific Islands, eco-friendly buildings, and historic structures without modern plumbing. The possibilities are endless, and together we hope to realize them all.</p>
|
||||
|
||||
<p>Our team is developing a business model that fosters community around social and environmental ethics within the open source circular economy.</p>
|
||||
|
||||
<div class="mdl-grid" style="justify-content: center; margin-top: 30px;">
|
||||
<a class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" href="/make-it">
|
||||
Support the Project
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</code></pre></div></div></div></main><footer class="mdl-mini-footer site-footer"><div class=mdl-mini-footer__left-section>© 2020 Shower Loop | All Rights Reserved</div><div class=mdl-mini-footer__right-section></div></footer></div><script>document.addEventListener("DOMContentLoaded",function(){setTimeout(function(){if(!window.__bfcacheLiveReloadActive){const e=window.location.protocol==="https:"?"wss:":"ws:",t=`${e}//${window.location.host}/__livereload`;try{if(window.OriginalWebSocket){const e=new window.OriginalWebSocket(t);e.onmessage=function(e){e.data==="reload"&&window.location.reload()},window.__bfcacheLiveReloadActive=!0}}catch(e){console.warn("LiveReload connection error:",e)}}},500)})</script></body></html>
|
|
@ -0,0 +1,7 @@
|
|||
services:
|
||||
showerloop:
|
||||
build:
|
||||
context: docker/showerloop
|
||||
dockerfile: Dockerfile.production
|
||||
pull: true
|
||||
image: git.nixc.us/colin/showerloop-cc:production
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
showerloop:
|
||||
build:
|
||||
context: docker/showerloop
|
||||
pull: true
|
||||
image: git.nixc.us/colin/showerloop-cc:staging
|
|
@ -0,0 +1 @@
|
|||
FROM git.nixc.us/colin/showerloop-cc:staging
|
|
@ -0,0 +1,87 @@
|
|||
# Template: Caddyfile.override
|
||||
# Purpose: Default configuration for custom containers.
|
||||
# Description:
|
||||
# - Serves static files from /srv.
|
||||
# - Provides a /health endpoint for health checks.
|
||||
# - Designed to run behind a reverse proxy like Træfik, listening only on port 80.
|
||||
# - comes with security headers
|
||||
|
||||
:80 {
|
||||
# Health check endpoint
|
||||
respond /health "OK" 200
|
||||
|
||||
# Enable compression for text-based resources
|
||||
encode gzip zstd
|
||||
|
||||
# Security headers
|
||||
header {
|
||||
# Cross-Origin headers
|
||||
Cross-Origin-Embedder-Policy "require-corp"
|
||||
Cross-Origin-Opener-Policy "same-origin"
|
||||
Cross-Origin-Resource-Policy "same-origin"
|
||||
|
||||
# Permissions Policy
|
||||
Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()"
|
||||
|
||||
# Referrer Policy
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
|
||||
# HSTS
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
|
||||
# Content Type Options
|
||||
X-Content-Type-Options "nosniff"
|
||||
|
||||
# XSS Protection
|
||||
X-XSS-Protection "1; mode=block"
|
||||
|
||||
# Frame Options (prevents clickjacking)
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
|
||||
# Frame ancestors (prevents embedding in other sites)
|
||||
Content-Security-Policy "frame-ancestors 'none'"
|
||||
|
||||
# Remove Server header
|
||||
-Server
|
||||
}
|
||||
|
||||
# Cache control for static assets - images, fonts, etc.
|
||||
@staticAssets {
|
||||
path *.jpg *.jpeg *.png *.webp *.avif *.gif *.ico *.svg *.woff *.woff2 *.ttf *.eot
|
||||
method GET HEAD
|
||||
}
|
||||
header @staticAssets Cache-Control "public, max-age=31536000, immutable"
|
||||
header @staticAssets ?Access-Control-Allow-Origin *
|
||||
|
||||
# Special handling for CSS and JS files
|
||||
@cssAndJs {
|
||||
path *.css *.js
|
||||
method GET HEAD
|
||||
}
|
||||
header @cssAndJs Cache-Control "public, max-age=31536000, immutable"
|
||||
|
||||
# Cache HTML files but for a shorter period
|
||||
@htmlFiles {
|
||||
path *.html
|
||||
method GET HEAD
|
||||
}
|
||||
header @htmlFiles Cache-Control "public, max-age=86400, must-revalidate"
|
||||
|
||||
# Static file server
|
||||
file_server {
|
||||
root /srv # Root directory for serving static files
|
||||
}
|
||||
|
||||
# Restrict allowed methods to only GET and HEAD
|
||||
@staticRequests {
|
||||
method GET HEAD
|
||||
}
|
||||
|
||||
handle @staticRequests {
|
||||
root * /srv
|
||||
file_server
|
||||
}
|
||||
|
||||
# Handle all other methods
|
||||
respond "Method Not Allowed" 405
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
# Stage 1: Build Hugo site
|
||||
FROM alpine:latest AS hugo-builder
|
||||
|
||||
# Install necessary dependencies (Hugo, Git, Node.js, and npm)
|
||||
RUN apk add --no-cache hugo git nodejs npm
|
||||
|
||||
# Copy our enhanced Caddyfile
|
||||
COPY Caddyfile.default.template /etc/caddy/Caddyfile.override
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /site
|
||||
|
||||
# Copy the Hugo source files
|
||||
COPY public/ /site
|
||||
|
||||
# Install PostCSS and its dependencies locally and update browserslist
|
||||
RUN cd /site && npm init -y && \
|
||||
npm install --save-dev postcss postcss-cli autoprefixer && \
|
||||
npm install --save-dev caniuse-lite && \
|
||||
npm update caniuse-lite browserslist
|
||||
|
||||
# Build the Hugo site for production with optimizations
|
||||
# Disable GitInfo, enable minification, and set production environment
|
||||
RUN mkdir /public && \
|
||||
cd /site && \
|
||||
HUGO_ENABLEGITINFO=false \
|
||||
HUGO_ENV=production \
|
||||
npm run build:prod && \
|
||||
cp -r /site/public/* /public/
|
||||
|
||||
# Stage 2: Production image with prebuilt static files
|
||||
FROM git.nixc.us/colin/container-base:production-nixiusstatic
|
||||
|
||||
# Copy the built site from the first stage
|
||||
COPY --from=hugo-builder /public /srv
|
||||
|
||||
# Copy our enhanced Caddyfile for development
|
||||
COPY Caddyfile.default.template /etc/caddy/Caddyfile.override
|
||||
|
||||
# Add health check endpoint for production monitoring
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -q --spider http://localhost/healthcheck.txt || exit 1
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Categories on</title><link>http://localhost:1313/categories/</link><description>Recent content in Categories on</description><generator>Hugo</generator><language>en-us</language><atom:link href="http://localhost:1313/categories/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
|
@ -1,4 +1,4 @@
|
|||
<!doctype html><html lang=en><head><script src="/livereload.js?mindelay=10&v=2&port=54386&path=livereload" data-no-instant defer></script><title>Components | ShowerLoop</title>
|
||||
<!doctype html><html lang=en><head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script><title>Components | ShowerLoop</title>
|
||||
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="ShowerLoop - Real-time filtration, purification, recycling & heat recovery system for showers. Open source and sustainable water conservation technology."><script>let liveReloadSocket=null;const OriginalWebSocket=window.WebSocket;window.WebSocket=function(e,t){if(e.includes("/__livereload")){if(document.readyState==="complete"){liveReloadSocket=new OriginalWebSocket(e,t);const n={onmessage:null,onclose:null};return Object.defineProperty(liveReloadSocket,"onmessage",{set:function(e){n.onmessage=e},get:function(){return n.onmessage}}),Object.defineProperty(liveReloadSocket,"onclose",{set:function(e){n.onclose=e},get:function(){return n.onclose}}),liveReloadSocket.addEventListener("message",function(e){n.onmessage&&n.onmessage(e)}),liveReloadSocket.addEventListener("close",function(e){liveReloadSocket=null,n.onclose&&n.onclose(e)}),liveReloadSocket}return{send:function(){},close:function(){},addEventListener:function(){},removeEventListener:function(){},set onmessage(e){},set onclose(e){}}}return new OriginalWebSocket(e,t)};for(const e in OriginalWebSocket)OriginalWebSocket.hasOwnProperty(e)&&(window.WebSocket[e]=OriginalWebSocket[e]);window.WebSocket.prototype=OriginalWebSocket.prototype,document.addEventListener("pageshow",function(e){if(e.persisted){console.log("Page restored from bfcache");const e=window.location.protocol==="https:"?"wss:":"ws:";liveReloadSocket=new OriginalWebSocket(`${e}//${window.location.host}/__livereload`),liveReloadSocket.onmessage=function(e){e.data==="reload"&&window.location.reload()}}}),window.addEventListener("pagehide",function(){liveReloadSocket&&(liveReloadSocket.onclose=null,liveReloadSocket.close(),liveReloadSocket=null)})</script><link rel=preload href=/css/vendor/material-icons.css as=style><link rel=preload href=/images/logo2.webp as=image><link rel=stylesheet href=/css/vendor/material-icons.css><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css></noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css></noscript><script type=module>
|
||||
|
||||
import * as utils from '/js/utils.modern.min.js';
|
Before Width: | Height: | Size: 730 KiB After Width: | Height: | Size: 730 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 898 KiB After Width: | Height: | Size: 898 KiB |
|
@ -1,4 +1,4 @@
|
|||
<!doctype html><html lang=en><head><script src="/livereload.js?mindelay=10&v=2&port=54386&path=livereload" data-no-instant defer></script><title>How It Works | ShowerLoop</title>
|
||||
<!doctype html><html lang=en><head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script><title>How It Works | ShowerLoop</title>
|
||||
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Learn how the ShowerLoop system filters, purifies and recycles shower water in real-time, saving up to 90% of water and energy."><script>let liveReloadSocket=null;const OriginalWebSocket=window.WebSocket;window.WebSocket=function(e,t){if(e.includes("/__livereload")){if(document.readyState==="complete"){liveReloadSocket=new OriginalWebSocket(e,t);const n={onmessage:null,onclose:null};return Object.defineProperty(liveReloadSocket,"onmessage",{set:function(e){n.onmessage=e},get:function(){return n.onmessage}}),Object.defineProperty(liveReloadSocket,"onclose",{set:function(e){n.onclose=e},get:function(){return n.onclose}}),liveReloadSocket.addEventListener("message",function(e){n.onmessage&&n.onmessage(e)}),liveReloadSocket.addEventListener("close",function(e){liveReloadSocket=null,n.onclose&&n.onclose(e)}),liveReloadSocket}return{send:function(){},close:function(){},addEventListener:function(){},removeEventListener:function(){},set onmessage(e){},set onclose(e){}}}return new OriginalWebSocket(e,t)};for(const e in OriginalWebSocket)OriginalWebSocket.hasOwnProperty(e)&&(window.WebSocket[e]=OriginalWebSocket[e]);window.WebSocket.prototype=OriginalWebSocket.prototype,document.addEventListener("pageshow",function(e){if(e.persisted){console.log("Page restored from bfcache");const e=window.location.protocol==="https:"?"wss:":"ws:";liveReloadSocket=new OriginalWebSocket(`${e}//${window.location.host}/__livereload`),liveReloadSocket.onmessage=function(e){e.data==="reload"&&window.location.reload()}}}),window.addEventListener("pagehide",function(){liveReloadSocket&&(liveReloadSocket.onclose=null,liveReloadSocket.close(),liveReloadSocket=null)})</script><link rel=preload href=/css/vendor/material-icons.css as=style><link rel=preload href=/images/logo2.webp as=image><link rel=stylesheet href=/css/vendor/material-icons.css><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/material.indigo-pink.min.css></noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css media=print onload='this.media="all"'><noscript><link rel=stylesheet href=/css/vendor/fontawesome.min.css></noscript><script type=module>
|
||||
|
||||
import * as utils from '/js/utils.modern.min.js';
|
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 270 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 988 KiB After Width: | Height: | Size: 988 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 488 KiB After Width: | Height: | Size: 488 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |