new: view counter
This commit is contained in:
31
Dockerfile
31
Dockerfile
@@ -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" ]
|
@@ -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
1
src/_data/views.json
Normal file
@@ -0,0 +1 @@
|
||||
{"8.14.25":1,"8.5.25":1}
|
@@ -9,8 +9,12 @@ layout: "layout.njk"
|
||||
</a>
|
||||
</div>
|
||||
<h1>{{ title }}</h1>
|
||||
<p class="text-gray-400">Published on {{ date | readableDate }}</p>
|
||||
<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
38
src/js/view-counter.js
Normal 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
53
src/server.js
Normal 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}`);
|
||||
});
|
Reference in New Issue
Block a user