first commit

This commit is contained in:
2026-10-29 14:13:06 +07:00
commit cb369cfef9
8 changed files with 2091 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
APP_PORT=0
DB_HOST=
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_PORT=
+10
View File
@@ -0,0 +1,10 @@
# ignore visual studio code
/.vs/
# dotenv environment variable files
.env
.env.*
!.env.example
# Dependency directories
node_modules/
+36
View File
@@ -0,0 +1,36 @@
# INVESA-NODE
Aplikasi INVESA Service
## Deskripsi
Aplikasi basis NodeJS untuk Web Aplikasi INVESA, pengontrol supervisi dan lalu lintas data monitor pada display depan kasir.
## Install
```
$ git clone https://git.trifonia.cloud/CMASA-MEMPAWAH/invesa-node.git
$ cd invesa-node
$ npm install
```
## Penggunaan
Jalankan service dengan perintah :
```$ npm start```
## Authors
Developer dan programmer
[@siaupheng](mailto:[email protected])
## Version History
* 1.0
* Initial Release
## License
This project is licensed.
+124
View File
@@ -0,0 +1,124 @@
const express = require('express');
const { createServer } = require('node:http');
const { join } = require('node:path');
const { Server } = require('socket.io');
const mysql = require('mysql2/promise');
const dotenv = require('@dotenvx/dotenvx');
const { PoolConnection } = require('mysql2');
const app = express();
const server = createServer(app);
const io = new Server(server, { 'path': '/io' });
dotenv.config();
const db = mysql.createPool({
host: process.env.DB_HOST || '127.0.0.1',
user: process.env.DB_USER || 'mariadb',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
port: process.env.DB_PORT || 3306,
});
const app_port = process.env.APP_PORT || 3000;
io.on('connection', (socket) => {
let pollPPOB = null;
let pollSuper = null;
let pollMonitor = null;
// get ip address
const forwardedIp = socket.handshake.headers["x-forwarded-for"];
//const cloudflareIp = socket.handshake.headers["cf-connecting-ip"];
const clientIp = forwardedIp ? forwardedIp.split(',')[0] : socket.handshake.address;
//console.log("Real Client IP:", clientIp);
// PPOB
socket.on('ppob-check', (ajson) => {
if (!ajson || typeof ajson !== 'object') return socket.emit('ppob-error', { 'message': 'Invalid payload format' });
const { CabID, UserID } = ajson;
if (!CabID || !UserID) return socket.emit('ppob-error', { 'message': 'Missing required CabID and UserID' });
if (pollPPOB) clearInterval(pollPPOB);
pollPPOB = setInterval(async () => {
try {
const [rows, fields] = await db.query("SELECT No_TX,Status FROM ppob_tx WHERE No_TX IN (SELECT No_TX FROM ppob_pend WHERE CabID=? AND UserID=?)", [ CabID, UserID ]);
socket.emit('ppob-data', { 'prabayar': rows });
} catch (error) {
console.error("db query failed :", error);
}
}, 1500);
});
socket.on('ppob-update', (ajson) => {
if (!ajson || typeof ajson !== 'object') return socket.emit('ppob-error', { 'message': 'Invalid payload format' });
const { CabID, UserID } = ajson;
if (!CabID || !UserID) return socket.emit('ppob-error', { 'message': 'Missing required CabID and UserID' });
try {
db.execute("DELETE FROM ppob_pend WHERE Status=4 AND CabID=? AND UserID=?", [ CabID, UserID ]);
} catch (error) {
console.error("db execute failed :", error);
}
});
socket.on('ppob-done', () => {
if (pollPPOB) clearInterval(pollPPOB);
pollPPOB = null;
});
// SUPERVISI
socket.on('super-check', (ajson) => {
if (pollSuper) return;
pollSuper = setInterval(async () => {
try {
const [rows, fields] = await db.query({sql: "SELECT A.AuthID,O.Nama Nama_User,A.Tanggal,M.Judul,A.Catatan,A.REQ,'<a class=\"table-icon unlock2\" title=\"Proses\"><i class=\"bi-unlock2\"></i></a>' Aksi FROM (SELECT AuthID,UserID,Kode,DATE_FORMAT(Tanggal,'%d-%m-%Y %H:%i:%s') Tanggal,REQ,Catatan FROM app_auth WHERE Auth=0) A LEFT JOIN app_operator O ON (A.UserID=O.UserID) LEFT JOIN app_menu M ON (A.Kode=M.Kode)", rowsAsArray: true});
socket.emit('super-data', { 'data': rows });
} catch (error) {
console.error("db query failed :", error);
}
}, 1500);
});
socket.on('super-done', () => {
if (pollSuper) clearInterval(pollSuper);
pollSuper = null;
});
// MONITOR
socket.on('monitor-check', (ajson) => {
if (!ajson || typeof ajson !== 'object') return socket.emit('monitor-error', { 'message': 'Invalid payload format' });
const { CabID, UserID } = ajson;
if (pollMonitor) return;
pollMonitor = setInterval(async () => {
try {
const adata = { 'total': 0, 'thank': 0 };
const [row_pos, fields_pos] = await db.query("SELECT * FROM disp_login WHERE CabID=? AND IP=? ORDER BY Date DESC", [ CabID, clientIp ]);
if (row_pos.length) {
const [row_total, fields_total] = await db.query("SELECT SUM(-Qty*(Harga_Jual-Disk_Rupiah)) Total FROM tmp_inv_detail WHERE No_Ref=? AND Unix=?", [ row_pos[0].No_Pos, row_pos[0].Unix ]);
const [row_thank, fields_thank] = await db.query("SELECT IF(No_Cetak='',0,1) Ada FROM disp_login WHERE CabID=? AND UserID=? AND Unix=?", [ CabID, row_pos[0].UserID, row_pos[0].Unix ]);
if (row_total.length) adata.total = row_total[0].Total || 0;
if (row_thank.length) adata.thank = row_thank[0].Ada || 0;
}
socket.emit('monitor-data', { 'data': adata });
} catch (error) {
console.error("db query failed :", error);
}
}, 1500);
});
socket.on('monitor-done', () => {
if (pollMonitor) clearInterval(pollMonitor);
pollMonitor = null;
});
// DISCONNECT
socket.on('disconnect', () => {
if (pollPPOB) clearInterval(pollPPOB);
if (pollSuper) clearInterval(pollSuper);
if (pollMonitor) clearInterval(pollMonitor);
});
});
app.get('/', (req, res) => {
res.sendFile(join(__dirname, 'public', 'index.html'));
});
app.use(express.static('public'));
server.listen(app_port, () => {
console.log('Server running at http://localhost:' + app_port);
});
+1866
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
{
"name": "invesa-node",
"version": "0.0.1",
"description": "service apps for invesa",
"dependencies": {
"@dotenvx/dotenvx": "^1.69.1",
"express": "^4.22.2",
"mysql2": "^3.22.4",
"socket.io": "^4.8.3"
},
"devDependencies": {
"nodemon": "^3.1.14"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO for INVESA</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="author" content="Siau Pheng <siaupheng[at]3fonia.com>">
<meta name="description" content="Socket.IO for Invesa v1.0.0, 3fonia Software">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<style>
body {
margin: 200px 0 0 0;
padding: 0;
width: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-align: center;
color: #aaa;
font-size: 13pt;
}
h1 {
color: #719e40;
letter-spacing: -4px;
font-family: 'Lato', sans-serif;
font-size: 50pt;
font-weight: 300;
margin: 0;
}
</style>
</head>
<body>
<img src="/images/logo300.png">
<h1>INVESA Socket.IO</h1>
<div>power by 3fonia Software, a web-base software house</div>
</body>
</html>