-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_jobs.mjs
More file actions
131 lines (116 loc) · 4.26 KB
/
server_jobs.mjs
File metadata and controls
131 lines (116 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { spawn } from "child_process";
import { createWriteStream, promises as fs } from "fs";
import path from "path";
import { fileURLToPath } from "url";
import logger from "./logger.mjs";
import queries from "./queries.mjs";
const { runTrip } = queries; //This also threads the model-database-sync through queries for safe db interaction
import models from "./models.mjs";
import { Op } from "sequelize";
const { Trip } = models;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const BACKUP_DIR = path.join(__dirname, "past_semesters");
// async function destroyTrip(trip) {
// const signups = await trip.getTripSignUps();
// let proms = signups.map((signup) => signup.destroy());
// proms.push(trip.destroy());
// return Promise.all(proms);
// }
async function destroyTrip(trip) {
const t = await sequelize.transaction();
try {
//Grab signups
const signups = await trip.getTripSignUps({ transaction: t });
//Delete signups and trip sequentially - this avoids odd deadlocks
for (const signup of signups) {
await signup.destroy({ transaction: t });
}
await trip.destroy({ transaction: t });
await t.commit();
} catch (error) {
await t.rollback();
throw error;
}
}
async function runTrips() {
logger.log("[SERVER DAEMON] Runnning all of today's trips");
//Check if there are any open trips whose planned date is today/has passed and delete them
const todaysDateonly = new Date().toISOString().slice(0, 10);
const tripsToDelete = await Trip.findAll({
where: {
status: "Open",
plannedDate: {
[Op.lte]: todaysDateonly,
}
}
});
if (tripsToDelete.length > 0) logger.log(`[SERVER DAEMON] Destroyed ${tripsToDelete.length} trips for being open on or past planned date`);
let proms = tripsToDelete.map(destroyTrip);
//Run trips that are due to be run
const tripsToRun = await Trip.findAll({
where: {
status: "Pre-Trip",
plannedDate: {
[Op.lte]: todaysDateonly,
}
}
});
if (tripsToRun.length > 0) logger.log(`[SERVER DAEMON] Ran ${tripsToRun.length} trip(s)!`);
const tripUpdateProms = tripsToRun.map(runTrip);
proms.concat(tripUpdateProms);
return Promise.all(proms);
}
async function backupDatabase() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1; // 1-indexed
const season = month <= 5 ? "fall" : "spring";
const backupPath = path.join(BACKUP_DIR, `${season}_${year}.sql`);
await fs.mkdir(BACKUP_DIR, { recursive: true });
await new Promise((resolve, reject) => {
const dump = spawn("mariadb-dump", [
"-h127.0.0.1",
"-uservice",
`-p${process.env.MARIADB_SERVICE_PASSWORD}`,
"boc",
]);
const writeStream = createWriteStream(backupPath);
dump.stdout.pipe(writeStream);
let stderrData = "";
dump.stderr.on("data", (data) => { stderrData += data; });
dump.on("error", reject);
writeStream.on("error", reject);
dump.on("close", (code) => {
writeStream.end();
if (code !== 0) {
reject(new Error(`mariadb-dump exited with code ${code}: ${stderrData}`));
} else {
resolve();
}
});
});
const { size } = await fs.stat(backupPath);
if (size === 0) throw new Error(`Backup file ${backupPath} is empty`);
return backupPath;
}
async function semesterBackup() {
logger.log("[SERVER DAEMON] Creating semester database backup");
let backupPath;
try {
backupPath = await backupDatabase();
} catch (error) {
logger.log(`[SERVER DAEMON] Database backup failed: ${error.message}`);
throw error;
}
logger.log(`[SERVER DAEMON] Backup successfully written to ${backupPath}`);
}
function jobify(cronString, job) {
return {
cronString: cronString,
job: job,
}
}
export default [
jobify("0 5 * * *", runTrips), //Tick status of all trips being run on a given day to Post-Trip at 5am that morning
jobify("0 0 1 1,6 *", semesterBackup), //Back up database between semesters (ie. Jan 1st and June 1st)
]