OmET Backend
https://gitlab.com/hobbes/cinedia-apiserverRelational DB Design
Relations and Consistency
Klares Design des SQL Schemas, um die Vorteile der relationalen Datenbank zu nutzen. Vermeiden von Duplikaten der Daten durch den konsequenten Einsatz von Referenzen. Verhindern von Verlust oder inkonsistenten Daten aufgrund von Programmfehler durch Constraints.
CREATE TABLE sujet_stage_dateslot ( sujet_id integer NOT NULL REFERENCES sujets ON DELETE RESTRICT, stage_id integer NOT NULL REFERENCES stages ON DELETE RESTRICT, dateslot_id integer NOT NULL REFERENCES dateslots ON DELETE RESTRICT, customer_id integer NOT NULL REFERENCES customers ON DELETE RESTRICT, position integer DEFAULT NULL, CONSTRAINT unique_sujet_per_stagedate UNIQUE (sujet_id, stage_id, dateslot_id, customer_id), CONSTRAINT unique_position_per_stagedatescustomer UNIQUE (stage_id, dateslot_id, customer_id, position) DEFERRABLE INITIALLY DEFERRED);https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/migrations/schemas/1.sql#L220Views
Zahlreiche SQL-Views erleichtern die Abfrage von Daten und verhindern repetitive SQL Queries.
CREATE VIEW view_users ASSELECT users.user_id, users.customer_id, users.firstname AS user_firstname, users.lastname AS user_lastname, users.email, users.flag_notification_dcpready, customers.name AS customer_name, customers.nameshort AS customer_nameshort, customers.type AS customer_typeFROM usersLEFT JOIN customers USING (customer_id);https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/migrations/schemas/1.sql#L1263Asynchronous Task Queues
Um HTTP Request kurz halten, werden länger dauernde Tasks an Worker in einem eigenen Prozess übergeben. Die Tasks werden von einer FIFO Queue verwaltet. Diese stellt sicher, dass die Tasks mindestens einmal, aber auch sicher nur einmal von einem Worker bearbeitet werden. Die Daten der Queue werden in Redis gespeichert.
if (insertData.type === "dia") { return jobqueue .newJob(DIA_VALIDATE_TASKNAME, taskData, { lifo: true, }) .then(function (jobId) { return { jobId, sujetId, }; });}Abstraction Layer CLI Tools
Für CLI Tools wie GraphicsMagick, FFmpeg oder OpenDCP habe ich einen Abstraction Layer geschrieben. So konnte ich Inputs und Outputs standardisieren und konsequent das gleiche Interface mit Promises verwenden.
exports.thumbnail = function (inputFile, outputFile, size) { return execPromise("gm", [ "convert", "-size", size, inputFile, "-strip", "-thumbnail", `${size}^`, "-gravity", "Center", "-background", "black", "-extent", `${size}^`, "+matte", "-type", "TrueColor", "-quality", "70", `jpg:${outputFile}`, ]);};https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/lib/worker/tools.js#L238Cron Jobs für wiederholende Aufgaben
Tasks, die wiederholt und regelmässig ausgeführt werden müssen, werden von Cron Jobs gestartet.
block .removeOldDcps() .then(function (deletedDcps) { log.info(`removed ${deletedDcps} dcps`); log.info("end remove old dcps"); }) .catch(function (error) { log.error(error); return mailer .sendGenericEmail({ From: `bot@${mailhost}`, To: `support@${mailhost}`, Subject: "error delete old dcps", TextBody: `There was an error deleting old dcps. Please check the logs for details.`, }) .then(() => { process.exit(1); }); }) .finally(function () { process.exit(0); });https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/cron/removeOldDcps.js#L10Authentication and Authorization
Authentication und Authorization werden in ExpressJS mit Middleware geregelt.
exports.createSujet = function (req, res, next) { var user = getRequestUser(res), hasSujetCreateActivity = hasActivity(user, SUJETS_CREATE); if (hasSujetCreateActivity) { return next(); } return next( ClientError({ title: "authorization error", statusCode: 403, }) );};https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/lib/web/middleware/can.js#L57Deployment mit PaaS
Um den Aufwand für DevOps gering zu halten, setzte ich auf den PaaS Anbieter Nanobox, der inzwischen von Digital Ocean übernommen wurde.
