Public Access
1
0

new: view counter

This commit is contained in:
giteaadmin
2025-08-22 23:28:53 -04:00
parent af4501e527
commit ecdac48379
7 changed files with 132 additions and 8 deletions

View File

@@ -1,8 +1,33 @@
FROM node:18-alpine
ENV NODE_ENV=production
# STAGE 1: Build the Eleventy site
FROM node:18-alpine AS builder
WORKDIR /app
# Copy project files and install dependencies
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080 9229
# Build the Eleventy site
# The output will be in the default `_site` directory
RUN npx @11ty/eleventy
# STAGE 2: Setup the production server
FROM node:18-alpine
WORKDIR /app
# Copy server dependencies from the builder stage
COPY --from=builder /app/package*.json ./
# Install only production dependencies for the server
RUN npm install --omit=dev
# Copy the server file and the built Eleventy site
COPY --from=builder /app/server.js .
COPY --from=builder /app/_site ./_site
COPY --from=builder /app/data ./data
# Expose the port the server runs on
EXPOSE 8080
# The command to start the server
CMD [ "npm", "start" ]

View File

@@ -10,6 +10,9 @@
"author": "",
"license": "ISC",
"dependencies": {
"@11ty/eleventy": "^2.0.1"
"@11ty/eleventy": "^2.0.1",
"cors": "^2.8.5",
"express": "^4.21.2",
"fs-extra": "^11.3.1"
}
}

1
src/_data/views.json Normal file
View File

@@ -0,0 +1 @@
{"8.14.25":1,"8.5.25":1}

View File

@@ -9,8 +9,12 @@ layout: "layout.njk"
</a>
</div>
<h1>{{ title }}</h1>
<div class="post-meta">
<p class="text-gray-400">Published on {{ date | readableDate }}</p>
<span class="view-count" data-view-count data-slug="{{ page.fileSlug }}">Loading views...</span>
</div>
<br/>
{{ content | safe }}
</article>
<script src="/js/view-counter.js"></script>
<div id="bottom"></div>

38
src/js/view-counter.js Normal file
View File

@@ -0,0 +1,38 @@
document.addEventListener('DOMContentLoaded', () => {
const viewCountElement = document.querySelector('[data-view-count]');
if (viewCountElement) {
const slug = viewCountElement.dataset.slug;
const serverUrl = 'http://localhost:3000'; // IMPORTANT: Replace with your server's IP/domain
// Function to fetch and display the view count
const getViews = async () => {
try {
const response = await fetch(`${serverUrl}/api/views/${slug}`);
if (!response.ok) throw new Error('Failed to fetch views');
const data = await response.json();
viewCountElement.textContent = `${data.count} views`;
} catch (error) {
console.error('Error getting views:', error);
viewCountElement.textContent = 'Could not load views';
}
};
// Function to increment the view count
const incrementView = async () => {
try {
// Use a flag in localStorage to prevent incrementing on every refresh
const viewed = localStorage.getItem(`viewed-${slug}`);
if (!viewed) {
await fetch(`${serverUrl}/api/views/${slug}`, { method: 'POST' });
localStorage.setItem(`viewed-${slug}`, 'true');
}
} catch (error) {
console.error('Error incrementing view:', error);
}
};
// Run the functions
incrementView().then(getViews);
}
});

53
src/server.js Normal file
View File

@@ -0,0 +1,53 @@
const express = require('express');
const cors = require('cors');
const fs = require('fs-extra');
const path = require('path');
const app = express();
const port = 3000; // You can change this port if needed
// Path to the file where view counts will be stored
const dbPath = path.join(__dirname, '_data', 'views.json');
// Ensure the data directory and the views.json file exist
fs.ensureFileSync(dbPath);
const initialData = fs.readFileSync(dbPath, 'utf8');
if (initialData.length === 0) {
fs.writeJsonSync(dbPath, {});
}
app.use(cors()); // Enable Cross-Origin Resource Sharing
app.use(express.json());
// --- API Endpoints ---
// GET: Fetch the view count for a specific post slug
app.get('/api/views/:slug', async (req, res) => {
try {
const { slug } = req.params;
const views = await fs.readJson(dbPath);
const count = views[slug] || 0;
res.json({ count });
} catch (error) {
console.error('Error reading view count:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// POST: Increment the view count for a specific post slug
app.post('/api/views/:slug', async (req, res) => {
try {
const { slug } = req.params;
const views = await fs.readJson(dbPath);
views[slug] = (views[slug] || 0) + 1;
await fs.writeJson(dbPath, views);
res.json({ count: views[slug] });
} catch (error) {
console.error('Error updating view count:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.listen(port, () => {
console.log(`View counter server listening at http://localhost:${port}`);
});