${escapeHtml(item.title)}
${item.dek ? `${escapeHtml(item.dek)}
` : ''}${escapeHtml(item.summary)}
Read the full briefdiff --git a/.gitea/workflows/build-and-deploy.yaml b/.gitea/workflows/build-and-deploy.yaml index 72fdbf2..5d1ca6a 100644 --- a/.gitea/workflows/build-and-deploy.yaml +++ b/.gitea/workflows/build-and-deploy.yaml @@ -36,8 +36,8 @@ jobs: - name: Upload build artifact uses: actions/upload-artifact@v3 with: - name: dist - path: dist/ + name: public + path: public/ build-image: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a31123c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +public/ +dist/ +.env diff --git a/Dockerfile b/Dockerfile index de3d7e4..581f198 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,9 @@ FROM node:22-alpine AS builder WORKDIR /app +# Install build dependencies for sharp (libvips) +RUN apk add --no-cache python3 make g++ vips-dev + # Copy package files and install dependencies COPY package*.json ./ RUN npm ci @@ -15,12 +18,12 @@ RUN npm run build FROM nginx:alpine # Copy built site -COPY --from=builder /app/dist /usr/share/nginx/html +COPY --from=builder /app/public /usr/share/nginx/html # Copy ads.txt if present at root -COPY --from=builder /app/ads.txt /usr/share/nginx/html/ads.txt +COPY --from=builder /app/public/ads.txt /usr/share/nginx/html/ads.txt -# Custom nginx config for SPA/history routing +# Custom nginx config for SPA/history routing with image format support RUN echo 'server { \ listen 80; \ server_name localhost; \ @@ -29,7 +32,7 @@ RUN echo 'server { \ location / { \ try_files $uri $uri/ /index.html; \ } \ - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { \ + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|webp|avif)$ { \ expires 1y; \ add_header Cache-Control "public, immutable"; \ } \ diff --git a/build.js b/build.js deleted file mode 100644 index 43d23f8..0000000 --- a/build.js +++ /dev/null @@ -1,46 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const srcDir = path.join(__dirname, 'src'); -const distDir = path.join(__dirname, 'dist'); - -if (!fs.existsSync(distDir)) { - fs.mkdirSync(distDir, { recursive: true }); -} - -// Simple build: copy static assets and generate index.html -const indexHtml = ` - -
- - -Independent news. Clear signal.
-Welcome to Signal Ledger — a subsidiary of Jopdorp.
-Independent news. Clear signal.
-Welcome to Signal Ledger — a subsidiary of Jopdorp.
-${escapeHtml(item.dek)}
` : ''}${escapeHtml(item.summary)}
Read the full briefDigital news magazine
${escapeHtml(SITE_TAGLINE)}
${escapeHtml(EDITOR_NOTE)}
${escapeHtml(item.dek)}
` : ''}This story sits alongside related Signal Ledger coverage that helps frame the broader pattern.
${escapeHtml(item.dek)}
` : ''}${escapeHtml(item.summary)}
${escapeHtml(item.whyItMatters)}
${escapeHtml(item.context)}
${escapeHtml(item.viewpoint)}
${escapeHtml(item.sourceNote)}
${sourceHref}Section
Magazine briefs, analysis, and context from the ${escapeHtml(group.category)} desk.
Archive
A running index of current Signal Ledger briefs.
${escapeHtml(item.dek)}
About
${escapeHtml(EDITOR_NOTE)}
${escapeHtml(SITE_NAME)} aims to sit between the raw newswire and the over-heated opinion economy. We publish compact, readable briefs with context, judgment, and a magazine voice.
Each story is grounded in attributed reporting from established publishers, then rewritten into a distinct Signal Ledger brief with analysis, framing, and context for readers who want understanding rather than repetition.
Editorial and business contact: ${escapeHtml(CONTACT_EMAIL)}.
${escapeHtml(SITE_NAME)} is a subsidiary of ${escapeHtml(PARENT_COMPANY)}.
Planned expansions include author bylines, topic pages, and daily briefing edition pages.