Improve blog post view counting to track unique visitors

Add `article_views` table and update logic in `server/routes.ts` and `server/storage.ts` to record and check IP hashes before incrementing view counts, ensuring unique visitor tracking.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: d8397246-0deb-4af1-ac00-7fcd2a5d3a42
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/8XlN305
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic
2026-03-07 08:41:47 +00:00
parent da7de230c8
commit 6bea7e0691
4 changed files with 23 additions and 6 deletions
+12 -4
View File
@@ -1,6 +1,7 @@
import { type Article, type InsertArticle, articles } from "@shared/schema";
import { type Article, type InsertArticle, articles, articleViews } from "@shared/schema";
import { db } from "./db";
import { eq, desc, sql, count } from "drizzle-orm";
import { eq, desc, sql, count, and } from "drizzle-orm";
import crypto from "crypto";
export interface IStorage {
getArticles(): Promise<Article[]>;
@@ -12,7 +13,7 @@ export interface IStorage {
getArticlesByCategoryPaginated(category: string, page: number, limit: number): Promise<{ articles: Article[]; total: number }>;
createArticle(article: InsertArticle): Promise<Article>;
updateArticle(id: number, article: Partial<InsertArticle>): Promise<Article | undefined>;
incrementViews(id: number): Promise<void>;
incrementViews(id: number, ip: string): Promise<boolean>;
deleteArticle(id: number): Promise<void>;
}
@@ -61,8 +62,15 @@ export class DatabaseStorage implements IStorage {
return updated;
}
async incrementViews(id: number): Promise<void> {
async incrementViews(id: number, ip: string): Promise<boolean> {
const ipHash = crypto.createHash("sha256").update(ip + "folx-salt").digest("hex").substring(0, 16);
const [existing] = await db.select().from(articleViews)
.where(and(eq(articleViews.articleId, id), eq(articleViews.ipHash, ipHash)))
.limit(1);
if (existing) return false;
await db.insert(articleViews).values({ articleId: id, ipHash });
await db.update(articles).set({ views: sql`${articles.views} + 1` }).where(eq(articles.id, id));
return true;
}
async deleteArticle(id: number): Promise<void> {