Skip to content

Commit 4e0ca13

Browse files
author
yusriyusuf-sketch
authored
Create i-ASET INSOLVENSI
LELONGAN BARANG JUALAN INSOLVENSI
1 parent 6e474c0 commit 4e0ca13

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

i-ASET INSOLVENSI

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// app.js (Express + pg + socket.io)
2+
const express = require('express');
3+
const {Pool} = require('pg');
4+
const http = require('http');
5+
const socketio = require('socket.io');
6+
7+
const pool = new Pool({ /* connection config */ });
8+
const app = express();
9+
app.use(express.json());
10+
const server = http.createServer(app);
11+
const io = socketio(server);
12+
13+
// simple auth middleware placeholder
14+
function auth(req, res, next) {
15+
// assume req.user set by token middleware
16+
req.user = { id: req.header('x-user-id') || 'test-user' };
17+
next();
18+
}
19+
20+
// WebSocket namespace for auctions
21+
io.of('/auctions').on('connection', socket => {
22+
const {auctionId} = socket.handshake.query;
23+
socket.join(`auction:${auctionId}`);
24+
});
25+
26+
// POST /auctions/:id/bid
27+
app.post('/auctions/:id/bid', auth, async (req, res) => {
28+
const auctionId = req.params.id;
29+
const userId = req.user.id;
30+
const amount = Number(req.body.amount);
31+
if (!amount || amount <= 0) return res.status(400).json({error:'invalid amount'});
32+
33+
const client = await pool.connect();
34+
try {
35+
await client.query('BEGIN');
36+
37+
// Lock the auction row to prevent race
38+
const aRes = await client.query(
39+
`SELECT id, reserve_price, end_time, status, highest_bid_id FROM auctions WHERE id=$1 FOR UPDATE`,
40+
[auctionId]
41+
);
42+
if (aRes.rowCount === 0) {
43+
await client.query('ROLLBACK');
44+
return res.status(404).json({error:'auction not found'});
45+
}
46+
const auction = aRes.rows[0];
47+
if (auction.status !== 'running') {
48+
await client.query('ROLLBACK');
49+
return res.status(400).json({error:'auction not running'});
50+
}
51+
52+
// get current highest bid amount
53+
let currentHighest = 0;
54+
if (auction.highest_bid_id) {
55+
const hb = await client.query('SELECT amount FROM bids WHERE id=$1', [auction.highest_bid_id]);
56+
if (hb.rowCount) currentHighest = Number(hb.rows[0].amount);
57+
}
58+
59+
const minIncrement = 1; // business rule; could vary
60+
const minAllowed = Math.max(auction.reserve_price || 0, currentHighest + minIncrement);
61+
62+
if (amount < minAllowed) {
63+
await client.query('ROLLBACK');
64+
return res.status(409).json({error:'bid too low', minAllowed});
65+
}
66+
67+
// insert bid
68+
const bidRes = await client.query(
69+
`INSERT INTO bids (auction_id, user_id, amount, is_proxy, created_at)
70+
VALUES ($1,$2,$3,false,now()) RETURNING id, created_at`,
71+
[auctionId, userId, amount]
72+
);
73+
const bidId = bidRes.rows[0].id;
74+
75+
// update auction cache pointer
76+
await client.query(`UPDATE auctions SET highest_bid_id=$1 WHERE id=$2`, [bidId, auctionId]);
77+
78+
// write audit event
79+
await client.query(
80+
`INSERT INTO audit_events (auction_id, event_type, payload_json, created_at)
81+
VALUES ($1,'bid_placed', $2, now())`,
82+
[auctionId, JSON.stringify({bidId, userId, amount})]
83+
);
84+
85+
await client.query('COMMIT');
86+
87+
// notify via websocket
88+
io.of('/auctions').to(`auction:${auctionId}`).emit('bid_update', {
89+
auctionId, bidId, userId, amount, timestamp: bidRes.rows[0].created_at
90+
});
91+
92+
return res.json({status:'accepted', bidId, current_price: amount});
93+
} catch (err) {
94+
await client.query('ROLLBACK');
95+
console.error(err);
96+
return res.status(500).json({error:'internal'});
97+
} finally {
98+
client.release();
99+
}
100+
});
101+
102+
server.listen(3000, ()=> console.log('listening 3000'));

0 commit comments

Comments
 (0)