Skip to content

Commit 443de1a

Browse files
authored
Merge pull request #344 from Pseudo-Lab/feat/enhance-logging
feat: enhance platform access logging with ip hash and unify salt config
2 parents ffa3433 + b199214 commit 443de1a

File tree

4 files changed

+41
-3
lines changed

4 files changed

+41
-3
lines changed

.github/workflows/devfactory-homepage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
cat > .env <<'EOF'
3333
APP_HOST=${{ vars.APP_HOST }}
3434
DATABASE_URL=${{ secrets.DATABASE_URL }}
35+
ACCESS_LOGGING_IP_SALT=${{ secrets.ACCESS_LOGGING_IP_SALT }}
3536
EOF
3637
3738
- name: Build & up (prod)

platform/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ APP_HOST=your-domain.com
33

44
# Database Setting
55
DATABASE_URL=postgresql://user:pass@devfactory-postgres:5432/dbname
6+
7+
# Logging Setting
8+
ACCESS_LOGGING_IP_SALT=your-secret-salt-here

platform/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ services:
3535
restart: unless-stopped
3636
environment:
3737
- DATABASE_URL=${DATABASE_URL}
38+
- ACCESS_LOGGING_IP_SALT=${ACCESS_LOGGING_IP_SALT}
3839
- PORT=3000
3940
networks:
4041
- traefik

platform/server/src/index.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require('dotenv').config();
22
const express = require('express');
3+
const crypto = require('crypto');
34
const { Pool } = require('pg');
45
const cors = require('cors');
56

@@ -25,6 +26,35 @@ pool.query('SELECT NOW()', (err, res) => {
2526
});
2627

2728
// API Routes
29+
30+
/**
31+
* Extracts the client IP address from request headers or connection info.
32+
*/
33+
function getClientIp(req) {
34+
// Check X-Forwarded-For header (common for reverse proxies)
35+
const forwardedFor = req.headers['x-forwarded-for'];
36+
if (forwardedFor) {
37+
// Can be a comma-separated list; the first one is the original client
38+
return forwardedFor.split(',')[0].trim();
39+
}
40+
41+
// Check X-Real-IP header
42+
const realIp = req.headers['x-real-ip'];
43+
if (realIp) {
44+
return realIp;
45+
}
46+
47+
// Fallback to Express req.ip or socket address
48+
return req.ip || req.socket.remoteAddress;
49+
}
50+
51+
/**
52+
* Hashes the IP address with a salt, matching the behavior in the cert system.
53+
*/
54+
function hashIp(ip, salt = '') {
55+
if (!ip) return null;
56+
return crypto.createHash('sha256').update(salt + ip).digest('hex');
57+
}
2858
app.get('/api/health', (req, res) => {
2959
res.json({ status: 'ok' });
3060
});
@@ -33,12 +63,15 @@ app.get('/api/health', (req, res) => {
3363
app.post('/api/stats/visit', async (req, res) => {
3464
try {
3565
const { path, userAgent } = req.body;
36-
// 기존 로그 포맷에 맞춰 method는 'PAGEVIEW'로, referrer는 현재 호스트로 기록
3766
const referrer = req.headers.referer || '';
3867

68+
// Extract client IP and generate hash
69+
const clientIp = getClientIp(req);
70+
const ipHash = hashIp(clientIp, process.env.ACCESS_LOGGING_IP_SALT || '');
71+
3972
await pool.query(
40-
'INSERT INTO logging.access_log (path, method, status, user_agent, referrer, ts) VALUES ($1, $2, $3, $4, $5, NOW())',
41-
[path || '/', 'PAGEVIEW', 200, userAgent, referrer]
73+
'INSERT INTO logging.access_log (path, method, status, ip_hash, user_agent, referrer, ts) VALUES ($1, $2, $3, $4, $5, $6, NOW())',
74+
[path || '/', 'PAGEVIEW', 200, ipHash, userAgent, referrer]
4275
);
4376
res.status(201).json({ message: 'Visit logged successfully' });
4477
} catch (err) {

0 commit comments

Comments
 (0)