kopia lustrzana https://github.com/FacilMap/facilmap
268 wiersze
9.2 KiB
TypeScript
268 wiersze
9.2 KiB
TypeScript
import { DataTypes, InferAttributes, InferCreationAttributes, Model, Op, Sequelize } from "sequelize";
|
|
import { FindPadsQuery, FindPadsResult, PadData, PadDataCreate, PadDataUpdate, PadId, PagedResults } from "facilmap-types";
|
|
import Database from "./database";
|
|
import { streamEachPromise } from "../utils/streams";
|
|
import { createModel } from "./helpers";
|
|
|
|
export interface PadModel extends Model<InferAttributes<PadModel>, InferCreationAttributes<PadModel>> {
|
|
id: PadId;
|
|
name: string;
|
|
writeId: PadId;
|
|
adminId: PadId;
|
|
searchEngines: boolean;
|
|
description: string;
|
|
clusterMarkers: boolean;
|
|
legend1: string;
|
|
legend2: string;
|
|
toJSON: () => PadData;
|
|
};
|
|
|
|
export default class DatabasePads {
|
|
|
|
PadModel = createModel<PadModel>();
|
|
|
|
_db: Database;
|
|
|
|
constructor(database: Database) {
|
|
this._db = database;
|
|
|
|
this.PadModel.init({
|
|
id : { type: DataTypes.STRING, allowNull: false, primaryKey: true, validate: { is: /^.+$/ } },
|
|
name: { type: DataTypes.TEXT, allowNull: true, get: function(this: PadModel) { return this.getDataValue("name") || "New FacilMap"; } },
|
|
writeId: { type: DataTypes.STRING, allowNull: false, validate: { is: /^.+$/ } },
|
|
adminId: { type: DataTypes.STRING, allowNull: false, validate: { is: /^.+$/ } },
|
|
searchEngines: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
|
|
description: { type: DataTypes.STRING, allowNull: false, defaultValue: "" },
|
|
clusterMarkers: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
|
|
legend1: { type: DataTypes.STRING, allowNull: false, defaultValue: "" },
|
|
legend2: { type: DataTypes.STRING, allowNull: false, defaultValue: "" }
|
|
}, {
|
|
sequelize: this._db._conn,
|
|
modelName: "Pad"
|
|
});
|
|
}
|
|
|
|
afterInit(): void {
|
|
this.PadModel.belongsTo(this._db.views.ViewModel, { as: "defaultView", foreignKey: "defaultViewId", constraints: false });
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
|
|
async padIdExists(padId: PadId): Promise<boolean> {
|
|
const num = await this.PadModel.count({ where: { [Op.or]: [ { id: padId }, { writeId: padId }, { adminId: padId } ] } });
|
|
return num > 0;
|
|
}
|
|
|
|
async getPadData(padId: PadId): Promise<PadData | undefined> {
|
|
const obj = await this.PadModel.findOne({ where: { id: padId }, include: [ { model: this._db.views.ViewModel, as: "defaultView" } ]});
|
|
return obj?.toJSON();
|
|
}
|
|
|
|
async getPadDataByWriteId(writeId: PadId): Promise<PadData | undefined> {
|
|
const obj = await this.PadModel.findOne({ where: { writeId: writeId }, include: [ { model: this._db.views.ViewModel, as: "defaultView" } ] });
|
|
return obj?.toJSON();
|
|
}
|
|
|
|
async getPadDataByAdminId(adminId: PadId): Promise<PadData | undefined> {
|
|
const obj = await this.PadModel.findOne({ where: { adminId: adminId }, include: [ { model: this._db.views.ViewModel, as: "defaultView" } ] });
|
|
return obj?.toJSON();
|
|
}
|
|
|
|
async getPadDataByAnyId(padId: PadId): Promise<PadData | undefined> {
|
|
const obj = await this.PadModel.findOne({ where: { [Op.or]: { id: padId, writeId: padId, adminId: padId } }, include: [ { model: this._db.views.ViewModel, as: "defaultView" } ] });
|
|
return obj?.toJSON();
|
|
}
|
|
|
|
async createPad(data: PadDataCreate): Promise<PadData> {
|
|
if(!data.id || data.id.length == 0)
|
|
throw new Error("Invalid read-only ID");
|
|
if(!data.writeId || data.writeId.length == 0)
|
|
throw new Error("Invalid read-write ID");
|
|
if(!data.adminId || data.adminId.length == 0)
|
|
throw new Error("Invalid admin ID");
|
|
if(data.id == data.writeId || data.id == data.adminId || data.writeId == data.adminId)
|
|
throw new Error("Read-only, read-write and admin ID have to be different from each other.");
|
|
|
|
await Promise.all([data.id, data.writeId, data.adminId].map(async (id) => {
|
|
if (await this.padIdExists(id))
|
|
throw new Error("ID '" + id + "' is already taken.");
|
|
}));
|
|
|
|
const createdObj = await this.PadModel.create(data);
|
|
|
|
await this._db.types.createDefaultTypes(data.id);
|
|
|
|
return createdObj.toJSON() as PadData;
|
|
}
|
|
|
|
async updatePadData(padId: PadId, data: PadDataUpdate): Promise<PadData> {
|
|
const oldData = await this.getPadData(padId);
|
|
|
|
if(!oldData)
|
|
throw new Error("Pad " + padId + " could not be found.");
|
|
|
|
if(data.id != null && data.id != padId && data.id.length == 0)
|
|
throw new Error("Invalid read-only ID");
|
|
|
|
if(data.id != null && data.id != padId) {
|
|
if (await this.padIdExists(data.id))
|
|
throw new Error("ID '" + data.id + "' is already taken.");
|
|
}
|
|
|
|
if(data.writeId != null && data.writeId != oldData.writeId) {
|
|
if(data.writeId.length == 0)
|
|
throw new Error("Invalid read-write ID");
|
|
if(data.writeId == (data.id != null ? data.id : padId))
|
|
throw new Error("Read-only and read-write ID cannot be the same.");
|
|
|
|
if (await this.padIdExists(data.writeId))
|
|
throw new Error("ID '" + data.writeId + "' is already taken.");
|
|
}
|
|
|
|
if(data.adminId != null && data.adminId != oldData.adminId) {
|
|
if(data.adminId.length == 0)
|
|
throw new Error("Invalid admin ID");
|
|
if(data.adminId == (data.id != null ? data.id : padId))
|
|
throw new Error("Read-only and admin ID cannot be the same.");
|
|
if(data.adminId == (data.writeId != null ? data.writeId : oldData.writeId))
|
|
throw new Error("Read-write and admin ID cannot be the same.");
|
|
|
|
if (await this.padIdExists(data.adminId))
|
|
throw new Error("ID '" + data.adminId + "' is already taken.");
|
|
}
|
|
|
|
await this.PadModel.update(data, { where: { id: padId } });
|
|
|
|
const newData = await this.getPadData(data.id || padId);
|
|
|
|
if (!newData)
|
|
throw new Error("Pad has disappeared after updating.");
|
|
|
|
await this._db.history.addHistoryEntry(data.id || padId, {
|
|
type: "Pad",
|
|
action: "update",
|
|
objectBefore: oldData,
|
|
objectAfter: newData
|
|
});
|
|
|
|
this._db.emit("padData", padId, newData);
|
|
return newData;
|
|
}
|
|
|
|
async deletePad(padId: PadId): Promise<void> {
|
|
const padData = await this.getPadDataByAnyId(padId);
|
|
|
|
if (!padData)
|
|
throw new Error(`Pad "${padId}" does not exist.`);
|
|
|
|
if (padData.defaultViewId) {
|
|
await this.updatePadData(padData.id, { defaultViewId: null });
|
|
}
|
|
|
|
await streamEachPromise(this._db.markers.getPadMarkers(padData.id), async (marker) => {
|
|
await this._db.markers.deleteMarker(padData.id, marker.id);
|
|
});
|
|
|
|
await streamEachPromise(this._db.lines.getPadLines(padData.id, ['id']), async (line) => {
|
|
await this._db.lines.deleteLine(padData.id, line.id);
|
|
});
|
|
|
|
await streamEachPromise(this._db.types.getTypes(padData.id), async (type) => {
|
|
await this._db.types.deleteType(padData.id, type.id);
|
|
});
|
|
|
|
await streamEachPromise(this._db.views.getViews(padData.id), async (view) => {
|
|
await this._db.views.deleteView(padData.id, view.id);
|
|
});
|
|
|
|
await this._db.history.clearHistory(padData.id);
|
|
|
|
await this.PadModel.destroy({ where: { id: padData.id } });
|
|
|
|
this._db.emit("deletePad", padId);
|
|
}
|
|
|
|
async findPads(query: FindPadsQuery): Promise<PagedResults<FindPadsResult>> {
|
|
const like = query.query.toLowerCase().replace(/[%_\\]/g, "\\$&").replace(/[*]/g, "%").replace(/[?]/g, "_");
|
|
const { count, rows } = await this.PadModel.findAndCountAll({
|
|
where: Sequelize.and(
|
|
{ searchEngines: true },
|
|
Sequelize.where(Sequelize.fn("lower", Sequelize.col(`Pad.name`)), {[Op.like]: `%${like}%`})
|
|
),
|
|
offset: query.start ?? 0,
|
|
...(query.limit != null ? { limit: query.limit } : {}),
|
|
attributes: ["id", "name", "description"]
|
|
});
|
|
|
|
return {
|
|
results: rows.map((row) => row.toJSON()),
|
|
totalLength: count
|
|
};
|
|
}
|
|
|
|
/*function copyPad(fromPadId, toPadId, callback) {
|
|
function _handleStream(stream, next, cb) {
|
|
stream.on("data", function(data) {
|
|
stream.pause();
|
|
cb(data, function() {
|
|
stream.resume();
|
|
});
|
|
});
|
|
|
|
stream.on("error", next);
|
|
stream.on("end", next);
|
|
}
|
|
|
|
async.auto({
|
|
fromPadData : function(next) {
|
|
backend.getPadData(fromPadId, next);
|
|
},
|
|
toPadData : function(next) {
|
|
getPadData(toPadId, next);
|
|
},
|
|
padsExist : [ "fromPadData", "toPadData", function(r, next) {
|
|
if(!r.fromPadData)
|
|
return next(new Error("Pad "+fromPadId+" does not exist."));
|
|
if(!r.toPadData.writable)
|
|
return next(new Error("Destination pad is read-only."));
|
|
|
|
toPadId = r.toPadData.id;
|
|
|
|
next();
|
|
}],
|
|
copyMarkers : [ "padsExist", function(r, next) {
|
|
_handleStream(getPadMarkers(fromPadId, null), next, function(marker, cb) {
|
|
createMarker(toPadId, marker, cb);
|
|
});
|
|
}],
|
|
copyLines : [ "padsExist", function(r, next) {
|
|
_handleStream(getPadLines(fromPadId), next, function(line, cb) {
|
|
async.auto({
|
|
createLine : function(next) {
|
|
_createLine(toPadId, line, next);
|
|
},
|
|
getLinePoints : function(next) {
|
|
backend.getLinePoints(line.id, next);
|
|
},
|
|
setLinePoints : [ "createLine", "getLinePoints", function(r, next) {
|
|
_setLinePoints(toPadId, r.createLine.id, r.getLinePoints, next);
|
|
} ]
|
|
}, cb);
|
|
});
|
|
}],
|
|
copyViews : [ "padsExist", function(r, next) {
|
|
_handleStream(getViews(fromPadId), next, function(view, cb) {
|
|
createView(toPadId, view, function(err, newView) {
|
|
if(err)
|
|
return cb(err);
|
|
|
|
if(r.fromPadData.defaultView && r.fromPadData.defaultView.id == view.id && r.toPadData.defaultView == null)
|
|
updatePadData(toPadId, { defaultView: newView.id }, cb);
|
|
else
|
|
cb();
|
|
});
|
|
});
|
|
}]
|
|
}, callback);
|
|
}*/
|
|
} |