OmET Backend

https://gitlab.com/hobbes/cinedia-apiserver

Relational 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#L220

Views

Zahlreiche SQL-Views erleichtern die Abfrage von Daten und verhindern repetitive SQL Queries.

CREATE VIEW view_users AS
SELECT
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_type
FROM users
LEFT JOIN customers USING (customer_id);
https://gitlab.com/hobbes/cinedia-apiserver/-/blob/master/migrations/schemas/1.sql#L1263

Asynchronous 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#L238

Cron 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#L10

Authentication 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#L57

Deployment 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.

PaaS Nanobox