dev #2

Merged
igodin merged 12 commits from dev into main 2026-07-27 18:37:38 +00:00
3 changed files with 138 additions and 88 deletions
Showing only changes of commit 14bc03be93 - Show all commits

89
src/boardService.js Normal file
View File

@ -0,0 +1,89 @@
const { startAuthentication } = require('./auth');
const { fetchClosedTickets } = require('./tickets');
function createBoardService({ clientId, clientSecret, technicians, statusSet, windowMinutes, checkMinutes }) {
const state = {
auth: 'disconnected',
verificationUrl: null,
userCode: null,
lastCheck: null,
lastError: null
};
const tickets = new Map();
let accessToken = null;
let pollingTimer = null;
async function refreshTickets() {
if (!accessToken) return;
await fetchClosedTickets({
accessToken,
state,
tickets,
technicians,
statusSet,
windowMinutes
});
}
function startPolling() {
if (pollingTimer) return;
pollingTimer = setInterval(() => {
refreshTickets().catch(err => {
state.lastError = err.message;
state.lastCheck = Date.now();
});
}, checkMinutes * 60 * 1000);
}
async function connect() {
const authResult = await startAuthentication({
clientId,
clientSecret,
state
});
if (!authResult?.accessToken) return;
accessToken = authResult.accessToken;
console.log('Authorization successful');
await refreshTickets();
startPolling();
}
function failConnection(err) {
state.auth = 'error';
state.lastError = err.message;
state.verificationUrl = null;
state.userCode = null;
}
function getState() {
return {
...state,
windowMinutes,
checkMinutes,
watching: technicians,
serverTime: Date.now(),
tickets: [...tickets.values()].sort((a, b) => b.closedAt - a.closedAt)
};
}
async function checkNow() {
await refreshTickets();
}
return {
connect,
checkNow,
failConnection,
getState
};
}
module.exports = {
createBoardService
};

37
src/routes.js Normal file
View File

@ -0,0 +1,37 @@
const express = require('express');
function createRoutes(boardService) {
const router = express.Router();
router.get('/state', (req, res) => {
res.json(boardService.getState());
});
router.post('/connect', async (req, res) => {
const currentState = boardService.getState();
if (currentState.auth === 'pending' || currentState.auth === 'connected') {
return res.json({ ok: true });
}
try {
boardService.connect().catch(err => {
boardService.failConnection(err);
});
res.json({ ok: true });
} catch (err) {
boardService.failConnection(err);
res.status(500).json({ ok: false, error: err.message });
}
});
router.post('/check', async (req, res) => {
await boardService.checkNow();
res.json({ ok: true });
});
return router;
}
module.exports = {
createRoutes
};

View File

@ -3,8 +3,8 @@ require('dotenv').config({ quiet: true });
const path = require('path');
const express = require('express');
const { startAuthentication } = require('./auth');
const { fetchClosedTickets } = require('./tickets');
const { createBoardService } = require('./boardService');
const { createRoutes } = require('./routes');
const { CLIENT_ID, CLIENT_SECRET, TECHNICIAN_NAMES, STATUS_NAMES } = process.env;
const PORT = process.env.PORT || 3000; // change to 3000 after done testing using 3001 as I have a second server running to make sure it functions correctly
@ -21,95 +21,19 @@ const statusSet = new Set(statuses.map(s => s.toLowerCase()));
const WINDOW_MINUTES = 10;
const CHECK_MINUTES = 1;
let accessToken = null;
const state = {
auth: 'disconnected',
verificationUrl: null,
userCode: null,
lastCheck: null,
lastError: null
};
const tickets = new Map();
async function connect() {
try {
const authResult = await startAuthentication({
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
state
});
if (!authResult?.accessToken) return;
accessToken = authResult.accessToken;
console.log('Authorization successful');
await fetchClosedTickets({
accessToken,
state,
tickets,
technicians,
statusSet,
windowMinutes: WINDOW_MINUTES
});
setInterval(() => {
fetchClosedTickets({
accessToken,
state,
tickets,
technicians,
statusSet,
windowMinutes: WINDOW_MINUTES
});
}, CHECK_MINUTES * 60 * 1000);
} catch (err) {
state.auth = 'error';
state.lastError = err.message;
state.verificationUrl = null;
state.userCode = null;
}
}
const app = express();
const boardService = createBoardService({
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
technicians,
statusSet,
windowMinutes: WINDOW_MINUTES,
checkMinutes: CHECK_MINUTES
});
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/state', (req, res) => {
res.json({
...state,
windowMinutes: WINDOW_MINUTES,
checkMinutes: CHECK_MINUTES,
watching: technicians,
serverTime: Date.now(),
tickets: [...tickets.values()].sort((a, b) => b.closedAt - a.closedAt)
});
});
app.post('/api/connect', async (req, res) => {
if (state.auth === 'pending' || state.auth === 'connected') {
return res.json({ ok: true });
}
try {
connect().catch(err => {
state.auth = 'error';
state.lastError = err.message;
state.verificationUrl = null;
state.userCode = null;
});
res.json({ ok: true });
} catch (err) {
state.auth = 'error';
state.lastError = err.message;
res.status(500).json({ ok: false, error: err.message });
}
});
app.post('/api/check', async (req, res) => {
await fetchClosedTickets();
res.json({ ok: true });
});
app.use('/api', createRoutes(boardService));
app.listen(PORT, () => {
console.log(`Open http://localhost:${PORT}`);