@syngrisi/syngrisi
Version:
Syngrisi - Visual Testing Tool
6,557 lines • 219 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/server/routes/v1/index.route.ts
var index_route_exports = {};
__export(index_route_exports, {
default: () => index_route_default
});
module.exports = __toCommonJS(index_route_exports);
var import_express15 = __toESM(require("express"));
// src/server/routes/v1/auth.route.ts
var import_express = __toESM(require("express"));
var import_zod_to_openapi3 = require("@asteasolutions/zod-to-openapi");
// src/server/controllers/auth.controller.ts
var import_http_status = __toESM(require("http-status"));
var import_passport = __toESM(require("passport"));
var import_hasha = __toESM(require("hasha"));
var import_uuid_apikey = __toESM(require("uuid-apikey"));
// src/server/models/Check.model.ts
var import_mongoose = __toESM(require("mongoose"));
// src/server/models/plugins/paginate.plugin.ts
var paginate = (schema) => {
schema.statics.paginate = async function(filter, options) {
let sort;
if (options.sortBy) {
const sortingCriteria = [];
options.sortBy.split(",").forEach((sortOption) => {
const [key, order] = sortOption.split(":");
sortingCriteria.push((order === "desc" ? "-" : "") + key);
});
sort = sortingCriteria.join(" ");
} else {
sort = { _id: -1 };
}
const limit = options.limit && parseInt(options.limit.toString(), 10) >= 0 ? parseInt(options.limit.toString(), 10) : 10;
const page = options.page && parseInt(options.page.toString(), 10) > 0 ? parseInt(options.page.toString(), 10) : 1;
const skip = (page - 1) * limit;
const countPromise = this.countDocuments(filter).exec();
let docsPromise = this.find(filter).sort(sort).skip(skip).limit(limit);
if (options.populate) {
options.populate.split(",").forEach((populateOption) => {
docsPromise = docsPromise.populate(
populateOption.split(".").reverse().reduce((a, b) => ({ path: b, populate: a }))
);
});
}
docsPromise = docsPromise.exec();
return Promise.all([countPromise, docsPromise]).then((values) => {
const [totalResults, results] = values;
const totalPages = Math.ceil(totalResults / limit);
const result = {
results,
page,
limit,
totalPages,
totalResults,
timestamp: Number(Date.now() + String(process.hrtime()[1]).slice(3, 6))
};
return Promise.resolve(result);
});
};
};
var paginate_plugin_default = paginate;
// src/server/models/plugins/toJSON.plugin.ts
var deleteAtPath = (obj, path6, index) => {
if (index === path6.length - 1) {
delete obj[path6[index]];
return;
}
deleteAtPath(obj[path6[index]], path6, index + 1);
};
var toJSON = (schema) => {
let transform;
if (schema.options.toJSON && schema.options.toJSON.transform) {
transform = schema.options.toJSON.transform;
}
schema.options.toJSON = Object.assign(schema.options.toJSON || {}, {
transform(doc, ret, options) {
Object.keys(schema.paths).forEach((path6) => {
if (schema.paths[path6].options && schema.paths[path6].options.private) {
deleteAtPath(ret, path6.split("."), 0);
}
});
ret.id = ret._id.toString();
delete ret.__v;
delete ret.createdAt;
delete ret.updatedAt;
if (transform) {
return transform(doc, ret, options);
}
}
});
};
var toJSON_plugin_default = toJSON;
// src/server/models/plugins/paginateDistinct.plugin.ts
var import_bson = require("bson");
var paginateDistinct = (schema) => {
schema.statics.paginateDistinct = async function(filter, options) {
let sort;
if (options.sortBy) {
options.sortBy.split(",").forEach((sortOption) => {
const [key, order] = sortOption.split(":");
sort[key] = order === "desc" ? -1 : 1;
});
} else {
sort = { _id: -1 };
}
let limit = options.limit && parseInt(options.limit.toString(), 10) >= 0 ? parseInt(options.limit.toString(), 10) : 10;
limit = limit === 0 ? 9007199254740991 : limit;
const page = options.page && parseInt(options.page.toString(), 10) > 0 ? parseInt(options.page.toString(), 10) : 1;
const skip = (page - 1) * limit;
const groupAggregateObj = { $group: { _id: `$${options.field}` } };
const documentsCount = (await this.aggregate([groupAggregateObj]).exec()).length;
const aggregateArr = [
{ $match: import_bson.EJSON.parse(filter.filter || "{}") },
groupAggregateObj,
{ $sort: sort },
{ $skip: skip },
{ $limit: limit }
];
const aggregatedDocs = (await this.aggregate(aggregateArr)).filter((x) => x._id).map((x) => {
if (x[options.field]) {
return x[options.field][0];
}
return { name: x._id };
});
return Promise.all([documentsCount, aggregatedDocs]).then((values) => {
const [totalResults, results] = values;
const totalPages = Math.ceil(totalResults / limit);
const result = {
results,
page,
limit,
totalPages,
totalResults,
timestamp: (/* @__PURE__ */ new Date()).getTime()
};
return Promise.resolve(result);
});
};
};
var paginateDistinct_plugin_default = paginateDistinct;
// src/server/models/Check.model.ts
var CheckSchema = new import_mongoose.Schema({
name: {
type: String,
required: [true, 'CheckSchema: The "name" field must be required']
},
test: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSTest",
required: [true, 'CheckSchema: The "test" field must be required']
},
suite: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSSuite",
required: [true, 'CheckSchema: The "suite" field must be required']
},
app: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSApp",
required: [true, 'CheckSchema: The "app" field must be required']
},
branch: {
type: String
},
realBaselineId: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSBaseline"
},
baselineId: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSSnapshot"
},
actualSnapshotId: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSSnapshot"
},
diffId: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSSnapshot"
},
createdDate: {
type: Date,
required: true,
default: Date.now
},
updatedDate: {
type: Date
},
status: {
type: [{
type: String,
enum: {
values: ["new", "pending", "approved", "running", "passed", "failed", "aborted"],
message: "status is required"
}
}],
default: ["new"]
},
browserName: {
type: String
},
browserVersion: {
type: String
},
browserFullVersion: {
type: String
},
viewport: {
type: String
},
os: {
type: String
},
domDump: {
type: String
},
result: {
type: String,
default: "{}"
},
run: {
type: import_mongoose.Schema.Types.ObjectId
},
markedAs: {
type: String,
enum: ["bug", "accepted"]
},
markedDate: {
type: Date
},
markedById: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSUser"
},
markedByUsername: {
type: String
},
markedBugComment: {
type: String
},
creatorId: {
type: import_mongoose.Schema.Types.ObjectId,
ref: "VRSUser"
},
creatorUsername: {
type: String
},
failReasons: {
type: [String]
},
vOffset: {
type: String
},
topStablePixels: {
type: String
},
meta: {
type: Object
}
});
CheckSchema.plugin(toJSON_plugin_default);
CheckSchema.plugin(paginate_plugin_default);
var Check = import_mongoose.default.model("VRSCheck", CheckSchema);
var Check_model_default = Check;
// src/server/models/Log.model.ts
var import_mongoose2 = __toESM(require("mongoose"));
var LogSchema = new import_mongoose2.Schema({
timestamp: {
type: Date
},
level: {
type: String
},
message: {
type: String
},
meta: {
type: Object
},
hostname: {
type: Object
}
});
LogSchema.plugin(toJSON_plugin_default);
LogSchema.plugin(paginate_plugin_default);
var Log = import_mongoose2.default.model("VRSLog", LogSchema);
var Log_model_default = Log;
// src/server/models/App.model.ts
var import_mongoose3 = __toESM(require("mongoose"));
var AppSchema = new import_mongoose3.Schema({
name: {
type: String,
default: "Others",
unique: true,
required: [true, 'AppSchema: The "name" field must be required']
},
description: {
type: String
},
version: {
type: String
},
updatedDate: {
type: Date
},
createdDate: {
type: Date
},
meta: {
type: Object
}
});
AppSchema.plugin(paginate_plugin_default);
AppSchema.plugin(toJSON_plugin_default);
var App = import_mongoose3.default.model("VRSApp", AppSchema);
var App_model_default = App;
// src/server/models/Snapshot.model.ts
var import_mongoose4 = __toESM(require("mongoose"));
var SnapshotSchema = new import_mongoose4.Schema({
name: {
type: String,
required: [true, 'SnapshotSchema: The "name" field must be required']
},
path: {
type: String
},
filename: {
type: String
},
imghash: {
type: String,
required: [true, 'SnapshotSchema: The "imghash" field must be required']
},
createdDate: {
type: Date,
default: Date.now
},
vOffset: {
type: Number
},
hOffset: {
type: Number
}
});
SnapshotSchema.plugin(toJSON_plugin_default);
SnapshotSchema.plugin(paginate_plugin_default);
var Snapshot = import_mongoose4.default.model("VRSSnapshot", SnapshotSchema);
var Snapshot_model_default = Snapshot;
// src/server/models/AppSettings.model.ts
var import_mongoose5 = __toESM(require("mongoose"));
var AppSettingsSchema = new import_mongoose5.Schema({
name: {
type: String,
unique: true,
required: [true, 'AppSettingsSchema: The "name" field must be required']
},
label: {
type: String,
required: [true, 'AppSettingsSchema: The "label" field must be required']
},
description: {
type: String
},
type: {
type: String,
required: [true, 'AppSettingsSchema: The "type" field must be required']
},
value: {
type: import_mongoose5.Schema.Types.Mixed,
required: [true, 'AppSettingsSchema: The "value" field must be required']
},
env_variable: {
type: String
},
enabled: {
type: Boolean
}
});
AppSettingsSchema.plugin(toJSON_plugin_default);
var AppSettings = import_mongoose5.default.model("VRSAppSettings", AppSettingsSchema);
var AppSettings_model_default = AppSettings;
// src/server/models/Suite.model.ts
var import_mongoose6 = __toESM(require("mongoose"));
var SuiteSchema = new import_mongoose6.Schema({
name: {
type: String,
default: "Others",
unique: true,
required: [true, 'SuiteSchema: The "name" field must be required']
},
tags: {
type: [String]
},
app: {
type: import_mongoose6.Schema.Types.ObjectId,
ref: "VRSApp",
required: [true, 'SuiteSchema: The "app" field must be required']
},
description: {
type: String
},
updatedDate: {
type: Date,
default: Date.now
},
createdDate: {
type: Date
},
meta: {
type: Object
}
});
SuiteSchema.plugin(paginate_plugin_default);
SuiteSchema.plugin(toJSON_plugin_default);
var Suite = import_mongoose6.default.model("VRSSuite", SuiteSchema);
var Suite_model_default = Suite;
// src/server/models/Run.model.ts
var import_mongoose7 = __toESM(require("mongoose"));
var RunSchema = new import_mongoose7.Schema({
name: {
type: String,
required: [true, 'RunSchema: The "name" field must be required']
},
app: {
type: import_mongoose7.Schema.Types.ObjectId,
ref: "VRSApp",
required: [true, 'RunSchema: The "app" field must be required']
},
ident: {
type: String,
unique: true,
required: [true, 'RunSchema: The "ident" field must be required']
},
description: {
type: String
},
updatedDate: {
type: Date,
default: Date.now
},
createdDate: {
type: Date
},
parameters: {
type: [String]
},
meta: {
type: Object
}
});
RunSchema.plugin(paginate_plugin_default);
RunSchema.plugin(toJSON_plugin_default);
var Run = import_mongoose7.default.model("VRSRun", RunSchema);
var Run_model_default = Run;
// src/server/models/User.model.ts
var import_mongoose8 = __toESM(require("mongoose"));
var import_passport_local_mongoose = __toESM(require("passport-local-mongoose"));
var UserSchema = new import_mongoose8.Schema({
username: {
type: String,
unique: true,
required: [true, 'UserSchema: The "username" field must be required']
},
firstName: {
type: String,
required: [true, 'UserSchema: The "firstName" field must be required']
},
lastName: {
type: String,
required: [true, 'UserSchema: The "lastName" field must be required']
},
role: {
type: String,
enum: ["admin", "reviewer", "user"],
required: [true, 'UserSchema: The "role" field must be required']
},
password: {
type: String
},
token: {
type: String
},
apiKey: {
type: String
},
createdDate: {
type: Date
},
updatedDate: {
type: Date
},
expiration: {
type: Date
},
meta: {
type: Object
}
});
UserSchema.statics.isEmailTaken = async function(username, excludeUserId) {
const user = await this.findOne({ username, _id: { $ne: excludeUserId } });
return !!user;
};
UserSchema.plugin(toJSON_plugin_default);
UserSchema.plugin(paginate_plugin_default);
UserSchema.plugin(import_passport_local_mongoose.default, { hashField: "password" });
var User = import_mongoose8.default.model("VRSUser", UserSchema);
var User_model_default = User;
// src/server/models/Baseline.model.ts
var import_mongoose9 = __toESM(require("mongoose"));
var BaselineSchema = new import_mongoose9.Schema({
snapshootId: {
type: import_mongoose9.Schema.Types.ObjectId
},
name: {
type: String,
required: [true, 'VRSBaselineSchema: The "name" field must be required']
},
app: {
type: import_mongoose9.Schema.Types.ObjectId,
ref: "VRSApp",
required: [true, 'VRSBaselineSchema: The "app" field must be required']
},
branch: {
type: String
},
browserName: {
type: String
},
browserVersion: {
type: String
},
browserFullVersion: {
type: String
},
viewport: {
type: String
},
os: {
type: String
},
markedAs: {
type: String,
enum: ["bug", "accepted"]
},
lastMarkedDate: {
type: Date
},
createdDate: {
type: Date
},
updatedDate: {
type: Date
},
markedById: {
type: import_mongoose9.Schema.Types.ObjectId,
ref: "VRSUser"
},
markedByUsername: {
type: String
},
ignoreRegions: {
type: String
},
boundRegions: {
type: String
},
matchType: {
type: String,
enum: ["antialiasing", "nothing", "colors"]
},
meta: {
type: Object
}
});
BaselineSchema.plugin(toJSON_plugin_default);
BaselineSchema.plugin(paginate_plugin_default);
var Baseline = import_mongoose9.default.model("VRSBaseline", BaselineSchema);
var Baseline_model_default = Baseline;
// src/server/models/Test.model.ts
var import_mongoose10 = __toESM(require("mongoose"));
var TestSchema = new import_mongoose10.Schema(
{
name: {
type: String,
required: "TestSchema: the test name is empty"
},
description: {
type: String
},
status: {
type: String
},
browserName: {
type: String
},
browserVersion: {
type: String
},
branch: {
type: String
},
tags: {
type: [String]
},
viewport: {
type: String
},
calculatedViewport: {
type: String
},
os: {
type: String
},
app: {
type: import_mongoose10.Schema.Types.ObjectId,
ref: "VRSApp",
required: [true, 'TestSchema: The "app" field must be required']
},
blinking: {
type: Number,
default: 0
},
updatedDate: {
type: Date
},
startDate: {
type: Date
},
checks: [
{
type: import_mongoose10.default.Schema.Types.ObjectId,
ref: "VRSCheck"
}
],
suite: {
type: import_mongoose10.Schema.Types.ObjectId,
ref: "VRSSuite"
},
run: {
type: import_mongoose10.Schema.Types.ObjectId,
ref: "VRSRun"
},
markedAs: {
type: String,
enum: ["Bug", "Accepted", "Unaccepted", "Partially"]
},
creatorId: {
type: import_mongoose10.Schema.Types.ObjectId,
ref: "VRSUser"
},
creatorUsername: {
type: String
},
meta: {
type: Object
}
},
{ strictQuery: true }
);
TestSchema.plugin(toJSON_plugin_default);
TestSchema.plugin(paginate_plugin_default);
TestSchema.plugin(paginateDistinct_plugin_default);
var Test = import_mongoose10.default.model("VRSTest", TestSchema);
var Test_model_default = Test;
// src/server/utils/pick.ts
var pick = (object, keys) => {
return keys.reduce((obj, key) => {
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
if (object[key] !== void 0) obj[key] = object[key];
}
return obj;
}, {});
};
var pick_default = pick;
// src/server/utils/isJSON.ts
var isJSON = (text) => {
if (!text) return false;
const isValid = /^[\],:{}\s]*$/.test(
text.replace(/\\["\\\/bfnrtu]/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, "")
);
return isValid;
};
var isJSON_default = isJSON;
// src/server/utils/catchAsync.ts
var catchAsync = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch((err) => {
return next(err);
});
};
var catchAsync_default = catchAsync;
// src/server/utils/dateToISO8601.ts
var dateToISO8601 = (date) => {
return new Date(new Date(date)).toISOString().split("T")[0];
};
var dateToISO8601_default = dateToISO8601;
// src/server/utils/ProgressBar.ts
var ProgressBar = class {
constructor(length) {
this.length = length;
this.percentLenght = parseFloat((length / 100).toString());
this.prevPercent = 0;
this.currentPercent = 0;
this.progressString = "";
}
isChange(current2) {
this.currentPercent = parseInt((current2 / this.percentLenght).toString(), 10);
if (this.prevPercent === this.currentPercent) {
return false;
}
this.prevPercent = this.currentPercent;
this.progressString += "#";
return true;
}
writeIfChange(index, count, fn, res) {
if (this.isChange(index)) {
const placeholderString = Array.from(new Array(99 - this.currentPercent)).reduce((accum) => accum += ".", "");
fn(`[${this.progressString}${placeholderString}](${index}/${count})`, res);
}
}
};
// src/server/utils/ApiError.ts
var ApiError = class extends Error {
constructor(statusCode, message, isOperational = true, stack = "") {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
if (stack) {
this.stack = stack;
} else {
Error.captureStackTrace(this, this.constructor);
}
}
};
var ApiError_default = ApiError;
// src/server/utils/removeEmptyProperties.ts
var removeEmptyProperties = (obj) => Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Object.entries(obj).filter(([_, v]) => v != null && v !== "")
);
// src/server/utils/deserializeIfJSON.ts
var import_bson2 = require("bson");
var deserializeIfJSON = (text) => {
if (isJSON_default(text)) return import_bson2.EJSON.parse(text) || void 0;
return text;
};
var deserializeIfJSON_default = deserializeIfJSON;
// src/server/utils/prettyCheckParams.ts
var prettyCheckParams = (result) => {
if (!result.domDump) {
return JSON.stringify(result);
}
const dump = JSON.parse(result.domDump);
const resObs = { ...result };
delete resObs.domDump;
resObs.domDump = `${JSON.stringify(dump).substr(0, 20)}... and about ${dump.length} items]`;
return JSON.stringify(resObs);
};
var prettyCheckParams_default = prettyCheckParams;
// src/server/utils/waitUntil.ts
var waitUntil = async (cb, attempts = 5, interval = 700) => {
let result = false;
let iteration = 0;
while (result === false) {
result = await cb();
await new Promise((r) => setTimeout(r, interval));
iteration += 1;
if (iteration > attempts) {
result = true;
}
}
return result;
};
// src/server/utils/paramsGuard.ts
var paramsGuard = (params, functionName, schema) => {
const result = schema.safeParse(params);
if (result.success) {
return true;
} else {
const errorDetails = result.error.format();
throw new Error(`
Invalid '${functionName}' parameters: ${JSON.stringify(errorDetails)}
error: ${result.error.stack || result.error}
params: ${JSON.stringify(params, null, 2)}
`);
}
};
// src/server/utils/ident.ts
var ident = ["name", "viewport", "browserName", "os", "app", "branch"];
// src/server/utils/buildIdentObject.ts
var MissingIdentFieldError = class extends Error {
constructor(field) {
super(`Missing required ident field: ${field}`);
this.name = "MissingIdentFieldError";
}
};
var buildIdentObject = (params) => {
const result = {};
for (const key of ident) {
if (key in params && params[key] !== void 0) {
result[key] = params[key];
} else {
throw new MissingIdentFieldError(key);
}
}
return result;
};
// src/server/utils/calculateAcceptedStatus.ts
var calculateAcceptedStatus = async function calculateAcceptedStatus2(testId) {
const checksInTest = await Check_model_default.find({ test: testId });
const statuses = checksInTest.map((x) => x.markedAs);
if (statuses.length < 1) {
return "Unaccepted";
}
let testCalculatedStatus = "Unaccepted";
if (statuses.some((x) => x === "accepted")) {
testCalculatedStatus = "Partially";
}
if (statuses.every((x) => x === "accepted")) {
testCalculatedStatus = "Accepted";
}
return testCalculatedStatus;
};
// src/server/utils/subDays.ts
var subDays = (date, days) => {
const result = new Date(date);
result.setDate(result.getDate() - days);
return result;
};
var subDays_default = subDays;
// src/server/utils/errMsg.ts
var errMsg = (e) => {
return String(e instanceof Error ? e.stack : e);
};
// src/server/lib/logger.ts
var import_winston = __toESM(require("winston"));
var import_winston_mongodb = require("winston-mongodb");
var import_chalk = require("chalk");
// src/server/utils/formatISOToDateTime.ts
function formatISOToDateTime(isoDateString) {
const date = new Date(isoDateString);
return `${date.toISOString().slice(0, 10)} ${date.toTimeString().slice(0, 8)}`;
}
var formatISOToDateTime_default = formatISOToDateTime;
// src/server/config.ts
var import_fs = __toESM(require("fs"));
var import_dotenv2 = __toESM(require("dotenv"));
// package.json
var version = "2.2.26-alpha.0";
// src/server/config.ts
var import_crypto2 = __toESM(require("crypto"));
// src/server/envConfig.ts
var import_envalid = require("envalid");
var import_crypto = __toESM(require("crypto"));
var import_path = __toESM(require("path"));
var import_dotenv = __toESM(require("dotenv"));
import_dotenv.default.config();
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = "production";
}
var env = (0, import_envalid.cleanEnv)(process.env, {
NODE_ENV: (0, import_envalid.str)({ choices: ["development", "production", "test"] }),
SYNGRISI_DB_URI: (0, import_envalid.str)({ default: "mongodb://127.0.0.1:27017/SyngrisiDb" }),
SYNGRISI_APP_PORT: (0, import_envalid.port)({ default: 3e3 }),
SYNGRISI_IMAGES_PATH: (0, import_envalid.str)({ default: import_path.default.join(process.cwd(), "./.snapshots-images") }),
SYNGRISI_TMP_DIR: (0, import_envalid.str)({ default: import_path.default.join(process.cwd(), ".tmp") }),
SYNGRISI_HTTP_LOG: (0, import_envalid.bool)({ default: false }),
SYNGRISI_COVERAGE: (0, import_envalid.bool)({ default: false }),
SYNGRISI_HOSTNAME: (0, import_envalid.host)({ default: "localhost" }),
SYNGRISI_AUTH: (0, import_envalid.bool)({ default: true }),
SYNGRISI_TEST_MODE: (0, import_envalid.bool)({ default: false }),
SYNGRISI_DISABLE_FIRST_RUN: (0, import_envalid.bool)({ default: false }),
MONGODB_ROOT_USERNAME: (0, import_envalid.str)({ default: "" }),
MONGODB_ROOT_PASSWORD: (0, import_envalid.str)({ default: "" }),
LOGLEVEL: (0, import_envalid.str)({ choices: ["error", "warn", "info", "verbose", "debug", "silly"], default: "debug" }),
SYNGRISI_PAGINATION_SIZE: (0, import_envalid.num)({ default: 50 }),
SYNGRISI_DISABLE_DEV_CORS: (0, import_envalid.bool)({ default: true, devDefault: true }),
SYNGRISI_SESSION_STORE_KEY: (0, import_envalid.str)({ default: import_crypto.default.randomBytes(64).toString("hex") }),
SYNGRISI_LOG_LEVEL: (0, import_envalid.str)({ default: "debug" }),
// trunk features
SYNGRISI_TRUNK_FEATURE_AI_SEVERITY: (0, import_envalid.bool)({ default: false }),
SYNGRISI_AI_KEY: (0, import_envalid.str)({ default: "" }),
OPENAI_API_BASE_URL: (0, import_envalid.str)({ default: "https://api.openai.com/v1" }),
OPENAI_API_KEY: (0, import_envalid.str)({ default: "" })
});
// src/server/data/devices.json
var devices_default = [
{
os: "ios",
os_version: "16",
device: "iPhone 14 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 14 Pro",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 14 Plus",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 14",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 12 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 12 Pro",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 12 Mini",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPhone 11 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone XS",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 13 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 13 Pro",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 13 Mini",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 13",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 11 Pro",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 11",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone XS",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 12 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 12 Pro",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 12 Mini",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 12",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 11 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPhone 11",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone XS",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone 11 Pro Max",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone 11 Pro",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone 11",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone XS",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone XS Max",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone XR",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone XR",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone X",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone 8",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone 8",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone 8",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone 8",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone 8 Plus",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone 8 Plus",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone 7",
realMobile: true
},
{
os: "ios",
os_version: "10",
device: "iPhone 7",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPhone 6S",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone 6S",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone 6S Plus",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone 6",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPhone SE 2022",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPhone SE 2020",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPhone SE",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPad Air 4",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPad 9th",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPad Pro 12.9 2022",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPad Pro 12.9 2020",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPad Pro 11 2022",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPad 10th",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPad Air 5",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPad Pro 12.9 2021",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPad Pro 12.9 2020",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPad Pro 11 2021",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad Pro 12.9 2020",
realMobile: true
},
{
os: "ios",
os_version: "16",
device: "iPad 8th",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPad Pro 12.9 2018",
realMobile: true
},
{
os: "ios",
os_version: "15",
device: "iPad Mini 2021",
realMobile: true
},
{
os: "ios",
os_version: "14",
device: "iPad 8th",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad Pro 12.9 2018",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad Pro 11 2020",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad Mini 2019",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad Air 2019",
realMobile: true
},
{
os: "ios",
os_version: "13",
device: "iPad 7th",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPad Pro 12.9 2018",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPad Pro 11 2018",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPad Mini 2019",
realMobile: true
},
{
os: "ios",
os_version: "12",
device: "iPad Air 2019",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPad Pro 9.7 2016",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPad Pro 12.9 2017",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPad Mini 4",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPad 6th",
realMobile: true
},
{
os: "ios",
os_version: "11",
device: "iPad 5th",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Samsung Galaxy S22 Ultra",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Samsung Galaxy S22 Plus",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Samsung Galaxy S22",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Samsung Galaxy S21",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy S21 Ultra",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy S21",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy S21 Plus",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy S20",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy S20 Plus",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy S20 Ultra",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy M52",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy M32",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy A52",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy Note 20 Ultra",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy Note 20",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy A51",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy A11",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy S9 Plus",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy S10e",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy S10 Plus",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy S10",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy Note 10 Plus",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy Note 10",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy A10",
realMobile: true
},
{
os: "android",
os_version: "8.1",
device: "Samsung Galaxy Note 9",
realMobile: true
},
{
os: "android",
os_version: "8.1",
device: "Samsung Galaxy J7 Prime",
realMobile: true
},
{
os: "android",
os_version: "8.0",
device: "Samsung Galaxy S9 Plus",
realMobile: true
},
{
os: "android",
os_version: "8.0",
device: "Samsung Galaxy S9",
realMobile: true
},
{
os: "android",
os_version: "7.1",
device: "Samsung Galaxy Note 8",
realMobile: true
},
{
os: "android",
os_version: "7.1",
device: "Samsung Galaxy A8",
realMobile: true
},
{
os: "android",
os_version: "7.0",
device: "Samsung Galaxy S8 Plus",
realMobile: true
},
{
os: "android",
os_version: "7.0",
device: "Samsung Galaxy S8",
realMobile: true
},
{
os: "android",
os_version: "6.0",
device: "Samsung Galaxy S7",
realMobile: true
},
{
os: "android",
os_version: "5.0",
device: "Samsung Galaxy S6",
realMobile: true
},
{
os: "android",
os_version: "13.0",
device: "Google Pixel 7 Pro",
realMobile: true
},
{
os: "android",
os_version: "13.0",
device: "Google Pixel 7",
realMobile: true
},
{
os: "android",
os_version: "13.0",
device: "Google Pixel 6 Pro",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Google Pixel 6 Pro",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Google Pixel 6",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Google Pixel 5",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Google Pixel 5",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Google Pixel 4",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Google Pixel 4 XL",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Google Pixel 4",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Google Pixel 3",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Google Pixel 3a XL",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Google Pixel 3a",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Google Pixel 3 XL",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Google Pixel 3",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Google Pixel 2",
realMobile: true
},
{
os: "android",
os_version: "8.0",
device: "Google Pixel 2",
realMobile: true
},
{
os: "android",
os_version: "7.1",
device: "Google Pixel",
realMobile: true
},
{
os: "android",
os_version: "6.0",
device: "Google Nexus 6",
realMobile: true
},
{
os: "android",
os_version: "4.4",
device: "Google Nexus 5",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "OnePlus 9",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "OnePlus 8",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "OnePlus 7T",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "OnePlus 7",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "OnePlus 6T",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Xiaomi Redmi Note 11",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Xiaomi Redmi Note 9",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Xiaomi Redmi Note 8",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Xiaomi Redmi Note 7",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Vivo Y21",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Vivo V21",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Vivo Y50",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Oppo Reno 6",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Oppo A96",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Oppo Reno 3 Pro",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Motorola Moto G71 5G",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Motorola Moto G9 Play",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Motorola Moto G7 Play",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Huawei P30",
realMobile: true
},
{
os: "android",
os_version: "12.0",
device: "Samsung Galaxy Tab S8",
realMobile: true
},
{
os: "android",
os_version: "11.0",
device: "Samsung Galaxy Tab S7",
realMobile: true
},
{
os: "android",
os_version: "10.0",
device: "Samsung Galaxy Tab S7",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy Tab S6",
realMobile: true
},
{
os: "android",
os_version: "9.0",
device: "Samsung Galaxy Tab S5e",
realMobile: true
},
{
os: "android",
os_version: "8.1",
device: "Samsung Galaxy Tab S4",
realMobile: true
}
];
// src/server/config.ts
var customDevicesPath = "./server/data/custom_devices.json";
var logsFolder = "./logs";
import_dotenv2.default.config();
var config = {
version,
// this isn't used
getDevices: async () => {
if (import_fs.default.existsSync(customDevicesPath)) {
return [...devices_default, ...(await import(customDevicesPath)).default];
}
return devices_default;
},
defaultImagesPath: env.SYNGRISI_IMAGES_PATH,
connectionString: env.SYNGRISI_DB_URI || "mongodb://127.0.0.1:27017/SyngrisiDb",
host: env.SYNGRISI_HOSTNAME,
port: env.SYNGRISI_APP_PORT || 3e3,
backupsFolder: "./backups",
enableHttpLogger: env.SYNGRISI_HTTP_LOG,
httpLoggerFilePath: `${logsFolder}/http.log`,
storeSessionKey: env.SYNGRISI_SESSION_STORE_KEY || import_crypto2.default.randomBytes(64).toString("hex"),
codeCoverage: env.SYNGRISI_COVERAGE,
disableCors: env.SYNGRISI_DISABLE_DEV_CORS,
fileUploadMaxSize: 50 * 1024 * 1024,
testMode: env.SYNGRISI_TEST_MODE,
jsonLimit: "50mb",
tmpDir: env.SYNGRISI_TMP_DIR,
helmet: {
crossOriginEmbedderPolicy: false,
crossOriginResourcePolicy: false,
crossOriginOpenerPolicy: false,
contentSecurityPolicy: {
directives: {
// frameAncestors: ["'self'", "vscode-webview:", "vscode-resource:", "https:", "http:"],
// frameSrc: ["'self'", "vscode-webview:", "https:", "http:"],
// scriptSrc: ["'self'", "'unsafe-inline'"],
// styleSrc: ["'self'", "'unsafe-inline'"]
defaultSrc: ["'self'", "*", "'unsafe-inline'", "'unsafe-eval'", "data:", "blob:"],
frameAncestors: ["'self'", "*"],
frameSrc: ["'self'", "*"],
scriptSrc: ["'self'", "*", "'unsafe-inline'", "'unsafe-eval'"],
styleSrc: ["'self'", "*", "'unsafe-inline'"],
imgSrc: ["'self'", "*", "data:", "blob:"],
fontSrc: ["'self'", "*", "data:"],
connectSrc: ["'self'", "*"]
}
}
}
};
if (!import_fs.default.existsSync(config.defaultImagesPath)) {
import_fs.default.mkdirSync(config.defaultImagesPath, { recursive: true });
}
if (!import_fs.default.existsSync(logsFolder)) {
import_fs.default.mkdirSync(logsFolder, { recursive: true });
}
// src/server/lib/logger.ts
var import_path2 = __toESM(require("path"));
var logLevel = env.SYNGRISI_LOG_LEVEL;
function getScriptLine() {
const stack = new Error().stack;
if (stack) {
const stackLines = stack.split("\n");
let loggerLineIndex = -1;
for (let i = 0; i < stackLines.length; i++) {
if (stackLines[i].includes("lib/logger")) {
loggerLineIndex = i;
}
}
const targetLineIndex = loggerLineIndex + 1;
if (targetLineIndex >= 0 && targetLineIndex < stackLines.length) {
const targetLine = stackLines[targetLineIndex];
const match = targetLine.match(/at\s+(?:.+\s+\()?(.+):(\d+):(\d+)\)?/);
if (match) {
const scriptPath = match[1];
const relativePath = import_path2.default.relative(process.cwd(), scriptPath);
const lineNumber = match[2];
return `${relativePath}:${lineNumber}`;
}
}
}
return "unknown";
}
function createWinstonLogger(opts) {
return import_winston.default.createLogger({
transports: [
new import_winston.default.transports.Console({
level: logLevel || "silly",
format: import_winston.default.format.combine(
import_winston.default.format.colorize(),
import_winston.default.format.timestamp(),
import_winston.default.format.ms(),
import_winston.default.format.metadata(),
import_winston.default.format.printf((info2) => {
const user = info2.metadata.user ? (0, import_chalk.blue)(` <${info2.metadata.user}>`) : "";
const ref = info2.metadata.ref ? (0, import_chalk.gray)(` ${info2.metadata.ref}`) : "";
const msgType = info2.metadata.msgType ? ` ${info2.metadata.msgType}` : "";
const itemType = info2.metadata.itemType ? (0, import_chalk.magenta)(` ${info2.metadata.itemType}`) : "";
const scope = info2.metadata.scope ? (0, import_chalk.magenta)(` [${info2.metadata.scope}] `) : (0, import_chalk.magenta)(` [${getScriptLine()}] `);
const msg = typeof info2.message === "object" ? `
${JSON.stringify(info2.message, null, 2)}` : info2.message;
return `${info2.level} ${scope}${formatISOToDateTime_default(info2.metadata.timestamp)} ${info2.metadata.ms}${user}${ref}${msgType}${itemType} '${msg}'`;
}),
import_winston.default.format.padLevels()
)
}),
new import_winston.default.transports.MongoDB({
level: logLevel || "debug",
format: import_winston.default.format.combine(
import_winston.default.format.timestamp(),
import_winston.default.format.json(),
import_winston.default.format.metadata()
),
options: {
useUnifiedTopology: true
},
db: opts.dbConnectionString,
collection: "vrslogs"
})
]
});
}
var Logger = class _Logger {
constructor(opts = { dbConnectionString: config.connectionString }) {
this.winstonLogger = createWinstonLogger(opts);
}
static mergeMeta(objects) {
return objects.reduce((acc, obj) => {
return { ...acc, ...obj };
}, {});
}
log(severity, msg, meta) {
const mergedMeta = _Logger.mergeMeta(meta);
if (!mergedMeta.scope) {
mergedMeta.scope = getScriptLine();
}
const formattedMsg = typeof msg === "object" ? JSON.stringify(msg, null, 2) : msg;
this.winstonLogger.log(severity, formattedMsg, mergedMeta);
}
error(msg, ...meta) {
let message = String(msg);
let code = 0;
if (msg instanceof Object) {
message = JSON.stringify(msg);
}
if (msg instanceof Error) {
message = msg.stack;
}
if (msg instanceof ApiError_default) {
code = msg.statusCode;
}
this.log("error", `${code !== 0 ? "[" + code + "]" : ""}${message}
stacktrace: ${new Error().stack}`, meta);
}
warn(msg, ...meta) {
this.log("warn", `${msg}
stacktrace: ${new Error().stack}`, meta);
}
info(msg, ...meta) {
this.log("info", msg, meta);
}
verbose(msg, ...meta) {
this.log("verbose", msg, meta);
}
debug(msg, ...meta) {
this.log("debug", msg, meta);
}
silly(msg, ...meta) {
this.log("silly", msg, meta);
}
};
var logger_default = new Logger();
// src/seeds/initialAppSettings.json
var initialAppSettings_default = [
{
name: "first_run",
label: "First Run",
description: "Indicates if the application is running the first time",
type: "Boolean",
value: "true",
enabled: true
},
{
name: "authentication",
label: "Authentication",
description: "Enable application authentication",
type: "Boolean",
value: "false",
enabled: true
}
];
// src/server/lib/AppSettings/AppSettings.ts
var AppSettings2 = class {
constructor() {
this.model = AppSettings_model_default;
this.cache = null;
}
async init() {
this.cache = await this.model.find().lean().exec();
return this;
}
ensureInitialized() {
if (!this.cache) {
throw new Error("AppSettings is not initialized. Please call init() before using this method.");
}
}
async count() {
this.ensureInitialized();
return this.model.countDocuments().exec();
}
async loadInitialFromFile() {
this.ensureInitialized();
const settings = initialAppSettings_default;
await this.model.insertMany(settings);
this.cache = settings;
}
async get(name) {
this.ensureInitialized();
return this.cache.find((x) => x.name === name) || this.model.findOne({ name }).exec();
}
async set(name, value) {
this.ensureInitialized();
const item = await this.model.findOneAndUpdate({ name }, { value });
await item.save();
const cachedItem = this.cache.find((x) => x.name === name);
if (cachedItem) {
cachedItem["value"] = value;
}
}
async enable(name) {
this.ensureInitialized();
const item = await this.model.findOneAndUpdate({ name }, { enabled: true });
await item.save();
const cachedItem = this.cache.find((x) => x.name === name);
if (cachedItem) {
cachedItem["enabled"] = true;
}
}
async disable(name) {
this.ensureInitialized();
const item = await this.model.findOneAndUpdate({ name }, { enabled: false });
await item.save();
const cachedItem = this.cache.find((x) => x.name === name);
if (cachedItem) {
cachedItem["enabled"] = false;
}
}
async isAuthEnabled() {
this.ensureInitialized();
return env.SYNGRISI_AUTH || (await this.get("authentication"))?.value === "true";
}
async isFirstRun() {
this.ensureInitialized();
return (await this.get("first_run"))?.value === "true";
}
};
var appSettings = new AppSettings2().init();
// src/server/controllers/auth.controller.ts
function getApiKey() {
return import_uuid_apikey.default.create().apiKey;
}
var apikey = catchAsync_default(async (req, res) => {
const logOpts5 = {
user: req?.user?.username || void 0,
scope: "apikey",
msgType: "GENERATE_API"
};
const apiKey = getApiKey();
logger_default.debug(
`generate API Key for user: '${req.user?.username}'`,
logOpts5
);
const hash = (0, import_hasha.default)(apiKey);
if (!req.user?.username) throw new Error(`Username is empty`);
const user = await User_model_default.findOne({ username: req.user.username });
if (!user) throw new Error(`cannot find the user with username: '${req.user.username}'`);
user.apiKey = hash;
await user.save();
res.status(200).json({ apikey: apiKey });
});
var login = catchAsync_default(async (req, res, next) => {
const logOpts5 = {
scope: "login",
msgType: "AUTHENTICATION"
};
import_passport.default.authenticate("local", (err, user, info2) => {
if (err) {
logger_default.error(`Authentication error: '${err}'`, logOpts5);
return res.status(import_http_status.default.UNAUTHORIZED).json({ message: "authentication error" });
}
if (!user) {
logger_default.error(`Authentication error: '${info2.message}'`, logOpts5);
return res.status(import_http_status.default.UNAUTHORIZED).json({ message: `Authentication error: '${info2.message}'` });
}
req.logIn(user, (e) => {
if (e) {
logger_default.error(e, logOpts5);
return next(e);
}
logger_default.info("user is logged in", { user: user.username });
return res.status(200).json({ message: "success" });
});
})(req, res, next);
});
var logout = catchAsync_default(async (req, res) => {
const logOpts5 = {
scope: "logout",
msgType: "AUTHENTICATION"
};
try {
logger_default.debug(`try to log out user: '${req?.user?.username}'`, logOpts5);
await req.logout({}, () => res.status(import_http_status.default.OK).json({ message: "success" }));
} catch (e) {
logger_default.error(e, logOpts5);
res.status(import_http_status.default.INTERNAL_SERVER_ERROR).json({ message: "fail" });
}
});
var changePassword = catchAsync_default(async (req, res) => {
const logOpts5 = {
scope: "changePassword",
msgType: "CHANGE_PASSWORD",
itemType: "user",
ref: req?.user?.username
};
const { currentPassword, newPassword } = req.body;
const username = req?.user?.username;
logger_default.debug(`change password for '${username}', params: '${JSON.stringify(req.body)}'`, logOpts5);
const user = await User_model_default.findOne({ username });
if (!user) {
logger_default.error("user is not logged in", logOpts5);
return res.status(import_http_status.default.UNAUTHORIZED).json({ message: "user is not logged in" });
}
try {
await user.changePassword(currentPassword, newPassword);
} catch (e) {
logger_default.error(e, logOpts5);
return res.status(import_http_status.default.INTERNAL_SERVER_ERROR).json({ message: errMsg(e) });
}
logger_default.debug(`password was successfully changed for user: ${req.user?.username}`, logOpts5);
return res.status(200).json({ message: "success" });
});
var changePasswordFirstRun = catchAsync_default(async (req, res) => {
const logOpts5 = {
scope: "changePasswordFirstRun",
msgType: "CHANGE_PASSWORD_FIRST_RUN",
itemType: "user",
ref: req?.user?.username
};
const { newPassword } = req.body;
const AppSettings3 = await appSettings;
if (await AppSettings3.isAuthEnabled() && await AppSettings3.isFirstRun()) {
logger_default.debug(`first run, change password for default 'Administrator', params: '${JSON.stringify(req.body)}'`, logOpts5);
const user = await User_model_default.findOne({ username: "Administrator" }).exec();
if (!user) throw new Error(`cannot find the Administrator`);
logOpts5.ref = String(user?.username);
await user.setPassword(newPassword);
await user.save();
logger_default.debug("password was successfully changed for default Administrator", logOpts5);
await AppSettings3.set("first_run", false);
res.status(200).json({ message: "success" });
} else {
logger_default.error(`trying to use first run API with no first run state, auth: '${await AppSettings3.isAuthEnabled()}', global settings: '${JSON.stringify(await AppSettings3.get("first_run"))}'`, logOpts5);
res.status(import_http_status.default.FORBIDDEN).json({ message: "forbidden" });
}
});
// src/server/utils/validateRequest.ts
var import_http_status2 = __toESM(require("http-status"));
var import_zod = require("zod");
// src/server/utils/ServiceResponse.ts
var ServiceResponse = class {
constructor(status3, message, responseObject, statusCode) {
this.success = status3 === 0 /* Success */;
this.message = message;
this.responseObject = responseObject;
this.statusCode = statusCode;
}
};
// src/server/utils/validateRequest.ts
var logOpts = {
scope: "validateRequests",
itemType: "type",
msgType: "VALIDATION"
};
function getReceivedValueFromRequest(request, path6) {
let currentValue = request;
path6.forEach((segment) => {
currentValue = currentValue[segment];
});
return currentValue;
}
var validateRequest = (schema, endpoint = "") => (req, res, next) => {
try {
schema.parse({
body: req.body,
query: req.query,
params: req.params
});
next();
} catch (err) {
if (err instanceof import_zod.ZodError) {
const errors = err.errors.map((e) => {
const receivedValue = getReceivedValueFromRequest(
{ body: req.body, query: req.query, params: req.params },
e.path
);
return `
Error path: '${e.path.join(".")}':
Error ${e.message}, but received ${JSON.stringify(receivedValue)}`;
}).join(", ");
const errorMessage = ` ${endpoint ? '\nValidation error in the endpoint: "' + endpoint + '"' : ""}${errors},
HTTP PROPERTIES:
body: ${JSON.stringify(req.body, null, " ")},
query: ${JSON.stringify(req.query, null, " ")},
params: ${JSON.stringify(req.params, null, " ")}`;
const statusCode = import_http_status2.default.BAD_REQUEST;
logger_default.error(errorMessage, logOpts);
res.status(statusCode).send(new ServiceResponse(1 /* Failed */, errorMessage, null, statusCode));
} else {
logger_default.error(`Unexpected error: ${errMsg(err)}`, logOpts);
next(err);
}
}
};
// src/server/schemas/Auth.schema.ts
var import_zod_to_openapi2 = require("@asteasolutions/zod-to-openapi");
var import_zod4 = require("zod");
// src/server/schemas/utils/commonValidations.ts
var import_zod3 = require("zod");
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/common/Version.schema.ts
var import_zod2 = require("zod");
var VersionSchema = import_zod2.z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be in the format "x.y.z"').transform((value) => {
const parts = value.split(".");
return {
major: parseInt(parts[0]),
minor: parseInt(parts[1]),
patch: parseInt(parts[2])
};
});
var Version_schema_default = VersionSchema;
// src/server/schemas/utils/commonValidations.ts
(0, import_zod_to_openapi.extendZodWithOpenApi)(import_zod3.z);
var mongooseIdRegex = /^[0-9a-fA-F]{24}$/;
var id = import_zod3.z.string().regex(mongooseIdRegex, {
message: "Invalid Mongoose ObjectId format: /^[0-9a-fA-F]{24}$/"
}).openapi({
description: "baseline ID",
example: "6bbF35cAB3C59dA969edAe79"
});
var commonValidations = {
id,
version: Version_schema_default.openapi({ example: "1.1.2" }),
positiveNumberString: import_zod3.z.string().refine((value) => {
const num2 = Number(value);
return Number.isInteger(num2) && num2 >= 0;
}, {
message: "String must be a positive number or 0"
}),
password: import_zod3.z.string().min(6).regex(/(?=.*[0-9])/, "Password must include a number").regex(/(?=.*[a-z])/, "Password must include a lowercase letter").regex(/(?=.*[A-Z])/, "Password must include an uppercase letter").refine((value) => {
return /(?=.*[!@#$%^&*(),.?":{}|<>-])/.test(value);
}, {
message: "Password must include a special symbol"
}).openapi({ example: "Aa1!IJASSNOJ" }),
username: import_zod3.z.string().min(1).openapi({ example: "john.doe@example.com" }),
// TODO: workaround TBD
date: import_zod3.z.string().refine((val) => {
const date = new Date(val);
return !isNaN(date.getTime());
}, {
message: "Invalid date format"
}),
paramsId: { params: import_zod3.z.object({ id }) },
paramsTestId: { params: import_zod3.z.object({ testid: id }) },
success: import_zod3.z.object({
message: import_zod3.z.literal("success")
})
};
// src/server/schemas/Auth.schema.ts
(0, import_zod_to_openapi2.extendZodWithOpenApi)(import_zod4.z);
var AuthLoginSchema = import_zod4.z.object({
username: commonValidations.username,
password: commonValidations.password
});
var AuthLoginSuccessRespSchema = commonValidations.success;
var AuthChangePasswordSchema = import_zod4.z.object({
currentPassword: commonValidations.password,
newPassword: commonValidations.password
});
var AuthChangePasswordFirstRunSchema = import_zod4.z.object({
// currentPassword: commonValidations.password,
newPassword: commonValidations.password
});
var AuthApiKeyRespSchema = import_zod4.z.object({
apikey: import_zod4.z.string().regex(/^[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}$/).openapi({ example: "J3QQ400-H7H2V00-2HCH400-M3HK800" })
});
var AuthLogoutRespSchema = commonValidations.success;
// src/server/api-docs/openAPIResponseBuilders.ts
var import_http_status3 = __toESM(require("http-status"));
// src/server/api-docs/serviceResponse.ts
var import_zod5 = require("zod");
var ServiceResponsePaginationSchema = (dataSchema) => import_zod5.z.object({
results: import_zod5.z.array(dataSchema.optional()),
page: import_zod5.z.number().openapi({ example: 1 }),
limit: import_zod5.z.number().openapi({ example: 10 }),
totalPages: import_zod5.z.number().openapi({ example: 2 }),
totalResults: import_zod5.z.number().openapi({ example: 12 }),
timestamp: import_zod5.z.number().openapi({ example: 1718035239731968 })
});
// src/server/api-docs/openAPIResponseBuilders.ts
function createApiResponse(schema, description, statusCode = import_http_status3.default.OK) {
return {
[statusCode]: {
description,
content: {
"application/json": {
schema
}
}
}
};
}
function createApiEmptyResponse(description, statusCode = import_http_status3.default.OK) {
return {
[statusCode]: {
description
}
};
}
function createPaginatedApiResponse(schema, description, statusCode = import_http_status3.default.OK) {
return {
[statusCode]: {
description,
content: {
"application/json": {
schema: ServiceResponsePaginationSchema(schema)
}
}
}
};
}
// src/server/schemas/SkipValid.schema.ts
var import_zod6 = require("zod");
var SkipValid = import_zod6.z.any();
// src/server/schemas/utils/createRequestBodySchema.ts
var import_zod7 = require("zod");
var createRequestBodySchema = (schema) => import_zod7.z.object({ body: schema });
// src/server/schemas/utils/createRequestOpenApiBodySchema.ts
var createRequestOpenApiBodySchema = (schema) => ({
content: {
"application/json": { schema }
}
});
// src/server/routes/v1/auth.route.ts
var registry = new import_zod_to_openapi3.OpenAPIRegistry();
var router = import_express.default.Router();
registry.registerPath({
method: "get",
path: "/v1/auth/logout",
summary: "Logout the current user.",
tags: ["Auth"],
responses: createApiEmptyResponse("Logout success")
});
router.get(
"/logout",
validateRequest(SkipValid, "get, /v1/auth/logout"),
logout
);
registry.registerPath({
method: "get",
path: "/v1/auth/apikey",
summary: "Generate a new API key for the current user.",
tags: ["Auth"],
responses: createApiResponse(AuthApiKeyRespSchema, "API Key generated")
});
router.get(
"/apikey",
validateRequest(SkipValid, "get, /v1/auth/apikey"),
apikey
);
registry.registerPath({
method: "post",
path: "/v1/auth/login",
summary: "Login a user with username and password.",
tags: ["Auth"],
request: { body: createRequestOpenApiBodySchema(AuthLoginSchema) },
responses: createApiResponse(AuthLoginSuccessRespSchema, "Login success")
});
router.post(
"/login",
validateRequest(createRequestBodySchema(AuthLoginSchema), "post, /v1/auth/login"),
login
);
registry.registerPath({
method: "post",
path: "/v1/auth/change",
summary: "Change the password for the current user.",
tags: ["Auth"],
request: { body: createRequestOpenApiBodySchema(AuthChangePasswordSchema) },
responses: createApiEmptyResponse("Password changed")
});
router.post(
"/change",
validateRequest(createRequestBodySchema(AuthChangePasswordSchema), "post, /v1/auth/change"),
changePassword
);
registry.registerPath({
method: "post",
path: "/v1/auth/change_first_run",
summary: "Change the password for the first run.",
tags: ["Auth"],
request: { body: createRequestOpenApiBodySchema(AuthChangePasswordFirstRunSchema) },
responses: createApiEmptyResponse("First run password changed")
});
router.post(
"/change_first_run",
validateRequest(createRequestBodySchema(AuthChangePasswordFirstRunSchema), "post, /v1/auth/change_first_run"),
changePasswordFirstRun
);
var auth_route_default = router;
// src/server/routes/v1/app.route.ts
var import_express2 = __toESM(require("express"));
var import_zod_to_openapi7 = require("@asteasolutions/zod-to-openapi");
// src/server/controllers/baseline.controller.ts
var import_http_status9 = __toESM(require("http-status"));
// src/server/services/run.service.ts
var run_service_exports = {};
__export(run_service_exports, {
remove: () => remove2
});
// src/server/services/test.service.ts
var test_service_exports = {};
__export(test_service_exports, {
accept: () => accept,
queryTests: () => queryTests,
queryTestsDistinct: () => queryTestsDistinct,
remove: () => remove
});
var queryTests = async (filter, options) => {
const tests = await Test_model_default.paginate(filter, options);
return tests;
};
var queryTestsDistinct = async (filter, options) => {
const tests = await Test_model_default.paginateDistinct({ filter: filter ? JSON.stringify(filter) : null }, options);
return tests;
};
var remove = async (id2, user) => {
const logOpts5 = {
scope: "removeTest",
itemType: "test",
ref: id2,
user: user?.username,
msgType: "REMOVE"
};
logger_default.info(`remove test with, id: '${id2}', user: '${user.username}'`, logOpts5);
try {
logger_default.debug(`try to delete all checks associated to test with ID: '${id2}'`, logOpts5);
const checks = await Check_model_default.find({ test: id2 });
for (const check of checks) {
await check_service_exports.remove(check._id, user);
}
return Test_model_default.findByIdAndDelete(id2);
} catch (e) {
logger_default.error(`cannot remove test with id: ${id2} error: ${e instanceof Error ? e.stack : String(e)}`, logOpts5);
throw new Error();
}
};
var accept = async (id2, user) => {
const logOpts5 = {
scope: "acceptTest",
itemType: "test",
ref: id2,
user: user?.username,
msgType: "ACCEPT"
};
logger_default.info(`accept test with, id: '${id2}', user: '${user.username}'`, logOpts5);
const checks = await Check_model_default.find({ test: id2 }).exec();
for (const check of checks) {
await check_service_exports.accept(check._id, String(check.actualSnapshotId), user);
}
return { message: "success" };
};
// src/server/services/run.service.ts
var import_http_status4 = __toESM(require("http-status"));
var remove2 = async (id2, user) => {
const logOpts5 = {
scope: "removeRun",
itemType: "run",
ref: id2,
user: user?.username,
msgType: "REMOVE"
};
logger_default.info(`remove run with, id: '${id2}', user: '${user.username}'`, logOpts5);
const tests = await Test_model_default.find({ run: id2 }).exec();
for (const test of tests) {
await remove(test._id, user);
}
const run = await Run_model_default.findByIdAndDelete(id2).exec();
if (!run) {
throw new ApiError_default(import_http_status4.default.NOT_FOUND, `cannot remove run with id: '${id2}', not found`);
}
return run;
};
// src/server/services/suite.service.ts
var suite_service_exports = {};
__export(suite_service_exports, {
remove: () => remove3
});
var import_http_status5 = __toESM(require("http-status"));
var remove3 = async (id2, user) => {
const logOpts5 = {
scope: "removeSuite",
itemType: "suite",
ref: id2,
user: user?.username,
msgType: "REMOVE"
};
logger_default.info(`remove suite with, id: '${id2}', user: '${user.username}'`, logOpts5);
const tests = await Test_model_default.find({ suite: id2 }).exec();
for (const test of tests) {
await remove(test._id, user);
}
const suite = await Suite_model_default.findByIdAndDelete(id2).exec();
if (!suite) throw new ApiError_default(import_http_status5.default.NOT_FOUND, `cannot remove suite with id: '${id2}', not found`);
return suite;
};
// src/server/services/logs.service.ts
var logs_service_exports = {};
__export(logs_service_exports, {
createLogs: () => createLogs,
distinct: () => distinct,
queryLogs: () => queryLogs
});
var queryLogs = async (filter, options) => Log_model_default.paginate(filter, options);
var distinct = async (field) => Log_model_default.distinct(field);
var createLogs = async (body) => {
logger_default[body.level || "debug"](body.message, {
user: body.user,
scope: body.scope || "test_scope",
msgType: body.msgType || "TEST_MSG_TYPE"
});
return { message: "success" };
};
// src/server/services/generic.service.ts
var generic_service_exports = {};
__export(generic_service_exports, {
get: () => get,
put: () => put
});
var import_mongoose11 = __toESM(require("mongoose"));
var get = async (modelName, filter, options) => {
const itemModel = import_mongoose11.default.model(modelName);
return itemModel.paginate(filter, options);
};
var put = async (modelName, id2, options, user) => {
const itemModel = import_mongoose11.default.model(modelName);
const logOpts5 = {
scope: "generic.service.put",
ref: id2,
itemType: modelName,
msgType: "UPDATE",
user: user?.username
};
const opts = removeEmptyProperties(options);
logger_default.debug(`start update '${modelName}' with id: '${id2}', body: '${JSON.stringify(opts)}'`, logOpts5);
const item = await itemModel.findByIdAndUpdate(id2, options).exec();
if (!item) throw new Error(`cannot find the item: ${modelName}, id: ${id2}, options: ${JSON.stringify(options)}`);
await item.save();
logger_default.debug(`baseline with id: '${id2}' and opts: '${JSON.stringify(opts)}' was updated`, logOpts5);
return item;
};
// src/server/services/app.service.ts
var app_service_exports = {};
__export(app_service_exports, {
get: () => get2
});
var get2 = async (filter, options) => App_model_default.paginate(filter, options);
// src/server/services/tasks.service.ts
var tasks_service_exports = {};
__export(tasks_service_exports, {
loadTestUser: () => loadTestUser,
screenshots: () => screenshots,
status: () => status,
task_handle_database_consistency: () => task_handle_database_consistency,
task_handle_old_checks: () => task_handle_old_checks,
task_remove_old_logs: () => task_remove_old_logs,
task_test: () => task_test
});
var import_fs2 = __toESM(require("fs"));
var import_string_table = __toESM(require("string-table"));
// src/seeds/testAdmin.json
var testAdmin_default = {
username: "Test",
firstName: "Test",
lastName: "Admin",
role: "admin",
openPassw: "123456aA-",
password: "5b8d4960316d1fb0c92498c90da6c397cdf247cae71f01467a88e2b42d7af6f5ac7ca75d3bea6e3e0078111a2e5dfc1611f9a9a8908a5a3af5bcd64c42989608977de192829bdf8ada113a60f8f0704443c659789761865e29a3103dbf0773f5bf31e4685d475ece56afaceb949b6e7467eaa287a02e4142d095bcbf84acaefe47ee080799a28188890d39d3397e285d8b46c9a0efe9517428825b64ee1ebcc96d92c084733db866c767341381b6254aaa1ef36d1bf3d24e3f5b8d8b6b4080589b130e9c90914a3da74e5b6adf5f569bfd77460abae8ae4f87c2a375397a37f09861b9e114cead0cc34fff2d631fd4294260dea17e4fe098940dbee2cb80c62eb3701d40f5b204de776b8252d55e5f567c599b1fbcdae79278d1f375a4c8244a26a3b721dbeec56c8f39b3eb810942d392aae371ea81ded6b820dd4b489566a33c495f5c291ff238d07202d2ff04c52426828e44af98ec056a42d13f4b166ec170083e2fff9efe2b8cfdde529f3bce56b8427cf2d188861808ad07fd13e073b2a804e818b2882c13f559d52420b49f301263a9de34fe22b6df4a82ae70e7e4c29c88479878d2c21fbb810532532e7ad9a28f610b63033520e703f178e7b44d3e101ec0d4339c085ccc8bb290b3cb996c75c2b8deaacba8098b9ec02c7e47542891da3bd887c31cd8e0bdfa56bb844b1703368afe8dc42d668ff2e3374b939b4f",
apiKey: "",
salt: "c6211751bdc372f491a86bcbd8e4196dc393d14e14e5f17019d5c317afd5bc27"
};
// src/server/services/tasks.service.ts
var import_path3 = __toESM(require("path"));
var stringTable = import_string_table.default;
function taskOutput(msg, res) {
res.write(`${msg.toString()}
`);
logger_default.debug(msg.toString());
}
function parseHrtimeToSeconds(hrtime) {
return (hrtime[0] + hrtime[1] / 1e9).toFixed(3);
}
var status = async (currentUser) => {
const count = await User_model_default.countDocuments().exec();
logger_default.silly(`server status: check users counts: ${count}`);
if (count > 1) {
return { alive: true, currentUser: currentUser?.username };
}
return { alive: false };
};
var screenshots = async () => {
const files = import_fs2.default.readdirSync(config.defaultImagesPath);
return files;
};
var loadTestUser = async () => {
const logOpts5 = {
itemType: "user",
msgType: "LOAD",
ref: "Administrator"
};
if (!env.SYNGRISI_TEST_MODE) {
return { message: "the feature works only in test mode" };
}
const testAdmin = await User_model_default.findOne({ username: "Test" }).exec();
if (!testAdmin) {
logger_default.info("create the test Administrator", logOpts5);
const admin = await User_model_default.create(testAdmin_default);
logger_default.info(`test Administrator with id: '${admin._id}' was created`, logOpts5);
return admin;
}
logger_default.info(`test admin is exists: ${JSON.stringify(testAdmin, null, 2)}`, logOpts5);
return { msg: `already exist '${testAdmin}'` };
};
var task_handle_database_consistency = async (options, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Content-Encoding": "none",
"x-no-compression": "true"
});
try {
const startTime = process.hrtime();
taskOutput("- starting...\n", res);
taskOutput("---------------------------------", res);
taskOutput("STAGE #1: Calculate Common stats", res);
taskOutput("get runs data", res);
const allRunsBefore = await Run_model_default.find().exec();
taskOutput("get suites data", res);
const allSuitesBefore = await Suite_model_default.find().exec();
taskOutput("get tests data", res);
const allTestsBefore = await Test_model_default.find().lean().exec();
taskOutput("get checks data", res);
const allChecksBefore = await Check_model_default.find().lean().exec();
taskOutput("get snapshots data", res);
const allSnapshotsBefore = await Snapshot_model_default.find().lean().exec();
taskOutput("get files data", res);
const allFilesBefore = (await import_fs2.promises.readdir(config.defaultImagesPath, { withFileTypes: true })).filter((item) => !item.isDirectory()).map((x) => x.name).filter((x) => x.includes(".png"));
taskOutput("-----------------------------", res);
const beforeStatTable = stringTable.create([
{ item: "suites", count: allSuitesBefore.length },
{ item: "runs", count: allRunsBefore.length },
{ item: "tests", count: allTestsBefore.length },
{ item: "checks", count: allChecksBefore.length },
{ item: "snapshots", count: allSnapshotsBefore.length },
{ item: "files", count: allFilesBefore.length }
]);
res.flush();
taskOutput(beforeStatTable, res);
taskOutput("---------------------------------", res);
taskOutput("STAGE #2: Calculate Inconsistent Items", res);
taskOutput("> calculate abandoned snapshots", res);
const abandonedSnapshots = allSnapshotsBefore.filter((sn) => !import_fs2.default.existsSync(import_path3.default.join(config.defaultImagesPath, sn.filename)));
taskOutput("> calculate abandoned files", res);
const snapshotsUniqueFiles = Array.from(new Set(allSnapshotsBefore.map((x) => x.filename)));
const abandonedFiles = [];
const progress = new ProgressBar(allFilesBefore.length);
for (const [index, file] of allFilesBefore.entries()) {
setTimeout(() => {
progress.writeIfChange(index, allFilesBefore.length, taskOutput, res);
}, 10);
if (!snapshotsUniqueFiles.includes(file.toString())) {
abandonedFiles.push(file);
}
}
taskOutput("> calculate abandoned checks", res);
const allSnapshotsBeforeIds = allSnapshotsBefore.map((x) => x._id.valueOf());
const allChecksBeforeLight = allChecksBefore.map((x) => ({
_id: x._id.valueOf(),
baselineId: x.baselineId.valueOf(),
actualSnapshotId: x.actualSnapshotId.valueOf()
}));
const abandonedChecks = [];
const progressChecks = new ProgressBar(allChecksBefore.length);
for (const [index, check] of allChecksBeforeLight.entries()) {
progressChecks.writeIfChange(index, allChecksBeforeLight.length, taskOutput, res);
if (!allSnapshotsBeforeIds.includes(check.baselineId) || !allSnapshotsBeforeIds.includes(check.actualSnapshotId.valueOf())) {
abandonedChecks.push(check._id.valueOf());
}
}
taskOutput("> calculate empty tests", res);
const checksUniqueTests = (await Check_model_default.find().lean().distinct("test").exec()).map((x) => x.valueOf());
const emptyTests = [];
for (const [index, test] of allTestsBefore.entries()) {
if (!checksUniqueTests.includes(test._id.valueOf())) {
emptyTests.push(test._id.valueOf());
}
}
taskOutput("> calculate empty runs", res);
const checksUniqueRuns = (await Check_model_default.find().distinct("run").exec()).map((x) => x.valueOf());
const emptyRuns = [];
for (const run of allRunsBefore) {
if (!checksUniqueRuns.includes(run._id.valueOf())) {
emptyRuns.push(run._id.valueOf());
}
}
taskOutput("> calculate empty suites", res);
const checksUniqueSuites = (await Check_model_default.find().distinct("suite").exec()).map((x) => x.valueOf());
const emptySuites = [];
for (const suite of allSuitesBefore) {
if (!checksUniqueSuites.includes(suite._id.valueOf())) {
emptySuites.push(suite._id.valueOf());
}
}
taskOutput("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", res);
taskOutput("Current inconsistent items:", res);
const inconsistentStatTable = stringTable.create([
{ item: "empty suites", count: emptySuites.length },
{ item: "empty runs", count: emptyRuns.length },
{ item: "empty tests", count: emptyTests.length },
{ item: "abandoned checks", count: abandonedChecks.length },
{ item: "abandoned snapshots", count: abandonedSnapshots.length },
{ item: "abandoned files", count: abandonedFiles.length }
]);
taskOutput(inconsistentStatTable, res);
if (options.clean) {
taskOutput("---------------------------------", res);
taskOutput("STAGE #3: Remove non consistent items", res);
taskOutput("> remove empty suites", res);
await Suite_model_default.deleteMany({ _id: { $in: emptySuites } });
taskOutput("> remove empty runs", res);
await Run_model_default.deleteMany({ _id: { $in: emptyRuns } });
taskOutput("> remove empty tests", res);
await Test_model_default.deleteMany({ _id: { $in: emptyTests } });
taskOutput("> remove abandoned checks", res);
await Check_model_default.deleteMany({ _id: { $in: abandonedChecks } });
taskOutput("> remove abandoned snapshots", res);
await Snapshot_model_default.deleteMany({ _id: { $in: abandonedSnapshots } });
taskOutput("> remove abandoned files", res);
await Promise.all(abandonedFiles.map((filename) => import_fs2.promises.unlink(import_path3.default.join(config.defaultImagesPath, filename))));
const allFilesAfter = import_fs2.default.readdirSync(config.defaultImagesPath, { withFileTypes: true }).filter((item) => !item.isDirectory()).map((x) => x.name).filter((x) => x.includes(".png"));
taskOutput("STAGE #4: Calculate Common stats after cleaning", res);
taskOutput("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", res);
taskOutput("Current items:", res);
const afterStatTable = stringTable.create([
{ item: "suites", count: await Suite_model_default.countDocuments() },
{ item: "runs", count: await Run_model_default.countDocuments() },
{ item: "tests", count: await Test_model_default.countDocuments() },
{ item: "checks", count: await Check_model_default.countDocuments() },
{ item: "snapshots", count: await Snapshot_model_default.countDocuments() },
{ item: "files", count: allFilesAfter.length }
]);
taskOutput(afterStatTable, res);
}
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
taskOutput(`> Done in ${elapsedSeconds} seconds, ${elapsedSeconds / 60} min`, res);
taskOutput("- end...\n", res);
} catch (e) {
const errMsg2 = e instanceof Error ? e.message : String(e);
logger_default.error(errMsg2);
taskOutput(errMsg2, res);
} finally {
res.end();
}
};
var task_remove_old_logs = async (options, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Content-Encoding": "none"
});
const trashHoldDate = subDays_default(/* @__PURE__ */ new Date(), parseInt(options.days, 10));
const filter = { timestamp: { $lt: trashHoldDate } };
const allLogsCountBefore = await Log_model_default.find({}).countDocuments();
const oldLogsCount = await Log_model_default.find(filter).countDocuments();
taskOutput(`- the count of all documents is: '${allLogsCountBefore}'
`, res);
taskOutput(`- the count of documents to be removed is: '${oldLogsCount}'
`, res);
if (options.statistics === "false") {
taskOutput(`- will remove all logs older that: '${options.days}' days, '${dateToISO8601_default(trashHoldDate)}'
`, res);
await Log_model_default.deleteMany(filter);
const allLogsCountAfter = await Log_model_default.find({}).countDocuments();
taskOutput(`- the count of all documents now is: '${allLogsCountAfter}'
`, res);
}
taskOutput("> Done", res);
res.end();
};
var task_handle_old_checks = async (options, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Content-Encoding": "none"
});
try {
const startTime = process.hrtime();
taskOutput("- starting...\n", res);
taskOutput("STAGE #1 Calculate common stats", res);
const trashHoldDate = subDays_default(/* @__PURE__ */ new Date(), parseInt(options.days, 10));
taskOutput("> get all checks data", res);
const allChecksBefore = await Check_model_default.find().lean().exec();
taskOutput("> get snapshots data", res);
const allSnapshotsBefore = await Snapshot_model_default.find().lean().exec();
taskOutput("> get files data", res);
const allFilesBefore = (await import_fs2.promises.readdir(config.defaultImagesPath, { withFileTypes: true })).filter((item) => !item.isDirectory()).map((x) => x.name).filter((x) => x.includes(".png"));
taskOutput("> get old checks data", res);
const oldChecks = await Check_model_default.find({ createdDate: { $lt: trashHoldDate } }).lean().exec();
taskOutput(">>> collect all baselineIds for old Checks ", res);
const oldSnapshotsBaselineIdIds = oldChecks.map((x) => x.baselineId).filter((x) => x);
taskOutput(">>> collect all actualSnapshotId for old Checks ", res);
const oldSnapshotsActualSnapshotIdIds = oldChecks.map((x) => x.actualSnapshotId).filter((x) => x);
taskOutput(">>> collect all diffId for old Checks ", res);
const oldSnapshotsDiffIds = oldChecks.map((x) => x.diffId).filter((x) => x);
taskOutput(">>> calculate all unique snapshots ids for old Checks ", res);
const allOldSnapshotsUniqueIds = Array.from(/* @__PURE__ */ new Set([...oldSnapshotsBaselineIdIds, ...oldSnapshotsActualSnapshotIdIds, ...oldSnapshotsDiffIds])).map((x) => x.valueOf());
taskOutput(">>> collect all old snapshots", res);
const oldSnapshots = await Snapshot_model_default.find({ _id: { $in: allOldSnapshotsUniqueIds } }).lean();
const outTable = stringTable.create([
{ item: "all checks", count: allChecksBefore.length },
{ item: "all snapshots", count: allSnapshotsBefore.length },
{ item: "all files", count: allFilesBefore.length },
{ item: `checks older than: '${options.days}' days`, count: oldChecks.length },
{ item: "old snapshots baseline ids", count: oldSnapshotsBaselineIdIds.length },
{ item: "old snapshots actual snapshotId", count: oldSnapshotsActualSnapshotIdIds.length },
{ item: "old snapshots diffIds", count: oldSnapshotsDiffIds.length },
{ item: "all old snapshots unique Ids", count: allOldSnapshotsUniqueIds.length },
{ item: "all old snapshots", count: oldSnapshots.length }
]);
taskOutput(outTable, res);
if (options.remove === "true") {
taskOutput(`STAGE #2 Remove checks that older that: '${options.days}' days, '${dateToISO8601_default(trashHoldDate)}'
`, res);
taskOutput("> remove checks", res);
const checkRemovingResult = await Check_model_default.deleteMany({ createdDate: { $lt: trashHoldDate } });
taskOutput(`>>> removed: '${checkRemovingResult.deletedCount}'`, res);
taskOutput("> remove snapshots", res);
taskOutput(">> collect data to removing", res);
taskOutput(">>> get all baselines snapshots id`s", res);
const baselinesSnapshotsIds = await Baseline_model_default.find({}).distinct("snapshootId");
taskOutput(">>> get all checks snapshots baselineId", res);
const checksSnapshotsBaselineId = await Check_model_default.find({}).distinct("baselineId");
taskOutput(">>> get all checks snapshots actualSnapshotId", res);
const checksSnapshotsActualSnapshotId = await Check_model_default.find({}).distinct("actualSnapshotId");
taskOutput(">> remove baselines snapshots", res);
taskOutput(">> remove all old snapshots that not related to new baseline and check items", res);
const removedByBaselineSnapshotsResult = await Snapshot_model_default.deleteMany({
$and: [
{ _id: { $nin: checksSnapshotsBaselineId } },
{ _id: { $nin: checksSnapshotsActualSnapshotId } },
{ _id: { $nin: baselinesSnapshotsIds } },
{ _id: { $in: oldSnapshotsBaselineIdIds } }
]
});
taskOutput(`>>> removed: '${removedByBaselineSnapshotsResult.deletedCount}'`, res);
taskOutput(">> remove actual snapshots", res);
taskOutput(">> remove all old snapshots that not related to new baseline and check items", res);
const removedByActualSnapshotsResult = await Snapshot_model_default.deleteMany({
$and: [
{ _id: { $nin: checksSnapshotsBaselineId } },
{ _id: { $nin: checksSnapshotsActualSnapshotId } },
{ _id: { $nin: baselinesSnapshotsIds } },
{ _id: { $in: oldSnapshotsActualSnapshotIdIds } }
]
});
taskOutput(`>>> removed: '${removedByActualSnapshotsResult.deletedCount}'`, res);
taskOutput(">> remove all old diff snapshots", res);
const removedByDiffSnapshotsResult = await Snapshot_model_default.deleteMany({
$and: [
{ _id: { $in: oldSnapshotsDiffIds } }
]
});
taskOutput(`>>> removed: '${removedByDiffSnapshotsResult.deletedCount}'`, res);
taskOutput("> remove files", res);
taskOutput(">>> collect all old snapshots filenames", res);
const oldSnapshotsUniqueFilenames = Array.from(new Set(oldSnapshots.map((x) => x.filename)));
taskOutput(`>> found: ${oldSnapshotsUniqueFilenames.length}`, res);
taskOutput("> get all current snapshots filenames", res);
const allCurrentSnapshotsFilenames = await Snapshot_model_default.find().distinct("filename").exec();
taskOutput(">> calculate interception between all current snapshot filenames and old shapshots filenames", res);
const arrayIntersection = (arr1, arr2) => arr1.filter((x) => arr2.includes(x));
const filesInterception = arrayIntersection(allCurrentSnapshotsFilenames, oldSnapshotsUniqueFilenames);
taskOutput(`>> found: ${filesInterception.length}`, res);
taskOutput(">> calculate filenames to remove", res);
const arrayDiff = (arr1, arr2) => arr1.filter((x) => !arr2.includes(x));
const filesToDelete = arrayDiff(oldSnapshotsUniqueFilenames, filesInterception);
taskOutput(`>> found: ${filesToDelete.length}`, res);
taskOutput(`>> remove these files: ${filesToDelete.length}`, res);
await Promise.all(filesToDelete.map((filename) => import_fs2.promises.unlink(import_path3.default.join(config.defaultImagesPath, filename))));
taskOutput(`>> done: ${filesToDelete.length}`, res);
taskOutput("STAGE #3 Calculate common stats after Removing", res);
taskOutput("> get all checks data", res);
const allChecksAfter = await Check_model_default.find().lean().exec();
taskOutput("> get snapshots data", res);
const allSnapshotsAfter = await Snapshot_model_default.find().lean().exec();
taskOutput("> get files data", res);
const allFilesAfter = (await import_fs2.promises.readdir(config.defaultImagesPath, { withFileTypes: true })).filter((item) => !item.isDirectory()).map((x) => x.name).filter((x) => x.includes(".png"));
const outTableAfter = stringTable.create([
{ item: "all checks", count: allChecksAfter.length },
{ item: "all snapshots", count: allSnapshotsAfter.length },
{ item: "all files", count: allFilesAfter.length }
]);
taskOutput(outTableAfter, res);
}
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
taskOutput(`> done in ${elapsedSeconds} seconds ${elapsedSeconds / 60} min`, res);
} catch (e) {
const errMsg2 = e instanceof Error ? e.message : String(e);
logger_default.error(errMsg2);
taskOutput(errMsg2, res);
} finally {
res.end();
}
};
var task_test = async (options = "empty", req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Content-Encoding": "none"
});
const x = 1e3;
let isAborted = false;
req.on("close", () => {
isAborted = true;
});
for (let i = 0; i < x; i += 1) {
taskOutput(`- Task Output: '${i}', options: ${options}
`, res);
if (isAborted) {
taskOutput("the task was aborted\n", res);
logger_default.warn("the task was aborted");
res.flush();
return res.end();
}
}
return res.end();
};
// src/server/services/client.service.ts
var client_service_exports = {};
__export(client_service_exports, {
createCheck: () => createCheck,
endSession: () => endSession,
getBaselines: () => getBaselines,
getIdent: () => getIdent,
startSession: () => startSession
});
var import_fs3 = __toESM(require("fs"));
var import_hasha2 = __toESM(require("hasha"));
// src/server/lib/dbItems/updateItem.ts
var import_mongoose12 = __toESM(require("mongoose"));
async function updateItem(itemType, filter, params) {
const logOpts5 = {
scope: "updateItem",
msgType: "UPDATE",
itemType
};
logger_default.debug(`update item type: '${itemType}', filter: '${JSON.stringify(filter)}', params: '${JSON.stringify(params)}'`, logOpts5);
const itemModel = await import_mongoose12.default.model(itemType).findOne(filter);
const updatedItem = await itemModel?.updateOne(params);
logger_default.debug(`'${itemType}' was updated: '${JSON.stringify(updatedItem)}'`, { ...logOpts5, ...{ ref: String(itemModel?._id) } });
return updatedItem;
}
// src/server/lib/dbItems/updateItemDate.ts
var import_mongoose13 = __toESM(require("mongoose"));
var logOpts2 = {
scope: "dbitems",
msgType: "DB"
};
async function updateItemDate(mdClass, id2) {
logger_default.debug(`update date for the item: '${mdClass}' with id: '${id2}'`, logOpts2);
const itemModel = await import_mongoose13.default.model(mdClass).findById(id2);
const updatedItem = await itemModel?.updateOne({ updatedDate: Date.now() });
logger_default.debug(`'${mdClass}' date updated: '${JSON.stringify(itemModel)}'`, logOpts2);
return updatedItem;
}
// src/server/lib/dbItems/createItemIfNotExist.ts
var import_mongoose14 = __toESM(require("mongoose"));
async function createItemIfNotExist(modelName, params, logsMeta = {}) {
const logOpts5 = {
scope: "createItemIfNotExist",
msgType: "CREATE",
itemType: modelName
};
try {
const itemModel = import_mongoose14.default.model(modelName);
const options = {
upsert: true,
new: true,
setDefaultsOnInsert: true
};
await itemModel.init();
const item = await itemModel.findOneAndUpdate(params, params, options);
logger_default.info(`ORM item '${modelName}' was created: '${JSON.stringify(item)}'`, { ...logOpts5, ...{ ref: String(item?._id) }, ...logsMeta });
return item;
} catch (e) {
logger_default.debug(`cannot create '${modelName}' ORM item, error: '${e.stack || e}'`, { ...logOpts5, ...logsMeta });
}
return null;
}
// src/server/lib/dbItems/createItemProm.ts
var import_mongoose15 = __toESM(require("mongoose"));
var logOpts3 = {
scope: "dbitems",
msgType: "DB"
};
async function createItemProm(modelName, params) {
try {
const itemModel = import_mongoose15.default.model(modelName);
logger_default.debug(`start to create ORM item via promise: '${modelName}', params: '${JSON.stringify(params)}'`, logOpts3);
const item = await itemModel.create(params);
return item;
} catch (e) {
const errMsg2 = `cannot create '${modelName}', error: '${e.stack || e}'`;
logger_default.error(errMsg2, logOpts3);
throw new Error(errMsg2);
}
}
// src/server/lib/dbItems/createTest.ts
async function createTest(params) {
return createItemProm("VRSTest", params);
}
// src/server/lib/dbItems/createSuiteIfNotExist.ts
async function createSuiteIfNotExist(params, logsMeta = {}) {
const logOpts5 = {
scope: "createSuiteIfNotExist",
msgType: "CREATE",
itemType: "VRSSuite"
};
if (!params.name || !params.app) throw new Error(`Cannot create suite, wrong params: '${JSON.stringify(params)}'`);
logger_default.debug(`try to create suite if exist, params: '${JSON.stringify(params)}'`, { ...logOpts5, ...logsMeta });
let suite = await Suite_model_default.findOne({ name: params.name }).exec();
if (suite) {
logger_default.debug(`suite already exist: '${JSON.stringify(params)}'`, { ...logOpts5, ...logsMeta });
return suite;
}
suite = await Suite_model_default.create(params);
logger_default.debug(`suite with name: '${params.name}' was created`, { ...logOpts5, ...logsMeta });
return suite;
}
// src/server/lib/dbItems/createRunIfNotExist.ts
async function createRunIfNotExist(params, logsMeta = {}) {
const logOpts5 = {
scope: "createRunIfNotExist",
msgType: "CREATE",
itemType: "VRSRun"
};
let run;
try {
if (!params.name || !params.app || !params.ident) {
throw new Error(`Cannot create run, wrong params: '${JSON.stringify(params)}'`);
}
logger_default.debug(`try to create run if exist, params: '${JSON.stringify(params)}'`, { ...logOpts5, ...logsMeta });
run = await Run_model_default.findOne({ ident: params.ident }).exec();
if (run) {
logger_default.debug(`run already exist: '${JSON.stringify(params)}'`, { ...logOpts5, ...logsMeta });
return run;
}
run = await Run_model_default.create({
...params,
createdDate: params.createdDate || /* @__PURE__ */ new Date()
});
logger_default.debug(`run with name: '${params.name}' was created: ${run}`, { ...logOpts5, ...logsMeta });
return run;
} catch (e) {
if (e.code === 11e3) {
logger_default.warn(`run key duplication collision: '${JSON.stringify(params)}', error: '${errMsg(e)}'`, { ...logOpts5, ...logsMeta });
run = await Run_model_default.findOne({ name: params.name, ident: params.ident });
logger_default.warn(`run key duplication collision, found: '${JSON.stringify(run)}'`, { ...logOpts5, ...logsMeta });
if (run) return run;
}
logger_default.error(`cannot create run, params: '${JSON.stringify(params)}', error: '${errMsg(e)}', obj: ${JSON.stringify(e)}`, { ...logOpts5, ...logsMeta });
throw e;
}
}
// src/server/lib/сomparison/compareImagesNode.ts
var import_node_resemble = __toESM(require("@syngrisi/node-resemble.js"));
async function streamToBuffer(stream) {
return new Promise((resolve, reject) => {
const data = [];
stream.on("data", (chunk) => {
data.push(chunk);
});
stream.on("end", () => {
resolve(Buffer.concat(data));
});
stream.on("error", (err) => {
reject(err);
});
});
}
function compareImages(image1, image2, options) {
return new Promise((resolve, reject) => {
try {
const ignoreTransform = {
antialiasing: "ignoreAntialiasing",
colors: "ignoreColors",
nothing: "ignoreNothing"
};
const ignoreMethod = ignoreTransform[options.ignore] ? ignoreTransform[options.ignore] : "ignoreNothing";
const outputOpts = options.output;
import_node_resemble.default.outputSettings(outputOpts);
let ignoredRect;
if (options.ignoreRectangles) {
ignoredRect = options.ignoreRectangles.map((it) => {
delete it.name;
return [it.left, it.top, it.right - it.left, it.bottom - it.top];
});
}
(0, import_node_resemble.default)(image1).compareTo(image2)[ignoreMethod]().ignoreRectangles(ignoredRect).onComplete(async (data) => {
console.log(data);
const stream = await data.getDiffImage();
const buffer = await streamToBuffer(stream.pack());
data.getBuffer = function() {
return buffer;
};
resolve(data);
});
} catch (e) {
reject(e);
}
});
}
// src/server/lib/сomparison/comparator.ts
var DEFAULT_OPTIONS = {
output: {
largeImageThreshold: 0,
outputDiff: true,
errorType: "flat",
transparency: 0
},
ignore: "nothing"
};
async function makeDiff(imgData1, imgData2, options = {}) {
const opts = Object.assign(DEFAULT_OPTIONS, options);
opts.ignoreRectangles = options.ignoredBoxes;
const compareData = await compareImages(imgData1, imgData2, opts);
return compareData;
}
async function getDiff(baselineOrigin, actualOrigin, opts = {}) {
const logOpts5 = {
scope: "getDiff",
itemType: "image",
msgType: "GET_DIFF"
};
try {
const executionTimer = process.hrtime();
logger_default.debug(`SAMPLE #1: ${process.hrtime(executionTimer).toString()}`, logOpts5);
const directDiff = await makeDiff(baselineOrigin, actualOrigin, opts);
logger_default.debug(`SAMPLE #2: ${process.hrtime(executionTimer).toString()}`, logOpts5);
directDiff.executionTotalTime = process.hrtime(executionTimer).toString();
logger_default.debug(`SAMPLE #3: ${process.hrtime(executionTimer).toString()}`, logOpts5);
logger_default.debug(`the diff is: ${JSON.stringify(directDiff, null, 4)}`, logOpts5);
return directDiff;
} catch (e) {
logger_default.error(errMsg(e), logOpts5);
throw new Error(errMsg(e));
}
}
// src/server/services/client.service.ts
var import_http_status6 = __toESM(require("http-status"));
var import_http_status7 = __toESM(require("http-status"));
var import_path4 = __toESM(require("path"));
async function updateTest(id2, update4) {
const logOpts5 = {
scope: "updateTest",
itemType: "test",
msgType: "UPDATE",
ref: id2
};
logger_default.debug(`update test id '${id2}' with params '${JSON.stringify(update4)}'`, logOpts5);
const updatedDate = update4.updatedDate || Date.now();
const test = await Test_model_default.findByIdAndUpdate(
id2,
{ ...update4, updatedDate }
).exec();
await test?.save();
return test;
}
var startSession = async (params, username) => {
const logOpts5 = {
scope: "createTest",
user: username,
itemType: "test",
msgType: "CREATE"
};
logger_default.info(`create test with name '${params.name}', params: '${JSON.stringify(params)}'`, logOpts5);
const opts = removeEmptyProperties({
name: params.name,
status: "Running",
app: params.app,
tags: params.tags && JSON.parse(params.tags),
branch: params.branch,
viewport: params.viewport,
browserName: params.browser,
browserVersion: params.browserVersion,
browserFullVersion: params.browserFullVersion,
os: params.os,
startDate: /* @__PURE__ */ new Date(),
updatedDate: /* @__PURE__ */ new Date()
});
try {
const app = await createItemIfNotExist(
"VRSApp",
{ name: params.app },
{ user: username, itemType: "app" }
);
opts.app = app._id;
const run = await createRunIfNotExist(
{ name: params.run, ident: params.runident, app: app._id },
{ user: username, itemType: "run" }
);
opts.run = run._id;
const suite = await createSuiteIfNotExist(
{ name: params.suite || "Others", app: app._id, createdDate: /* @__PURE__ */ new Date() },
{ user: username, itemType: "suite" }
);
opts.suite = suite._id;
const test = await createTest(opts);
return test;
} catch (e) {
logger_default.error(`cannot start session '${params.name}', params: '${JSON.stringify(params)}', error: ${errMsg(e)}`, logOpts5);
throw e;
}
};
var endSession = async (testId, username) => {
const logOpts5 = {
scope: "stopSession",
msgType: "END_SESSION",
user: username,
itemType: "test",
ref: testId
};
await waitUntil(async () => (await Check_model_default.find({ test: testId }).exec()).filter((ch) => ch.status.toString() !== "pending").length > 0);
const sessionChecks = await Check_model_default.find({ test: testId }).lean().exec();
const checksStatuses = sessionChecks.map((x) => x.status[0]);
let status3 = "not set";
if (checksStatuses.some((st2) => st2 === "failed")) {
status3 = "Failed";
}
if (checksStatuses.some((st2) => st2 === "passed") && !checksStatuses.some((st2) => st2 === "failed")) {
status3 = "Passed";
}
if (checksStatuses.some((st2) => st2 === "new") && !checksStatuses.some((st2) => st2 === "failed")) {
status3 = "Passed";
}
if (checksStatuses.some((st2) => st2 === "blinking") && !checksStatuses.some((st2) => st2 === "failed")) {
status3 = "Passed";
}
if (checksStatuses.every((st2) => st2 === "new")) {
status3 = "New";
}
const blinking = checksStatuses.filter((g) => g === "blinking").length;
const testParams = {
status: status3,
blinking
// calculatedViewport,
};
logger_default.info(`the session is over, the test will be updated with parameters: '${JSON.stringify(testParams)}'`, logOpts5);
const updatedTest = await updateTest(testId, testParams);
const result = updatedTest?.toObject();
return result;
};
async function getAcceptedBaseline(params) {
const identFieldsAccepted = Object.assign(buildIdentObject(params), { markedAs: "accepted" });
const acceptedBaseline = await Baseline_model_default.findOne(identFieldsAccepted, {}, { sort: { createdDate: -1 } });
logger_default.debug(`acceptedBaseline: '${acceptedBaseline ? JSON.stringify(acceptedBaseline) : "not found"}'`, { itemType: "baseline" });
if (acceptedBaseline) return acceptedBaseline;
return null;
}
async function getLastSuccessCheck(identifier) {
const condition = [{
...identifier,
status: "new"
}, {
...identifier,
status: "passed"
}];
return (await Check_model_default.find({ $or: condition }).sort({ updatedDate: -1 }).limit(1))[0];
}
async function getNotPendingChecksByIdent(identifier) {
return Check_model_default.find({
...identifier,
status: { $ne: "pending" }
}).sort({ updatedDate: -1 }).exec();
}
async function getSnapshotByImgHash(hash) {
return Snapshot_model_default.findOne({ imghash: hash });
}
async function createSnapshot(parameters) {
const logOpts5 = {
scope: "createSnapshot",
itemType: "snapshot",
msgType: "CREATE"
};
const { name, fileData, hashCode } = parameters;
const opts = { name };
if (fileData === null) throw new ApiError_default(import_http_status6.default.BAD_REQUEST, `cannot create the snapshot, the 'fileData' is not set, name: '${name}'`);
opts.imghash = hashCode || (0, import_hasha2.default)(fileData);
const snapshot = new Snapshot_model_default(opts);
const filename = `${snapshot.id}.png`;
const imagePath = import_path4.default.join(config.defaultImagesPath, filename);
logger_default.debug(`save screenshot for: '${name}' snapshot to: '${imagePath}'`, logOpts5);
await import_fs3.promises.writeFile(imagePath, fileData);
snapshot.filename = filename;
await snapshot.save();
logger_default.debug(`snapshot was saved: '${JSON.stringify(snapshot)}'`, { ...logOpts5, ...{ ref: snapshot._id } });
return snapshot;
}
async function cloneSnapshot(sourceSnapshot, name) {
const { filename } = sourceSnapshot;
const hashCode = sourceSnapshot.imghash;
const newSnapshot = new Snapshot_model_default({ name, filename, imghash: hashCode });
await newSnapshot.save();
return newSnapshot;
}
async function compareSnapshots(baselineSnapshot, actual, opts = {}) {
const logOpts5 = {
scope: "compareSnapshots",
ref: baselineSnapshot.id,
itemType: "snapshot",
msgType: "COMPARE"
};
try {
logger_default.debug(`compare baseline and actual snapshots with ids: [${baselineSnapshot.id}, ${actual.id}]`, logOpts5);
logger_default.debug(`current baseline snapshot: ${JSON.stringify(baselineSnapshot)}`, logOpts5);
let diff;
if (baselineSnapshot.imghash === actual.imghash) {
logger_default.debug(`baseline and actual snapshot have the identical image hashes: '${baselineSnapshot.imghash}'`, logOpts5);
diff = {
isSameDimensions: true,
dimensionDifference: { width: 0, height: 0 },
rawMisMatchPercentage: 0,
misMatchPercentage: "0.00",
analysisTime: 0,
executionTotalTime: "0",
getBuffer: null
};
} else {
const baselinePath = import_path4.default.join(config.defaultImagesPath, baselineSnapshot.filename);
const actualPath = import_path4.default.join(config.defaultImagesPath, actual.filename);
const baselineData = await import_fs3.promises.readFile(baselinePath);
const actualData = await import_fs3.promises.readFile(actualPath);
logger_default.debug(`baseline path: ${baselinePath}`, logOpts5);
logger_default.debug(`actual path: ${actualPath}`, logOpts5);
const options = opts;
const baseline = await Baseline_model_default.findOne({ snapshootId: baselineSnapshot._id }).exec();
if (baseline) {
if (baseline.ignoreRegions) {
logger_default.debug(`ignore regions: '${baseline.ignoreRegions}', type: '${typeof baseline.ignoreRegions}'`);
options.ignoredBoxes = JSON.parse(baseline.ignoreRegions);
}
options.ignore = baseline.matchType || "nothing";
}
diff = await getDiff(baselineData, actualData, options);
}
logger_default.silly(`the diff is: '${JSON.stringify(diff, null, 2)}'`);
if (diff.rawMisMatchPercentage.toString() !== "0") {
logger_default.debug(`images are different, ids: [${baselineSnapshot.id}, ${actual.id}], rawMisMatchPercentage: '${diff.rawMisMatchPercentage}'`);
}
if (diff.stabMethod && diff.vOffset) {
if (diff.stabMethod === "downup") {
actual.vOffset = -diff.vOffset;
await actual.save();
}
if (diff.stabMethod === "updown") {
baselineSnapshot.vOffset = -diff.vOffset;
await baselineSnapshot.save();
}
}
return diff;
} catch (e) {
const errMsg2 = `cannot compare snapshots: ${e}
${e instanceof Error ? e.stack : e}`;
logger_default.error(errMsg2, logOpts5);
throw new Error(String(e));
}
}
var isBaselineValid = (baseline) => {
const keys = [
"name",
"app",
"branch",
"browserName",
"viewport",
"os",
"createdDate",
"lastMarkedDate",
"markedAs",
"markedById",
"markedByUsername",
"snapshootId"
];
for (const key of keys) {
if (!baseline[key]) {
logger_default.error(`invalid baseline, the '${key}' property is empty`);
return false;
}
}
return true;
};
var updateCheckParamsFromBaseline = (params, baseline) => {
const updatedParams = { ...params };
updatedParams.baselineId = baseline.snapshootId.toString();
updatedParams.markedAs = baseline.markedAs;
updatedParams.markedDate = baseline.lastMarkedDate?.toString();
updatedParams.markedByUsername = baseline.markedByUsername;
return updatedParams;
};
var prepareActualSnapshot = async (checkParam, snapshotFoundedByHashcode, logOpts5) => {
let currentSnapshot;
const fileData = checkParam.files ? checkParam.files.file.data : null;
if (snapshotFoundedByHashcode) {
const fullFilename = import_path4.default.join(config.defaultImagesPath, snapshotFoundedByHashcode.filename);
if (!import_fs3.default.existsSync(fullFilename)) {
throw new Error(`Couldn't find the baseline file: '${fullFilename}'`);
}
logger_default.debug(`snapshot with such hashcode: '${checkParam.hashCode}' is already exists, will clone it`, logOpts5);
if (!checkParam.name) throw new ApiError_default(import_http_status6.default.BAD_REQUEST, `Cannot prepareActualSnapshot name is empty, hashe: ${checkParam.hashCode}`);
currentSnapshot = await cloneSnapshot(snapshotFoundedByHashcode, checkParam.name);
} else {
logger_default.debug(`snapshot with such hashcode: '${checkParam.hashCode}' does not exists, will create it`, logOpts5);
currentSnapshot = await createSnapshot({ name: checkParam.name, fileData, hashCode: checkParam.hashCode });
}
return currentSnapshot;
};
async function isNeedFiles(checkParam, logOpts5) {
const snapshotFoundedByHashcode = await getSnapshotByImgHash(checkParam.hashCode);
if (!checkParam.hashCode && !checkParam.files) {
logger_default.debug("hashCode or files parameters should be present", logOpts5);
return { needFilesStatus: true, snapshotFoundedByHashcode };
}
if (!checkParam.files && !snapshotFoundedByHashcode) {
logger_default.debug(`cannot find the snapshot with hash: '${checkParam.hashCode}'`, logOpts5);
return { needFilesStatus: true, snapshotFoundedByHashcode };
}
return { needFilesStatus: false, snapshotFoundedByHashcode };
}
async function inspectBaseline(newCheckParams, storedBaseline, checkIdent, currentSnapshot, logOpts5) {
let currentBaselineSnapshot = null;
const params = {};
params.failReasons = [];
if (storedBaseline !== null) {
logger_default.debug(`a baseline for check name: '${newCheckParams.name}', id: '${storedBaseline.snapshootId}' is already exists`, logOpts5);
if (!isBaselineValid(storedBaseline)) {
newCheckParams.failReasons.push("invalid_baseline");
}
Object.assign(params, updateCheckParamsFromBaseline(newCheckParams, storedBaseline));
currentBaselineSnapshot = await Snapshot_model_default.findById(storedBaseline.snapshootId);
if (!currentBaselineSnapshot) throw new ApiError_default(import_http_status6.default.INTERNAL_SERVER_ERROR, `Cannot find the snapshot with id: ${storedBaseline.snapshootId}`);
} else {
const checksWithSameIdent = await getNotPendingChecksByIdent(checkIdent);
if (checksWithSameIdent.length > 0) {
logger_default.error(`checks with ident'${JSON.stringify(checkIdent)}' exist, but baseline is absent`, logOpts5);
params.failReasons.push("not_accepted");
params.baselineId = currentSnapshot.id.toString();
currentBaselineSnapshot = currentSnapshot;
} else {
params.baselineId = currentSnapshot.id;
params.status = "new";
currentBaselineSnapshot = currentSnapshot;
logger_default.debug(`create the new check with params: '${prettyCheckParams_default(params)}'`, logOpts5);
}
}
return { inspectBaselineParams: params, currentBaselineSnapshot };
}
var ignoreDifferentResolutions = ({ height, width }) => {
if (width === 0 && height === -1) return true;
if (width === 0 && height === 1) return true;
return false;
};
var compare = async (expectedSnapshot, actualSnapshot, newCheckParams, skipSaveOnCompareError, currentUser) => {
const logOpts5 = {
scope: "createCheck.compare",
user: currentUser.username,
itemType: "check",
msgType: "COMPARE"
};
const executionTimer = process.hrtime();
const compareResult = {};
compareResult.failReasons = [...newCheckParams.failReasons];
let checkCompareResult;
let diffSnapshot = null;
const areSnapshotsDifferent = (result) => result.rawMisMatchPercentage.toString() !== "0";
const areSnapshotsWrongDimensions = (result) => !result.isSameDimensions && !ignoreDifferentResolutions(result.dimensionDifference);
if (newCheckParams.status !== "new" && !compareResult.failReasons.includes("not_accepted")) {
try {
logger_default.debug(`'the check with name: '${newCheckParams.name}' isn't new, make comparing'`, logOpts5);
checkCompareResult = await compareSnapshots(expectedSnapshot, actualSnapshot, { vShifting: newCheckParams.vShifting });
logger_default.silly(`ignoreDifferentResolutions: '${ignoreDifferentResolutions(checkCompareResult.dimensionDifference)}'`);
logger_default.silly(`dimensionDifference: '${JSON.stringify(checkCompareResult.dimensionDifference)}`);
if (areSnapshotsDifferent(checkCompareResult) || areSnapshotsWrongDimensions(checkCompareResult)) {
let logMsg;
if (areSnapshotsWrongDimensions(checkCompareResult)) {
logMsg = "snapshots have different dimensions";
compareResult.failReasons.push("wrong_dimensions");
}
if (areSnapshotsDifferent(checkCompareResult)) {
logMsg = "snapshots have differences";
compareResult.failReasons.push("different_images");
}
if (logMsg) logger_default.debug(logMsg, logOpts5);
logger_default.debug(`saving diff snapshot for check with name: '${newCheckParams.name}'`, logOpts5);
if (!skipSaveOnCompareError) {
diffSnapshot = await createSnapshot({
name: newCheckParams.name,
fileData: checkCompareResult.getBuffer()
});
compareResult.diffId = diffSnapshot.id;
compareResult.diffSnapshot = diffSnapshot;
}
compareResult.status = "failed";
} else {
compareResult.status = "passed";
}
checkCompareResult.totalCheckHandleTime = process.hrtime(executionTimer).toString();
compareResult.result = JSON.stringify(checkCompareResult, null, " ");
} catch (e) {
compareResult.status = "failed";
compareResult.result = JSON.stringify({ server_error: `error during comparing - ${errMsg(e)}` });
compareResult.failReasons.push("internal_server_error");
throw new ApiError_default(import_http_status6.default.INTERNAL_SERVER_ERROR, `error during comparing: ${errMsg(e)}`);
}
}
if (compareResult.failReasons.length > 0) {
compareResult.status = "failed";
}
return compareResult;
};
var createCheckParams = (checkParam, suite, app, test, currentUser) => ({
test: test.id,
name: checkParam.name,
status: "pending",
viewport: checkParam.viewport,
browserName: checkParam.browserName,
browserVersion: checkParam.browserVersion,
browserFullVersion: checkParam.browserFullVersion,
os: checkParam.os,
updatedDate: Date.now(),
suite: suite.id,
app: app.id,
branch: checkParam.branch,
domDump: checkParam.domDump,
run: test.run.toString(),
creatorId: currentUser._id.toString(),
creatorUsername: currentUser.username,
hashCode: checkParam.hashCode,
failReasons: []
});
var createCheck = async (checkParam, test, suite, app, currentUser, skipSaveOnCompareError = false) => {
const logOpts5 = {
scope: "createCheck",
user: currentUser.username,
itemType: "check",
msgType: "CREATE"
};
let actualSnapshot;
let currentBaselineSnapshot;
const newCheckParams = createCheckParams(checkParam, suite, app, test, currentUser);
const checkIdent = buildIdentObject(newCheckParams);
let check = null;
const totalCheckHandleTime = 0;
const addCheck = (test2, check2) => {
if (test2.checks) {
test2.checks.push(check2.id);
} else {
test2.checks = [check2.id];
}
};
try {
const { needFilesStatus, snapshotFoundedByHashcode } = await isNeedFiles(checkParam, logOpts5);
if (needFilesStatus) return { status: "needFiles" };
actualSnapshot = await prepareActualSnapshot(checkParam, snapshotFoundedByHashcode, logOpts5);
newCheckParams.actualSnapshotId = actualSnapshot.id;
logger_default.info(`find a baseline for the check with identifier: '${JSON.stringify(checkIdent)}'`, logOpts5);
const storedBaseline = await getAcceptedBaseline(checkIdent);
const inspectBaselineResult = await inspectBaseline(newCheckParams, storedBaseline, checkIdent, actualSnapshot, logOpts5);
Object.assign(newCheckParams, inspectBaselineResult.inspectBaselineParams);
currentBaselineSnapshot = inspectBaselineResult.currentBaselineSnapshot;
const compareResult = await compare(currentBaselineSnapshot, actualSnapshot, newCheckParams, skipSaveOnCompareError, currentUser);
Object.assign(newCheckParams, compareResult);
logger_default.debug(`create the new check document with params: '${prettyCheckParams_default(newCheckParams)}'`, logOpts5);
check = await Check_model_default.create(newCheckParams);
const savedCheck = await check.save();
logger_default.debug(`the check with id: '${check.id}', was created, will updated with data during creating process`, logOpts5);
logOpts5.ref = String(check.id);
logger_default.debug(`update test with check id: '${check.id}'`, logOpts5);
addCheck(test, check);
test.markedAs = await calculateAcceptedStatus(check.test);
test.updatedDate = /* @__PURE__ */ new Date();
await test.save();
logger_default.debug("update suite and run", logOpts5);
await updateItemDate("VRSSuite", check.suite);
await updateItemDate("VRSRun", check.run);
const lastSuccessCheck = await getLastSuccessCheck(checkIdent);
const checkObject = savedCheck.toObject();
const result = {
...checkObject,
currentSnapshot: actualSnapshot,
expectedSnapshot: currentBaselineSnapshot,
diffSnapshot: compareResult.diffSnapshot,
executeTime: totalCheckHandleTime,
lastSuccess: lastSuccessCheck ? lastSuccessCheck.id : null
};
return result;
} catch (e) {
newCheckParams.status = "failed";
newCheckParams.result = `{ "server error": "${errMsg(e)}" }`;
newCheckParams.failReasons.push("internal_server_error");
if (!check) {
logger_default.debug(`create the new check document with params: '${prettyCheckParams_default(newCheckParams)}'`, logOpts5);
check = await Check_model_default.create(newCheckParams);
await check.save();
} else {
check.set(newCheckParams);
await check.save();
}
logger_default.debug(`the check with id: '${check.id}', was created, will updated with data during creating process`, logOpts5);
logOpts5.ref = check.id;
logger_default.debug(`update test with check id: '${check.id}'`, logOpts5);
addCheck(test, check);
await test.save();
throw new ApiError_default(import_http_status7.default.INTERNAL_SERVER_ERROR, errMsg(e));
}
};
var getIdent = () => ident;
var getBaselines = async (filter, options) => {
const logOpts5 = {
scope: "getBaselines",
itemType: "baseline",
msgType: "GET"
};
const app = await App_model_default.findOne({ name: filter.app });
if (!app) {
logger_default.error(`Cannot find the app: '${filter.app}'`, logOpts5);
return {};
}
filter.app = app._id;
logger_default.debug(`Get baselines with filter: '${JSON.stringify(filter)}', options: '${JSON.stringify(options)}'`, logOpts5);
return Baseline_model_default.paginate(filter, options);
};
// src/server/services/user.service.ts
var user_service_exports = {};
__export(user_service_exports, {
createUser: () => createUser2,
deleteUserById: () => deleteUserById,
getUserByEmail: () => getUserByEmail,
getUserById: () => getUserById,
queryUsers: () => queryUsers,
updateUserById: () => updateUserById
});
var import_http_status8 = __toESM(require("http-status"));
var createUser2 = async (userBody) => {
if (await User_model_default.isEmailTaken(userBody.username)) {
throw new ApiError_default(import_http_status8.default.BAD_REQUEST, "Email already taken");
}
const logOpts5 = {
msgType: "CREATE",
itemType: "user",
ref: userBody.username,
scope: "createUser"
};
logger_default.debug(`create the user with name '${userBody.username}', params: '${JSON.stringify(userBody)}'`, logOpts5);
const user = await User_model_default.create({ ...userBody, createdDate: Date.now() });
const updatedUser = await user.setPassword(userBody.password);
await updatedUser.save();
logger_default.debug(`password for user: '${userBody.username}' set successfully`, logOpts5);
const userWithSelectedFields = await User_model_default.findById(updatedUser._id).select("username firstName lastName role createdDate updatedDate createdDate").exec();
return userWithSelectedFields;
};
var queryUsers = async (filter, options) => {
const users = await User_model_default.paginate(filter, options);
return users;
};
var getUserById = async (id2) => User_model_default.findById(id2).select("username firstName lastName role createdDate updatedDate createdDate").exec();
var getUserByEmail = async (email) => User_model_default.findOne({ email });
var updateUserById = async (userId, updateBody) => {
const logOpts5 = {
msgType: "UPDATE",
itemType: "user",
scope: "updateUserById",
ref: userId
};
logger_default.info(`update user with id: '${userId}' name '${updateBody.username}', params: '${JSON.stringify(updateBody)}'`, logOpts5);
const user = await getUserById(userId);
if (!user) {
throw new ApiError_default(import_http_status8.default.NOT_FOUND, "User not found");
}
if (updateBody.email && await User_model_default.isEmailTaken(updateBody.email, userId)) {
throw new ApiError_default(import_http_status8.default.BAD_REQUEST, "Email already taken");
}
if (updateBody.password) {
logger_default.debug(`update password for '${updateBody.username}'`, logOpts5);
await user.setPassword(updateBody.password);
await user.save();
logger_default.debug(`password for '${updateBody.username}' was updated`, logOpts5);
}
logger_default.debug(`user '${updateBody.username}' was updated successfully`, logOpts5);
const { password, ...newupdateBody } = updateBody;
Object.assign(user, {
...newupdateBody,
updatedDate: Date.now()
});
await user.save();
return user;
};
var deleteUserById = async (userId) => User_model_default.findByIdAndDelete(userId).exec();
// src/server/services/check.service.ts
var check_service_exports = {};
__export(check_service_exports, {
accept: () => accept2,
remove: () => remove5,
update: () => update
});
// src/server/services/snapshot.service.ts
var import_fs4 = __toESM(require("fs"));
var import_path5 = __toESM(require("path"));
var logOpts4 = {
scope: "snapshot_helper",
msgType: "API"
};
var removeSnapshotFile = async (snapshot) => {
let relatedSnapshots;
if (snapshot.filename) {
relatedSnapshots = await Snapshot_model_default.find({ filename: snapshot.filename });
logger_default.debug(`there are '${relatedSnapshots.length}' snapshots with filename: '${snapshot.filename}'`, logOpts4);
}
const isLastSnapshotFile = () => {
if (!snapshot.filename) {
return true;
}
return relatedSnapshots.length === 0;
};
logger_default.debug({ isLastSnapshotFile: isLastSnapshotFile() });
if (isLastSnapshotFile()) {
const imagePath = import_path5.default.join(config.defaultImagesPath, snapshot.filename);
logger_default.silly(`path: ${imagePath}`, logOpts4);
if (import_fs4.default.existsSync(imagePath)) {
logger_default.debug(`removing file: '${imagePath}'`, logOpts4, {
msgType: "REMOVE",
itemType: "file"
});
import_fs4.default.unlinkSync(imagePath);
}
}
};
var remove4 = async (id2) => {
const logOpts5 = {
scope: "removeSnapshot",
msgType: "REMOVE",
itemType: "snapshot",
ref: id2
};
logger_default.silly(`deleting snapshot with id: '${id2}'`, logOpts5);
if (!id2) {
logger_default.warn("id is empty");
return;
}
const snapshot = await Snapshot_model_default.findById(id2).lean().exec();
if (!snapshot) {
logger_default.warn(`cannot find snapshot with id: '${id2}'`);
return;
}
const baseline = await Baseline_model_default.findOne({ snapshootId: id2 });
if (baseline) {
logger_default.debug(`snapshot: '${id2}' is related to a baseline, skipping deletion`, logOpts5);
return;
}
logger_default.debug(`snapshot: '${id2}' is not related to a baseline, attempting to remove it`, logOpts5);
await Snapshot_model_default.findByIdAndDelete(id2);
logger_default.debug(`snapshot: '${id2}' was removed`, logOpts5);
const imagePath = import_path5.default.join(config.defaultImagesPath, snapshot.filename);
logger_default.debug(`attempting to remove snapshot file, id: '${snapshot._id}', filename: '${imagePath}'`, logOpts5);
await removeSnapshotFile(snapshot);
};
// src/server/services/check.service.ts
async function calculateTestStatus(testId) {
const checksInTest = await Check_model_default.find({ test: testId });
const statuses = checksInTest.map((x) => x.status[0]);
let testCalculatedStatus = "Failed";
if (statuses.every((x) => x === "new" || x === "passed")) {
testCalculatedStatus = "Passed";
}
if (statuses.every((x) => x === "new")) {
testCalculatedStatus = "New";
}
return testCalculatedStatus;
}
var validateBaselineParam = (params) => {
const mandatoryParams = ["markedAs", "markedById", "markedByUsername", "markedDate"];
for (const param of mandatoryParams) {
if (!params[param]) {
const errMsg2 = `invalid baseline parameters, '${param}' is empty, params: ${JSON.stringify(params)}`;
logger_default.error(errMsg2);
throw new Error(errMsg2);
}
}
};
async function createNewBaseline(params) {
const logOpts5 = {
scope: "createNewBaseline",
msgType: "CREATE"
};
validateBaselineParam(params);
const identFields = buildIdentObject(params);
const lastBaseline = await Baseline_model_default.findOne(identFields).exec();
const sameBaseline = await Baseline_model_default.findOne({ ...identFields, snapshootId: params.actualSnapshotId }).exec();
const baselineParams = lastBaseline?.ignoreRegions ? { ...identFields, ignoreRegions: lastBaseline.ignoreRegions } : identFields;
if (sameBaseline) {
logger_default.debug(`the baseline with same ident and snapshot id: ${params.actualSnapshotId} already exist`, logOpts5);
} else {
logger_default.debug(`the baseline with same ident and snapshot id: ${params.actualSnapshotId} does not exist,
create new one, baselineParams: ${JSON.stringify(baselineParams)}`, logOpts5);
}
logger_default.silly({ sameBaseline });
const resultedBaseline = sameBaseline || await Baseline_model_default.create(baselineParams);
resultedBaseline.markedAs = params.markedAs;
resultedBaseline.markedById = params.markedById;
resultedBaseline.markedByUsername = params.markedByUsername;
resultedBaseline.lastMarkedDate = params.markedDate;
resultedBaseline.createdDate = /* @__PURE__ */ new Date();
resultedBaseline.snapshootId = params.actualSnapshotId;
return resultedBaseline.save();
}
var accept2 = async (id2, baselineId, user) => {
const logOpts5 = {
msgType: "ACCEPT",
itemType: "check",
ref: id2,
user: user?.username,
scope: "accept"
};
logger_default.debug(`accept check: ${id2}`, logOpts5);
const check = await Check_model_default.findById(id2).exec();
if (!check) throw new Error(`cannot find check with id: ${id2}`);
const test = await Test_model_default.findById(check.test).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
check.markedById = user._id;
check.markedByUsername = user.username;
check.markedDate = /* @__PURE__ */ new Date();
check.markedAs = "accepted";
check.status = check.status[0] === "new" ? ["new"] : ["passed"];
check.updatedDate = /* @__PURE__ */ new Date();
logger_default.debug(`update check with options: '${JSON.stringify(check.toObject())}'`, logOpts5);
await createNewBaseline(check.toObject());
await check.save();
const testCalculatedStatus = await calculateTestStatus(String(check.test));
const testCalculatedAcceptedStatus = await calculateAcceptedStatus(check.test);
test.status = testCalculatedStatus;
test.markedAs = testCalculatedAcceptedStatus;
test.updatedDate = /* @__PURE__ */ new Date();
await Suite_model_default.findByIdAndUpdate(check.suite, { updatedDate: Date.now() });
logger_default.debug(`update test with status: '${testCalculatedStatus}', marked: '${testCalculatedAcceptedStatus}'`, logOpts5, {
msgType: "UPDATE",
itemType: "test",
ref: test._id
});
await test.save();
await check.save();
logger_default.debug(`check with id: '${id2}' was updated`, logOpts5);
return check;
};
async function removeCheck(id2, user) {
const logMeta = {
scope: "removeCheck",
itemType: "check",
ref: id2,
msgType: "REMOVE",
user: user?.username
};
try {
const check = await Check_model_default.findByIdAndDelete(id2).exec();
if (!check) throw new Error(`cannot find check with id: ${id2}`);
logger_default.debug(`check with id: '${id2}' was removed, update test: ${check.test}`, logMeta);
const test = await Test_model_default.findById(check.test).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
const testCalculatedStatus = await calculateTestStatus(String(check.test));
const testCalculatedAcceptedStatus = await calculateAcceptedStatus(check.test);
test.status = testCalculatedStatus;
test.markedAs = testCalculatedAcceptedStatus;
test.updatedDate = /* @__PURE__ */ new Date();
await updateItemDate("VRSSuite", check.suite);
await test.save();
if (check.baselineId && String(check.baselineId) !== "undefined") {
logger_default.debug(`try to remove the snapshot, baseline: ${check.baselineId}`, logMeta);
await remove4(check.baselineId.toString());
}
if (check.actualSnapshotId && String(check.baselineId) !== "undefined") {
logger_default.debug(`try to remove the snapshot, actual: ${check.actualSnapshotId}`, logMeta);
await remove4(check.actualSnapshotId.toString());
}
if (check.diffId && String(check.baselineId) !== "undefined") {
logger_default.debug(`try to remove snapshot, diff: ${check.diffId}`, logMeta);
await remove4(check.diffId.toString());
}
return check;
} catch (e) {
const errMsg2 = `cannot remove a check with id: '${id2}', error: '${e instanceof Error ? e.stack : String(e)}'`;
logger_default.error(errMsg2, logMeta);
throw new Error(errMsg2);
}
}
var remove5 = async (id2, user) => {
const logOpts5 = {
scope: "removeCheck",
itemType: "check",
ref: id2,
user: user?.username,
msgType: "REMOVE"
};
logger_default.info(`remove check with, id: '${id2}', user: '${user.username}'`, logOpts5);
return removeCheck(id2, user);
};
var update = async (id2, opts, user) => {
const logMeta = {
msgType: "UPDATE",
itemType: "check",
ref: id2,
user,
scope: "updateCheck"
};
logger_default.debug(`update check with id '${id2}' with params '${JSON.stringify(opts, null, 2)}'`, logMeta);
const check = await Check_model_default.findOneAndUpdate({ _id: id2 }, opts, { new: true }).exec();
if (!check) throw new Error(`cannot find check with id: ${id2}`);
const test = await Test_model_default.findOne({ _id: check.test }).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
test.status = await calculateTestStatus(String(check.test));
await updateItemDate("VRSCheck", check);
await updateItemDate("VRSTest", test);
await test.save();
await check.save();
return check;
};
// src/server/controllers/baseline.controller.ts
var get3 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? deserializeIfJSON_default(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page", "populate"]);
const result = await generic_service_exports.get("VRSBaseline", filter, options);
res.send(result);
});
var put2 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status9.default.BAD_REQUEST, "Cannot update the baseline - Id not found");
const result = await generic_service_exports.put("VRSBaseline", id2, req.body, req?.user);
res.send(result);
});
// src/server/controllers/check.controller.ts
var import_http_status10 = __toESM(require("http-status"));
var get4 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? deserializeIfJSON_default(req.query.filter) : {};
if (req.user?.role === "user") {
filter.creatorUsername = req.user?.username;
}
const options = pick_default(req.query, ["sortBy", "limit", "page", "populate"]);
const result = await generic_service_exports.get("VRSCheck", filter, options);
res.send(result);
});
var getViaPost = catchAsync_default(async (req, res) => {
const filter = req.body.filter ? pick_default(req.body, ["filter"]).filter : {};
const options = req.body.options ? pick_default(req.body, ["options"]).options : {};
const result = await generic_service_exports.get("VRSCheck", filter, options);
res.send(result);
});
var update2 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status10.default.BAD_REQUEST, "Cannot accept the check - Id not found");
const opts = removeEmptyProperties(req.body);
const user = req?.user?.username;
const result = await check_service_exports.update(id2, opts, user);
res.send(result);
});
var accept3 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status10.default.BAD_REQUEST, "Cannot accept the check - Id not found");
if (!req.body.baselineId) throw new ApiError_default(import_http_status10.default.BAD_REQUEST, `Cannot accept the check: ${id2} - new Baseline Id not found`);
const result = await check_service_exports.accept(id2, req.body.baselineId, req?.user);
res.send(result);
});
var remove6 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status10.default.BAD_REQUEST, "Cannot remove the check - Id not found");
const result = await check_service_exports.remove(id2, req?.user);
res.send(result);
});
// src/server/controllers/client.controller.ts
var import_http_status11 = __toESM(require("http-status"));
// src/server/schemas/CreateCheck.shema.ts
var import_zod8 = require("zod");
var createCheckParamsSchema = import_zod8.z.object({
branch: import_zod8.z.string().min(1),
appName: import_zod8.z.string().min(1),
suitename: import_zod8.z.string().min(1),
testid: import_zod8.z.string().regex(/^[a-f0-9]{24}$/),
// Regex for 24 hex characters
name: import_zod8.z.string().min(1),
viewport: import_zod8.z.string().regex(/^\d+x\d+$/),
// "WidthxHeight" format
browserName: import_zod8.z.string().min(1),
browserVersion: import_zod8.z.string().min(1),
browserFullVersion: import_zod8.z.string(),
os: import_zod8.z.string().min(1),
hashcode: import_zod8.z.string().length(128)
// Assuming hashcode is always 128 chars length
});
// src/server/schemas/GetBaseline.shema.ts
var import_zod9 = require("zod");
var RequiredIdentOptionsSchema = import_zod9.z.object({
name: import_zod9.z.string().min(1),
viewport: import_zod9.z.string().min(3),
browserName: import_zod9.z.string().min(1),
os: import_zod9.z.string().min(1),
app: import_zod9.z.string().min(1),
branch: import_zod9.z.string().min(1)
});
var IdentJSONStringSchema = import_zod9.z.string().optional().refine((data) => {
if (!data) return false;
try {
const parsed = JSON.parse(data);
RequiredIdentOptionsSchema.parse(parsed);
return true;
} catch (e) {
return false;
}
}, {
message: "Invalid JSON string or does not match the required schema"
}).openapi({
description: "baseline filter based on ident",
example: '{"name": "Login page", "viewport": "1366x768", "browserName": "chrome", "os": "macOS", "app": "My App", "branch": "master"}'
});
// src/server/schemas/App.schema.ts
var import_zod_to_openapi4 = require("@asteasolutions/zod-to-openapi");
var import_zod10 = require("zod");
(0, import_zod_to_openapi4.extendZodWithOpenApi)(import_zod10.z);
var registry2 = new import_zod_to_openapi4.OpenAPIRegistry();
var AppInfoRespSchema = import_zod10.z.object({
version: commonValidations.version
});
var AppRespSchema = import_zod10.z.object({
_id: commonValidations.id,
id: commonValidations.id,
name: import_zod10.z.string().min(1, "AppRespSchema: the name is empty").openapi({ example: "Admin Panel" })
});
// src/server/controllers/client.controller.ts
var startSession2 = catchAsync_default(async (req, res) => {
const params = pick_default(
req.body,
[
"name",
"status",
"app",
"tags",
"branch",
"viewport",
"browser",
"browserVersion",
"browserFullVersion",
"os",
"run",
"runident",
"suite"
]
);
const result = await client_service_exports.startSession(params, String(req?.user?.username));
res.send(result);
});
var endSession2 = catchAsync_default(async (req, res) => {
const testId = req.params.testid;
if (!testId || testId === "undefined") {
throw new ApiError_default(import_http_status11.default.BAD_REQUEST, "Cannot stop test Session testId is empty");
}
const result = await client_service_exports.endSession(testId, String(req?.user?.username));
res.send(result);
});
var createCheck2 = catchAsync_default(async (req, res) => {
const params = req.body;
paramsGuard(params, "createCheck, params", createCheckParamsSchema);
const apiKey = req.headers.apikey;
const currentUser = await User_model_default.findOne({ apiKey });
if (!currentUser) throw new ApiError_default(import_http_status11.default.NOT_FOUND, `cannot get current user by API`);
const logOpts5 = {
scope: "createCheck",
user: currentUser.username,
itemType: "check",
msgType: "CREATE"
};
logger_default.info(`start to create check: '${params.name}'`, logOpts5);
logger_default.debug(`try to find test with id: '${params.testid}'`, logOpts5);
const test = await Test_model_default.findById(params.testid);
if (!test) {
const errMsg2 = `can't find test with id: '${params.testid}', parameters: '${JSON.stringify(req.body)}', username: '${currentUser.username}', apiKey: ${apiKey}`;
throw new ApiError_default(import_http_status11.default.NOT_FOUND, errMsg2);
}
const app = await App_model_default.findOne({ name: params.appName });
if (!app) throw new ApiError_default(import_http_status11.default.NOT_FOUND, `cannot get the app: ${params.appName}`);
const suite = await Suite_model_default.findOne({ name: params.suitename });
if (!suite) throw new ApiError_default(import_http_status11.default.NOT_FOUND, `cannot get the suite: ${params.suitename}`);
await updateItem("VRSTest", { _id: test.id }, {
suite: suite.id,
creatorId: currentUser._id,
creatorUsername: currentUser.username
});
const result = await client_service_exports.createCheck(
{
branch: params.branch,
hashCode: params.hashcode,
// testId: params.testid,
name: params.name,
viewport: params.viewport,
browserName: params.browserName,
browserVersion: params.browserVersion,
browserFullVersion: params.browserFullVersion,
os: params.os,
files: req.files,
domDump: params.domdump,
vShifting: params.vShifting
},
test,
suite,
app,
currentUser
);
if (result.status === "needFiles") {
res.status(206).json({
status: "requiredFileData",
message: "could not find a snapshot with such a hash code, please add image file data and resend request",
hashCode: params.hashcode
});
return;
}
res.json(result);
});
var getIdent2 = catchAsync_default(async (req, res) => {
const result = client_service_exports.getIdent();
res.send(result);
});
var getBaselines2 = catchAsync_default(async (req, res) => {
const filter = pick_default(
req.query.filter ? deserializeIfJSON_default(String(req.query.filter)) : {},
["name", "viewport", "browserName", "os", "app", "branch"]
);
paramsGuard(filter, "getBaseline, filter", RequiredIdentOptionsSchema);
const options = pick_default(req.query, ["sortBy", "limit", "page", "populate"]);
const result = await client_service_exports.getBaselines(filter, options);
res.send(result);
});
var getSnapshots = catchAsync_default(async (req, res) => {
const filter = pick_default(
req.query.filter ? deserializeIfJSON_default(String(req.query.filter)) : {},
["_id", "name", "imghash", "createdDate", "filename", "id"]
);
const options = pick_default(req.query, ["sortBy", "limit", "page", "populate"]);
const result = await generic_service_exports.get("VRSSnapshot", filter, options);
res.send(result);
});
// src/server/controllers/logs.controller.ts
var import_http_status12 = __toESM(require("http-status"));
var import_bson3 = require("bson");
var getLogs = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? import_bson3.EJSON.parse(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await logs_service_exports.queryLogs(filter, options);
res.send(result);
});
var distinct2 = catchAsync_default(async (req, res) => {
const { field } = pick_default(req.query, ["field"]);
const result = await logs_service_exports.distinct(String(field));
res.send(result);
});
var createLog = catchAsync_default(async (req, res) => {
const user = await logs_service_exports.createLogs(req.body);
res.status(import_http_status12.default.CREATED).send(user);
});
// src/server/controllers/snapshots.controller.ts
var import_bson4 = require("bson");
var get5 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? import_bson4.EJSON.parse(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await generic_service_exports.get("VRSSnapshot", filter, options);
res.send(result);
});
// src/server/controllers/tasks.controller.ts
var task_test2 = catchAsync_default(async (req, res) => {
const { options } = pick_default(req.query, ["options"]);
await tasks_service_exports.task_test(String(options), req, res);
});
var task_handle_old_checks2 = catchAsync_default(async (req, res) => {
const options = pick_default(req.query, ["days", "remove"]);
await tasks_service_exports.task_handle_old_checks(options, res);
});
var task_handle_database_consistency2 = catchAsync_default(async (req, res) => {
const options = pick_default(req.query, ["days", "clean"]);
await tasks_service_exports.task_handle_database_consistency(options, res);
});
var task_remove_old_logs2 = catchAsync_default(async (req, res) => {
const options = pick_default(req.query, ["days", "statistics"]);
await tasks_service_exports.task_remove_old_logs(options, res);
});
var status2 = catchAsync_default(async (req, res) => {
res.send(await tasks_service_exports.status(req.user));
});
var screenshots2 = catchAsync_default(async (req, res) => {
res.send(await tasks_service_exports.screenshots());
});
var loadTestUser2 = catchAsync_default(async (req, res) => {
res.send(await tasks_service_exports.loadTestUser());
});
// src/server/controllers/test.controller.ts
var import_http_status13 = __toESM(require("http-status"));
var getTest = catchAsync_default(async (req, res) => {
const filter = {
...deserializeIfJSON_default(String(req.query.base_filter)),
...deserializeIfJSON_default(String(req.query.filter))
};
if (req.user?.role === "user") {
filter.creatorUsername = req.user?.username;
}
const options = pick_default(req.query, ["sortBy", "limit", "page", "populate"]);
const result = await test_service_exports.queryTests(filter, options);
res.status(import_http_status13.default.OK).send(result);
});
var distinct_with_filter = catchAsync_default(async (req, res) => {
const filter = req.query.filter ? deserializeIfJSON_default(String(req.query.filter)) : void 0;
const options = { ...pick_default(req.query, ["sortBy", "limit", "page", "populate"]), field: req.params.field };
const result = await test_service_exports.queryTestsDistinct(filter, options);
res.status(import_http_status13.default.OK).send(result);
});
var distinct3 = catchAsync_default(async (req, res) => {
const filter = {};
const options = { ...pick_default(req.query, ["sortBy", "limit", "page", "populate"]), field: req.params.id };
const result = await test_service_exports.queryTestsDistinct(filter, options);
res.status(import_http_status13.default.OK).send(result);
});
var remove7 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status13.default.BAD_REQUEST, "Cannot remove the test - Id not found");
if (!req.user) throw new ApiError_default(import_http_status13.default.BAD_REQUEST, "Cannot remove the test - req.user is empty");
const result = await test_service_exports.remove(id2, req?.user);
res.send(result);
});
var accept4 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!id2) throw new ApiError_default(import_http_status13.default.BAD_REQUEST, "Cannot accept the check - Id not found");
if (!req.user) throw new ApiError_default(import_http_status13.default.BAD_REQUEST, "Cannot accept the check - req.user is empty");
const result = await test_service_exports.accept(id2, req?.user);
res.send(result);
});
// src/server/controllers/users.controller.ts
var users_controller_exports = {};
__export(users_controller_exports, {
create: () => create,
current: () => current,
get: () => get6,
getById: () => getById,
remove: () => remove8,
update: () => update3
});
var import_http_status14 = __toESM(require("http-status"));
var import_bson5 = require("bson");
var current = catchAsync_default(async (req, res) => {
const logOpts5 = {
scope: "users",
msgType: "GET_CURRENT_USER"
};
logger_default.debug(`current user is: '${req?.user?.username || "not_logged"}'`, logOpts5);
res.status(import_http_status14.default.OK).json({
id: req?.user?.id,
username: req?.user?.username,
firstName: req?.user?.firstName,
lastName: req?.user?.lastName,
role: req?.user?.role
});
});
var get6 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? import_bson5.EJSON.parse(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await user_service_exports.queryUsers(filter, options);
res.send(result);
});
var getById = catchAsync_default(async (req, res) => {
const user = await user_service_exports.getUserById(req.params.userId);
res.send(user);
});
var create = catchAsync_default(async (req, res) => {
const logOpts5 = {
scope: "users",
msgType: "CREATE"
};
try {
const userData = pick_default(req.body, [
"username",
"firstName",
"lastName",
"role",
"password",
"apiKey",
"expiration",
"meta"
]);
const user = await user_service_exports.createUser(userData);
res.status(import_http_status14.default.CREATED).send(user);
} catch (e) {
if (e instanceof ApiError_default && e.statusCode) {
logger_default.error(e, logOpts5);
res.status(e.statusCode).json({ message: e.message });
} else {
logger_default.error(errMsg(e), logOpts5);
throw e;
}
}
});
var update3 = catchAsync_default(async (req, res) => {
const user = await user_service_exports.updateUserById(req.params.userId, req.body);
res.send(user);
});
var remove8 = catchAsync_default(async (req, res) => {
await user_service_exports.deleteUserById(req.params.userId);
res.status(import_http_status14.default.NO_CONTENT).send();
});
// src/server/controllers/app.controller.ts
var app_controller_exports = {};
__export(app_controller_exports, {
get: () => get7,
info: () => info
});
var import_http_status15 = __toESM(require("http-status"));
var info = catchAsync_default(async (req, res) => {
res.status(import_http_status15.default.OK).json({ version: config.version });
});
var get7 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? deserializeIfJSON_default(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await app_service_exports.get(filter, options);
res.send(result);
});
// src/server/controllers/settings.controller.ts
var getSettings = catchAsync_default(async (req, res) => {
const AppSettings3 = await appSettings;
const result = AppSettings3.cache;
res.json(result);
});
var updateSetting = catchAsync_default(async (req, res) => {
const AppSettings3 = await appSettings;
const { name } = req.params;
await AppSettings3.set(name, req.body.value);
if (req.body.enabled === false) {
await AppSettings3.disable(name);
} else {
await AppSettings3.enable(name);
}
res.json({ message: "success" });
});
// src/server/controllers/runs.controller.ts
var import_bson6 = require("bson");
var get8 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? import_bson6.EJSON.parse(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await generic_service_exports.get("VRSRun", filter, options);
res.send(result);
});
var remove9 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!req.user) throw new Error("req.user is empty");
const result = await run_service_exports.remove(id2, req?.user);
res.send(result);
});
// src/server/controllers/suite.controller.ts
var import_bson7 = require("bson");
var get9 = catchAsync_default(async (req, res) => {
const filter = typeof req.query.filter === "string" ? import_bson7.EJSON.parse(req.query.filter) : {};
const options = pick_default(req.query, ["sortBy", "limit", "page"]);
const result = await generic_service_exports.get("VRSSuite", filter, options);
res.send(result);
});
var remove10 = catchAsync_default(async (req, res) => {
const { id: id2 } = req.params;
if (!req.user) throw new Error("req.user is empty");
const result = await suite_service_exports.remove(id2, req?.user);
res.send(result);
});
// src/server/schemas/common/RequestPagination.schema.ts
var import_zod_to_openapi6 = require("@asteasolutions/zod-to-openapi");
var import_zod12 = require("zod");
// src/server/schemas/common/requestQueryFilterSchema.schema.ts
var import_zod_to_openapi5 = require("@asteasolutions/zod-to-openapi");
var import_zod11 = require("zod");
(0, import_zod_to_openapi5.extendZodWithOpenApi)(import_zod11.z);
var requestQueryFilterSchema = import_zod11.z.string().optional().refine((data) => {
if (!data) return false;
try {
const parsed = JSON.parse(data);
const valueSchema = import_zod11.z.lazy(() => import_zod11.z.union([
import_zod11.z.string(),
import_zod11.z.number(),
import_zod11.z.boolean(),
import_zod11.z.array(import_zod11.z.any()),
import_zod11.z.record(import_zod11.z.any())
]));
const schema = import_zod11.z.record(valueSchema);
schema.parse(parsed);
return true;
} catch (e) {
return false;
}
}, {
message: "Invalid JSON string or does not match the required schema"
}).openapi({ example: '{"key1": "value1", "key2": 123, "key3": true, "$and":[{"name":"CheckName"}]}' });
// src/server/schemas/common/RequestPagination.schema.ts
(0, import_zod_to_openapi6.extendZodWithOpenApi)(import_zod12.z);
var RequestPaginationSchema = import_zod12.z.object({
filter: requestQueryFilterSchema.optional(),
limit: commonValidations.positiveNumberString.optional().openapi({ example: "10" }),
page: commonValidations.positiveNumberString.optional().openapi({ example: "1" }),
sortBy: import_zod12.z.string().optional().openapi({ example: "name:desc" }),
populate: import_zod12.z.string().optional().openapi({ example: "test" })
});
// src/server/schemas/utils/createRequestQuerySchema.ts
var import_zod13 = require("zod");
var createRequestQuerySchema = (schema) => import_zod13.z.object({ query: schema });
// src/server/routes/v1/app.route.ts
var registry3 = new import_zod_to_openapi7.OpenAPIRegistry();
var router2 = import_express2.default.Router();
registry3.registerPath({
method: "get",
path: "/v1/app/info",
summary: "The current Syngrisi instance information.",
tags: ["App"],
responses: createApiResponse(AppInfoRespSchema, "Success")
});
router2.get(
"/info",
validateRequest(SkipValid, "get, /v1/app/info"),
app_controller_exports.info
);
registry3.registerPath({
method: "get",
path: "/v1/app",
summary: "List of applications (projects) with pagination, and optional filtering and sorting.",
tags: ["App"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(AppRespSchema, "Success")
});
router2.get(
"/",
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/app"),
app_controller_exports.get
);
var app_route_default = router2;
// src/server/routes/v1/tests.route.ts
var import_express3 = __toESM(require("express"));
var import_zod_to_openapi8 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Test.schema.ts
var import_zod14 = require("zod");
var TestGetSchema = import_zod14.z.object({
_id: commonValidations.id,
name: import_zod14.z.string().min(1).openapi({
description: "Name of the test",
example: "Get IP"
}),
status: import_zod14.z.string().openapi({
description: "Status of the test",
example: "Failed"
}),
browserName: import_zod14.z.string().openapi({
description: "Name of the browser",
example: "chrome"
}),
browserVersion: import_zod14.z.string().openapi({
description: "Version of the browser",
example: "125"
}),
branch: import_zod14.z.string().openapi({
description: "Branch name",
example: "master"
}),
tags: import_zod14.z.array(import_zod14.z.string()).openapi({
description: "Tags associated with the test",
example: ["@smoke", "@regression"]
}),
viewport: import_zod14.z.string().openapi({
description: "Viewport size",
example: "1366x768"
}),
os: import_zod14.z.string().openapi({
description: "Operating system",
example: "macOS"
}),
app: commonValidations.id.openapi({
description: "Application identifier",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
blinking: import_zod14.z.number().openapi({
description: "Blinking count",
example: 0
}),
updatedDate: commonValidations.date.openapi({
description: "Last update date of the test",
example: "2024-06-13T19:55:40.108Z"
}),
startDate: commonValidations.date.openapi({
description: "Start date of the test",
example: "2024-06-13T19:54:28.409Z"
}),
checks: import_zod14.z.array(commonValidations.id).openapi({
description: "List of checks associated with the test",
example: ["666b4e7e421977cbf466b458", "666b4ebc421977cbf466b47c"]
}),
suite: commonValidations.id.openapi({
description: "Suite identifier",
example: "666b3b828833d0cf24a670d7"
}),
run: commonValidations.id.openapi({
description: "Run identifier",
example: "666b4e74421977cbf466b443"
}),
creatorId: commonValidations.id.openapi({
description: "Creator identifier",
example: "66519e582c2c701cc438ce59"
}),
creatorUsername: import_zod14.z.string().openapi({
description: "Username of the creator",
example: "Guest"
}),
markedAs: import_zod14.z.string().openapi({
description: "Marked as status",
example: "Unaccepted"
}),
calculatedViewport: import_zod14.z.string().openapi({
description: "Calculated viewport size",
example: "1366x768"
}),
id: commonValidations.id.openapi({
description: "ID of the test",
example: "666b4e74421977cbf466b446"
})
});
var UpdateTestSchema = TestGetSchema.omit({ id: true, _id: true }).partial();
var TestAcceptSchema = import_zod14.z.object({
id: commonValidations.id
});
var validIds = [
"suite",
"run",
"markedAs",
"creatorId",
"creatorUsername",
"name",
"status",
"browserName",
"browserVersion",
"branch",
"tags",
"viewport",
"os",
"app",
"startDate"
];
var TestDistinctRequestFieldParamsSchema = import_zod14.z.object({
field: import_zod14.z.enum(validIds).openapi({
description: "Parameter identifier",
example: "suite"
})
});
// src/server/middlewares/ensureLogin/ensureLoggedIn.ts
var handleBasicAuth = async (req) => {
const logOpts5 = {
scope: "handleBasicAuth",
msgType: "AUTH_API"
};
if (req.isAuthenticated()) {
return { type: "success", status: 200 };
}
const AppSettings3 = await appSettings;
if (!await AppSettings3.isAuthEnabled()) {
const guest = await User_model_default.findOne({ username: "Guest" });
const result2 = new Promise((resolve) => {
req.logIn(guest, (err) => {
if (err) {
logger_default.error(`cannot find guest user: '${err}'`, logOpts5);
resolve({
type: "redirect",
status: 301,
value: `/auth?=Error: cannot find guest user: ${err}`,
user: null
});
} else {
resolve({
type: "success",
status: 200,
value: "",
user: guest
});
}
});
});
return result2;
}
const result = {
type: "error",
status: 400,
value: "",
user: null
};
if (await AppSettings3.isAuthEnabled() && await AppSettings3.isFirstRun() && !env.SYNGRISI_DISABLE_FIRST_RUN) {
logger_default.info("first run, set admin password", logOpts5);
result.type = "redirect";
result.status = 301;
result.value = "/auth/change?first_run=true";
return result;
}
if (await AppSettings3.isAuthEnabled()) {
logger_default.info(`user is not authenticated, will redirected - ${req.originalUrl}`, logOpts5);
result.type = "redirect";
result.status = 301;
if (req?.originalUrl !== "/") {
result.value = `/auth?origin=${encodeURIComponent(req.originalUrl)}`;
return result;
}
result.value = "/auth";
return result;
}
};
function ensureLoggedIn(options) {
return async (req, res, next) => {
const result = await handleBasicAuth(req);
req.user = result.user || req.user;
if (result.type === "success") {
return next();
}
res.status(result.status).redirect(result.value);
return next("redirect");
};
}
var handleAPIAuth = async (hashedApiKey) => {
const logOpts5 = {
scope: "handleAPIAuth",
msgType: "AUTH_API"
};
const result = {
status: 400,
type: "error",
value: "",
user: null
};
const AppSettings3 = await appSettings;
if (!await AppSettings3.isAuthEnabled()) {
const guest = await User_model_default.findOne({ username: "Guest" });
if (!guest) {
logger_default.error("cannot find Guest user", logOpts5);
result.type = "error";
result.value = "cannot find Guest user";
return result;
}
logger_default.debug("authentication disabled", logOpts5, { user: "Guest" });
result.type = "success";
result.user = guest;
result.status = 200;
return result;
}
if (!hashedApiKey) {
logger_default.debug("API key missing", logOpts5);
result.type = "error";
result.status = 401;
result.value = "API key missing";
return result;
}
const user = await User_model_default.findOne({ apiKey: hashedApiKey });
if (!user) {
logger_default.error(`wrong API key: ${hashedApiKey}`, logOpts5);
result.type = "error";
result.status = 401;
result.value = "wrong API key";
return result;
}
logger_default.debug("authenticated", { ...logOpts5, ...{ user: user?.username } });
result.type = "success";
result.status = 200;
result.user = user;
return result;
};
function ensureApiKey() {
const logOpts5 = {
scope: "ensureApiKey",
msgType: "AUTH_API"
};
return async (req, res, next) => {
logger_default.silly(`headers: ${JSON.stringify(req.headers, null, "..")}`, logOpts5);
logger_default.silly(`SYNGRISI_AUTH: '${env.SYNGRISI_AUTH}'`);
const hashedApiKey = req.headers.apikey || req.query.apikey;
const result = await handleAPIAuth(hashedApiKey);
req.user = req.user || result.user;
req.headers.apikey = result?.user?.apiKey || req?.headers?.apikey;
if (result.type !== "success") {
logger_default.info(`${result.value} - ${req.originalUrl}`, logOpts5);
res.status(result.status).json({ error: result.value });
return next(new Error(result.value));
}
return next();
};
}
function ensureLoggedInOrApiKey() {
return async (req, res, next) => {
const basicAuthResult = await handleBasicAuth(req);
const hashedApiKey = req.headers.apikey || req.query.apikey;
const apiKeyResult = await handleAPIAuth(hashedApiKey);
req.user = req.user || apiKeyResult.user;
if (basicAuthResult.type !== "success" && apiKeyResult.type !== "success") {
logger_default.info(`Unauthorized - ${req.originalUrl}`);
res.status(401).json({ error: `Unauthorized - ${req.originalUrl}` });
return next(new Error(`Unauthorized - ${req.originalUrl}`));
}
return next();
};
}
// src/server/schemas/utils/createRequestParamsSchema.ts
var import_zod15 = require("zod");
var createRequestParamsSchema = (schema) => import_zod15.z.object({ params: schema });
var getByIdParamsSchema = (id2 = "id") => createRequestParamsSchema(
import_zod15.z.object({ [id2]: commonValidations.id })
);
// src/server/schemas/TestDistinct.schema.ts
var import_zod16 = require("zod");
var validIds2 = [
"suite",
"run",
"markedAs",
"creatorId",
"creatorUsername",
"name",
"status",
"browserName",
"browserVersion",
"branch",
"tags",
"viewport",
"os",
"app",
"startDate",
"filter"
];
var TestDistinctRequestParamsSchema = import_zod16.z.object({
id: import_zod16.z.enum(validIds2).openapi({
description: "Parameter identifier",
example: "suite"
})
});
var TestDistinctResponseSchema = import_zod16.z.object({
name: import_zod16.z.string().min(1).openapi({
description: "Distinct field value",
example: "chrome"
})
});
// src/server/routes/v1/tests.route.ts
var registry4 = new import_zod_to_openapi8.OpenAPIRegistry();
var router3 = import_express3.default.Router();
registry4.registerPath({
method: "get",
path: "/v1/tests",
summary: "Get list of tests",
tags: ["Tests"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(TestGetSchema, "Success")
});
router3.get(
"/",
ensureLoggedIn(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/checks"),
getTest
);
registry4.registerPath({
method: "get",
path: "/v1/tests/distinct/{field}",
summary: "List of certain unique fields across all tests",
tags: ["Tests"],
request: { params: TestDistinctRequestFieldParamsSchema, query: RequestPaginationSchema },
responses: createPaginatedApiResponse(TestDistinctResponseSchema, "Success")
});
router3.get(
"/distinct/:field",
ensureLoggedIn(),
validateRequest(
createRequestParamsSchema(TestDistinctRequestFieldParamsSchema).merge(
createRequestQuerySchema(RequestPaginationSchema)
),
"get, /distinct/:field"
),
distinct_with_filter
);
registry4.registerPath({
method: "delete",
path: "/v1/tests/{id}",
summary: "Delete a test by ID",
tags: ["Tests"],
request: commonValidations.paramsId,
responses: createApiResponse(TestGetSchema, "Success")
});
router3.delete(
"/:id",
ensureLoggedIn(),
// validateRequest(getByIdParamsSchema(), '/v1/runs/{id}'),
validateRequest(getByIdParamsSchema(), "delete, /v1/tests/{id}"),
remove7
);
registry4.registerPath({
method: "put",
path: "/v1/tests/accept/{id}",
summary: "Accept a test by ID",
tags: ["Tests"],
request: commonValidations.paramsId,
// request: { params: TestDistinctRequestParamsSchema, body: createRequestOpenApiBodySchema(TestAcceptSchema) },
responses: createApiResponse(commonValidations.success, "Success")
});
router3.put(
"/accept/:id",
ensureLoggedIn(),
validateRequest(getByIdParamsSchema(), "put, /v1/tests/accept/{id}"),
accept4
);
var tests_route_default = router3;
// src/server/routes/v1/users.route.ts
var import_express4 = __toESM(require("express"));
var import_http_status17 = __toESM(require("http-status"));
var import_zod_to_openapi10 = require("@asteasolutions/zod-to-openapi");
// src/server/middlewares/authorization.ts
var import_http_status16 = __toESM(require("http-status"));
var authorization = (type) => {
const types = {
admin: catchAsync_default(async (req, res, next) => {
const AppSettings3 = await appSettings;
if (!await AppSettings3.isAuthEnabled()) {
return next();
}
if (req.user?.role === "admin") {
logger_default.silly(`user: '${req.user?.username}' was successfully authorized, type: '${type}'`);
return next();
}
logger_default.warn(`user authorization: '${req.user?.username}' wrong role, type: '${type}'`);
throw new ApiError_default(import_http_status16.default.FORBIDDEN, "Authorization Error - wrong Role");
}),
user: catchAsync_default(async (req, res, next) => {
const AppSettings3 = await appSettings;
if (!await AppSettings3.isAuthEnabled()) {
return next();
}
if (req.user?.role === "admin") {
logger_default.silly(`user: '${req.user?.username}' was successfully authorized, type: '${type}'`);
return next();
}
if (type === "user" && (req.user?.role === "user" || req.user?.role === "reviewer")) {
logger_default.silly(`user: '${req.user?.username}' was successfully authorized, type: '${type}'`);
return next();
}
logger_default.warn(`user authorization: '${req.user?.username}' wrong role, type: '${type}'`);
throw new ApiError_default(import_http_status16.default.FORBIDDEN, "Authorization Error - wrong Role");
})
};
if (types[type]) return types[type];
return catchAsync_default(
() => {
logger_default.error(JSON.stringify(new ApiError_default(import_http_status16.default.FORBIDDEN, "Wrong type of authorization")));
throw new ApiError_default(import_http_status16.default.FORBIDDEN, "Authorization Error - wrong type of authorization");
}
);
};
// src/server/middlewares/compressionFilter.ts
var import_compression = __toESM(require("compression"));
// src/server/schemas/User.schema.ts
var import_zod_to_openapi9 = require("@asteasolutions/zod-to-openapi");
var import_zod17 = require("zod");
(0, import_zod_to_openapi9.extendZodWithOpenApi)(import_zod17.z);
var UserSchema2 = import_zod17.z.object({
username: import_zod17.z.string().min(1, "UserSchema: the username name is empty").openapi({ example: "johndoe@example.com" }),
firstName: import_zod17.z.string().min(1, "UserSchema: the firstName name is empty").openapi({ example: "John" }),
lastName: import_zod17.z.string().min(1, "UserSchema: the lastName name is empty").openapi({ example: "Doe" }),
role: import_zod17.z.enum(["admin", "reviewer", "user"]),
password: import_zod17.z.string().optional(),
token: import_zod17.z.string().optional(),
apiKey: import_zod17.z.string().optional(),
createdDate: import_zod17.z.date().optional().openapi({ example: "2024-05-25T15:23:21.150Z" }),
updatedDate: import_zod17.z.date().optional().openapi({ example: "2024-05-26T15:23:21.150Z" }),
expiration: import_zod17.z.date().optional(),
meta: import_zod17.z.record(import_zod17.z.any()).optional(),
_id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" }),
id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" })
});
var UserCreateReqSchema = import_zod17.z.object({
username: import_zod17.z.string().min(1, "UserSchema: the username name is empty").openapi({ example: "johndoe@example.com" }),
firstName: import_zod17.z.string().min(1, "UserSchema: the firstName name is empty").openapi({ example: "John" }),
lastName: import_zod17.z.string().min(1, "UserSchema: the lastName name is empty").openapi({ example: "Doe" }),
role: import_zod17.z.enum(["admin", "reviewer", "user"]),
email: import_zod17.z.string().optional(),
password: import_zod17.z.string()
});
var UserCurrentRespSchema = import_zod17.z.object({
_id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" }),
id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" }),
username: import_zod17.z.string().min(1, "UserSchema: the username name is empty").openapi({ example: "johndoe@example.com" }),
firstName: import_zod17.z.string().min(1, "UserSchema: the firstName name is empty").openapi({ example: "John" }),
lastName: import_zod17.z.string().min(1, "UserSchema: the lastName name is empty").openapi({ example: "Doe" }),
role: import_zod17.z.enum(["admin", "reviewer", "user"])
});
var UserGetRespSchema = UserCurrentRespSchema;
var UserCreateRespSchema = import_zod17.z.object({
username: import_zod17.z.string().min(1, "UserSchema: the username name is empty").openapi({ example: "johndoe@example.com" }),
firstName: import_zod17.z.string().min(1, "UserSchema: the firstName name is empty").openapi({ example: "John" }),
lastName: import_zod17.z.string().min(1, "UserSchema: the lastName name is empty").openapi({ example: "Doe" }),
role: import_zod17.z.enum(["admin", "reviewer", "user"]),
createdDate: import_zod17.z.date().optional().openapi({ example: "2024-05-25T15:23:21.150Z" }),
updatedDate: import_zod17.z.date().optional().openapi({ example: "2024-05-26T15:23:21.150Z" }),
_id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" }),
id: commonValidations.id.openapi({ example: "6bbF35cAB3C59dA969edAe79" })
});
// src/server/routes/v1/users.route.ts
var registry5 = new import_zod_to_openapi10.OpenAPIRegistry();
var router4 = import_express4.default.Router();
registry5.registerPath({
method: "get",
path: "/v1/users/current",
summary: "Retrieve current user details.",
tags: ["Users"],
responses: createApiResponse(UserCurrentRespSchema, "Success")
});
router4.get(
"/current",
validateRequest(SkipValid, "get, /v1/users/current"),
users_controller_exports.current
);
registry5.registerPath({
method: "get",
path: "/v1/users/",
summary: "List users with pagination, and optional filtering and sorting.",
tags: ["Users"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(UserSchema2, "Success")
});
registry5.registerPath({
method: "post",
path: "/v1/users/",
summary: "Create a new user.",
tags: ["Users"],
request: { body: createRequestOpenApiBodySchema(UserCreateReqSchema) },
responses: createApiResponse(UserCreateRespSchema, "Success")
});
router4.route("/").get(
ensureLoggedIn(),
authorization("user"),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/users/"),
users_controller_exports.get
).post(
ensureLoggedIn(),
authorization("admin"),
validateRequest(createRequestBodySchema(UserCreateReqSchema), "post, /v1/users/"),
users_controller_exports.create
);
registry5.registerPath({
method: "get",
path: "/v1/users/{userId}",
summary: "Retrieve user details by user ID.",
tags: ["Users"],
request: { params: getByIdParamsSchema("userId") },
responses: createApiResponse(UserGetRespSchema, "Success")
});
registry5.registerPath({
method: "patch",
path: "/v1/users/{userId}",
summary: "Update user details by user ID.",
tags: ["Users"],
request: { params: getByIdParamsSchema("userId"), body: createRequestOpenApiBodySchema(UserSchema2) },
responses: createApiResponse(UserGetRespSchema, "Success")
});
registry5.registerPath({
method: "delete",
path: "/v1/users/{userId}",
summary: "Remove user by user ID.",
tags: ["Users"],
request: { params: getByIdParamsSchema("userId") },
responses: createApiEmptyResponse("No Content", import_http_status17.default.NO_CONTENT)
});
router4.route("/:userId").get(
ensureLoggedIn(),
authorization("admin"),
validateRequest(getByIdParamsSchema("userId"), "get, /v1/users/{userId}"),
users_controller_exports.getById
).patch(
ensureLoggedIn(),
authorization("admin"),
validateRequest(getByIdParamsSchema("userId"), "patch, /v1/users/{userId}"),
users_controller_exports.update
).delete(
ensureLoggedIn(),
authorization("admin"),
validateRequest(getByIdParamsSchema("userId"), "delete, /v1/users/{userId}"),
users_controller_exports.remove
);
var users_route_default = router4;
// src/server/routes/v1/logs.route.ts
var import_express5 = __toESM(require("express"));
var import_zod_to_openapi11 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Logs.schema.ts
var import_zod18 = require("zod");
var LogGetSchema = import_zod18.z.object({
_id: commonValidations.id,
level: import_zod18.z.string().min(1).openapi({
description: "Log level",
example: "info"
}),
message: import_zod18.z.string().min(1).openapi({
description: "Log message",
example: "User logged in"
}),
timestamp: commonValidations.date.openapi({
description: "Timestamp of the log entry",
example: "2024-05-26T10:49:19.896Z"
}),
meta: import_zod18.z.object({}).optional().openapi({
description: "Additional metadata for the log entry",
example: { userId: "66519e582c2c701cc438ce59" }
}),
id: commonValidations.id
});
var LogCreateSchema = import_zod18.z.object({
level: import_zod18.z.string().min(1).openapi({
description: "Log level",
example: "info"
}).optional(),
message: import_zod18.z.string().min(1).openapi({
description: "Log message",
example: "User logged in"
}).optional(),
meta: import_zod18.z.object({}).optional().openapi({
description: "Additional metadata for the log entry",
example: { userId: "66519e582c2c701cc438ce59" }
})
});
var LogCreateRespSchema = commonValidations.success;
var LogDistinctSchema = import_zod18.z.object({
field: import_zod18.z.string().min(1).openapi({
description: "Field name for distinct query",
example: "level"
})
});
var LogDistinctResponseSchema = import_zod18.z.array(import_zod18.z.string()).openapi({
description: "Array of distinct log levels",
example: ["debug", "error", "info", "warn"]
});
// src/server/routes/v1/logs.route.ts
var import_http_status18 = __toESM(require("http-status"));
var registry6 = new import_zod_to_openapi11.OpenAPIRegistry();
var router5 = import_express5.default.Router();
registry6.registerPath({
method: "get",
path: "/v1/logs",
summary: "List of logs with pagination, and optional filtering and sorting.",
tags: ["Logs"],
responses: createPaginatedApiResponse(LogGetSchema, "Success")
});
router5.get(
"/",
ensureLoggedIn(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/logs"),
getLogs
);
registry6.registerPath({
method: "get",
path: "/v1/logs/distinct",
summary: "Get distinct log fields",
tags: ["Logs"],
request: { query: createRequestQuerySchema(LogDistinctSchema) },
responses: createApiResponse(LogDistinctResponseSchema, "Success")
});
router5.get(
"/distinct",
ensureLoggedIn(),
validateRequest(createRequestQuerySchema(LogDistinctSchema), "get, /v1/logs/distinct"),
distinct2
);
registry6.registerPath({
method: "post",
path: "/v1/logs",
summary: "Create a new log entry",
tags: ["Logs"],
request: { body: createRequestOpenApiBodySchema(LogCreateSchema) },
responses: createApiResponse(LogCreateRespSchema, "Success", import_http_status18.default.CREATED)
});
router5.post(
"/",
ensureLoggedIn(),
validateRequest(createRequestBodySchema(LogCreateSchema), "post, /v1/logs"),
createLog
);
var logs_route_default = router5;
// src/server/routes/v1/runs.route.ts
var import_express6 = __toESM(require("express"));
var import_zod_to_openapi13 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Runs.schema.ts
var import_zod19 = require("zod");
var RunResponseSchema = import_zod19.z.object({
_id: commonValidations.id,
name: import_zod19.z.string().min(1).openapi({
description: "Name of the run",
example: "DEBUG (VIKTAR)"
}),
app: commonValidations.id.openapi({
description: "App identifier",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
ident: import_zod19.z.string().uuid().openapi({
description: "Identifier for the run",
example: "7a930247-e422-4833-8ab6-b136c23d07e9"
}),
createdDate: import_zod19.z.string().datetime().openapi({
description: "Creation date of the run",
example: "2024-05-25T13:15:26.592Z"
}),
parameters: import_zod19.z.array(import_zod19.z.unknown()).openapi({
description: "Parameters of the run",
example: []
}),
updatedDate: import_zod19.z.string().datetime().openapi({
description: "Last update date of the run",
example: "2024-05-25T13:15:30.969Z"
}),
id: commonValidations.id.openapi({
description: "Run identifier",
example: "6651e46e85f83573a821d1f4"
})
});
var RunGetSchema = import_zod19.z.object({
_id: commonValidations.id,
name: import_zod19.z.string().min(1).openapi({
description: "Name of the run",
example: "Sample Run"
})
// additional fields here...
});
// src/server/schemas/common/ApiError.schema.ts
var import_zod_to_openapi12 = require("@asteasolutions/zod-to-openapi");
var import_zod20 = require("zod");
(0, import_zod_to_openapi12.extendZodWithOpenApi)(import_zod20.z);
var ApiErrorSchema = import_zod20.z.object({
name: import_zod20.z.string().openapi({
description: "Name of the error type",
example: "Error"
}),
message: import_zod20.z.string().openapi({
description: "Detailed message describing the error",
example: "cannot remove run with id: '6651e46e85f83573a821d1f4', not found"
}),
status: import_zod20.z.number().openapi({
description: "HTTP status code that corresponds to the error",
example: 404
}),
stacktrace: import_zod20.z.string().openapi({
description: "Stack trace of the error for debugging purposes",
example: "Error: cannot remove run with id: '6651e46e85f83573a821d1f4', not found\\n at Object.remove2 (/Users/exadel/Projects/SYNGRISI/packages/syngrisi/src/server/services/run.service.ts:27:15)\\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\\n at /Users/exadel/Projects/SYNGRISI/packages/syngrisi/src/server/controllers/runs.controller.ts:25:20"
})
});
// src/server/routes/v1/runs.route.ts
var import_http_status19 = __toESM(require("http-status"));
var registry7 = new import_zod_to_openapi13.OpenAPIRegistry();
var router6 = import_express6.default.Router();
registry7.registerPath({
method: "get",
path: "/v1/runs",
summary: "List of runs with pagination, and optional filtering and sorting.",
tags: ["Runs"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(RunResponseSchema, "Success")
});
router6.get(
"/",
ensureLoggedIn(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/runs"),
get8
);
registry7.registerPath({
method: "delete",
path: "/v1/runs/{id}",
summary: "Remove a run by ID",
description: "Remove a run by ID",
tags: ["Runs"],
request: commonValidations.paramsId,
responses: {
...createApiResponse(RunResponseSchema, "Success"),
...createApiResponse(ApiErrorSchema, "ApiError", import_http_status19.default.NOT_FOUND)
}
});
router6.delete(
"/:id",
ensureLoggedIn(),
validateRequest(getByIdParamsSchema(), "delete, /v1/runs/{id}"),
remove9
);
var runs_route_default = router6;
// src/server/routes/v1/snapshots.route.ts
var import_express7 = __toESM(require("express"));
var import_zod_to_openapi14 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Snapshots.schema.ts
var import_zod21 = require("zod");
var SnapshotSchema2 = import_zod21.z.object({
_id: commonValidations.id,
name: import_zod21.z.string().min(1).openapi({
description: "Name of the snapshot",
example: "Login page"
}),
filename: import_zod21.z.string().min(1).openapi({
description: "Filename of the snapshot",
example: "6651dd4f7c9186e315910b24.png"
}),
imghash: import_zod21.z.string().min(1).openapi({
description: "Image hash of the snapshot",
example: "96e8359554f12142bc19e44288295aa67a59cd128e242b6756651bf8e3d9f34caa7f587367ca8e5cdcfbaaf180adfd8825250fc7485784c41de11a9c08c1f9ab"
}),
createdDate: import_zod21.z.string().datetime().openapi({
description: "Creation date of the snapshot",
example: "2024-05-25T13:15:30.946Z"
}),
id: commonValidations.id.openapi({
description: "Snapshot identifier",
example: "6651e47285f83573a821d20e"
}),
stabMethod: import_zod21.z.string().optional(),
vOffset: import_zod21.z.number().optional()
});
var SnapshotsResponseSchema = import_zod21.z.array(
SnapshotSchema2
);
// src/server/routes/v1/snapshots.route.ts
var registry8 = new import_zod_to_openapi14.OpenAPIRegistry();
var router7 = import_express7.default.Router();
registry8.registerPath({
method: "get",
path: "/v1/snapshots",
summary: "List of snapshots with pagination, and optional filtering and sorting.",
tags: ["Snapshots"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(SnapshotsResponseSchema, "Success")
});
router7.get(
"/",
ensureLoggedIn(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/snapshots"),
get5
);
var snapshots_route_default = router7;
// src/server/routes/v1/checks.route.ts
var import_express8 = __toESM(require("express"));
var import_zod_to_openapi15 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Check.schema.ts
var import_zod22 = require("zod");
var CheckGetSchema = import_zod22.z.object({
_id: commonValidations.id,
name: import_zod22.z.string().min(1).openapi({
description: "Name of the check",
example: "Sample Check"
}),
test: commonValidations.id.openapi({
description: "Test identifier",
example: "666acbe24fe1d2a67424ff60"
}),
suite: commonValidations.id.openapi({
description: "Suite identifier",
example: "6651dd457c9186e315910b00"
}),
app: commonValidations.id.openapi({
description: "Application identifier",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
branch: import_zod22.z.string().min(1).openapi({
description: "Branch name",
example: "master"
}),
baselineId: commonValidations.id.openapi({
description: "Baseline identifier",
example: "6656ee01bac26a33185c9488"
}),
actualSnapshotId: commonValidations.id.openapi({
description: "Actual snapshot identifier",
example: "666acbf04fe1d2a67424ffa4"
}),
diffId: commonValidations.id.openapi({
description: "Difference identifier",
example: "666acbf04fe1d2a67424ffa9"
}),
updatedDate: commonValidations.date.openapi({
description: "Last update date of the check",
example: "2024-06-13T10:37:36.264Z"
}),
status: import_zod22.z.array(import_zod22.z.string().min(1)).openapi({
description: "Status of the check",
example: ["failed"]
}),
browserName: import_zod22.z.string().min(1).openapi({
description: "Browser name used for the check",
example: "chrome"
}),
browserVersion: import_zod22.z.string().min(1).openapi({
description: "Browser version used for the check",
example: "125"
}),
browserFullVersion: import_zod22.z.string().min(1).openapi({
description: "Full browser version used for the check",
example: "125.0.6422.142"
}),
viewport: import_zod22.z.string().min(1).openapi({
description: "Viewport size used for the check",
example: "1366x768"
}),
os: import_zod22.z.string().min(1).openapi({
description: "Operating system used for the check",
example: "macOS"
}),
result: import_zod22.z.string().openapi({
description: "Result of the check",
example: '{\n "isSameDimensions": false,\n "dimensionDifference": {\n "width": 24,\n "height": 8\n },\n "rawMisMatchPercentage": 67.38024135551241,\n "misMatchPercentage": "67.38",\n "analysisTime": 99,\n "executionTotalTime": "0,398753687",\n "totalCheckHandleTime": "0,431217924"\n}'
}),
run: commonValidations.id.openapi({
description: "Run identifier",
example: "666acbe24fe1d2a67424ff5d"
}),
markedAs: import_zod22.z.string().min(1).openapi({
description: "Status marked for the check",
example: "accepted"
}),
markedDate: commonValidations.date.openapi({
description: "Marked date of the check",
example: "2024-06-13T07:19:32.246Z"
}),
markedByUsername: import_zod22.z.string().min(1).openapi({
description: "Username of the user who marked the check",
example: "Administrator"
}),
creatorId: commonValidations.id.openapi({
description: "Identifier of the user who created the check",
example: "66519e582c2c701cc438ce59"
}),
creatorUsername: import_zod22.z.string().min(1).openapi({
description: "Username of the user who created the check",
example: "Guest"
}),
failReasons: import_zod22.z.array(import_zod22.z.string().min(1)).openapi({
description: "Reasons for the check failure",
example: ["wrong_dimensions", "different_images"]
}),
createdDate: commonValidations.date.openapi({
description: "Creation date of the check",
example: "2024-06-13T10:37:36.721Z"
}),
id: commonValidations.id
});
var CheckUpdateSchema = CheckGetSchema.omit({ id: true, _id: true }).partial();
var CheckAcceptSchema = import_zod22.z.object({
baselineId: commonValidations.id.openapi({
description: "Baseline identifier to accept the check",
example: "6651ec20917e9ce26f7c0849"
})
});
// src/server/routes/v1/checks.route.ts
var registry9 = new import_zod_to_openapi15.OpenAPIRegistry();
var router8 = import_express8.default.Router();
registry9.registerPath({
method: "get",
path: "/v1/checks",
summary: "List of checks with pagination, and optional filtering and sorting.",
tags: ["Checks"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(CheckGetSchema, "Success")
});
router8.get(
"/",
ensureLoggedInOrApiKey(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/checks"),
get4
);
registry9.registerPath({
method: "delete",
path: "/v1/checks/{id}",
summary: "Delete a check by ID",
tags: ["Checks"],
request: commonValidations.paramsId,
responses: createApiResponse(CheckGetSchema, "Success")
});
router8.delete(
"/:id",
ensureLoggedInOrApiKey(),
validateRequest(getByIdParamsSchema(), "delete, /v1/checks/{id}"),
remove6
);
registry9.registerPath({
method: "put",
path: "/v1/checks/{id}",
summary: "Update a check by ID",
tags: ["Checks"],
request: { params: commonValidations.paramsId.params, body: createRequestOpenApiBodySchema(CheckUpdateSchema) },
responses: createApiResponse(CheckGetSchema, "Success")
});
router8.put(
"/:id",
ensureLoggedInOrApiKey(),
validateRequest(getByIdParamsSchema().merge(createRequestBodySchema(CheckUpdateSchema)), "put, /v1/checks/{id}"),
update2
);
registry9.registerPath({
method: "put",
path: "/v1/checks/{id}/accept",
summary: "Accept a check by ID",
tags: ["Checks"],
request: { params: commonValidations.paramsId.params, body: createRequestOpenApiBodySchema(CheckAcceptSchema) },
responses: createApiResponse(CheckGetSchema, "Success")
});
router8.put(
"/:id/accept",
ensureLoggedInOrApiKey(),
validateRequest(createRequestBodySchema(CheckAcceptSchema), "put, /v1/checks/{id}/accept"),
accept3
);
var checks_route_default = router8;
// src/server/routes/v1/baselines.route.ts
var import_express9 = __toESM(require("express"));
var import_zod_to_openapi16 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Baseline.schema.ts
var import_zod23 = require("zod");
var BaselineGetSchema = import_zod23.z.object({
_id: commonValidations.id,
name: import_zod23.z.string().min(1).openapi({
description: "Name of the baseline",
example: "Green Button"
}),
app: commonValidations.id.openapi({
description: "Application identifier for the baseline",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
branch: import_zod23.z.string().min(1).openapi({
description: "Branch name for the baseline",
example: "master"
}),
browserName: import_zod23.z.string().min(1).openapi({
description: "Browser name used for the baseline",
example: "chrome"
}),
viewport: import_zod23.z.string().min(1).openapi({
description: "Viewport size used for the baseline",
example: "1366x768"
}),
os: import_zod23.z.string().min(1).openapi({
description: "Operating system used for the baseline",
example: "macOS"
}),
createdDate: commonValidations.date.openapi({
description: "Creation date of the baseline",
example: "2024-05-26T10:49:19.896Z"
}),
lastMarkedDate: commonValidations.date.openapi({
description: "Last marked date of the baseline",
example: "2024-05-26T10:49:19.852Z"
}),
markedAs: import_zod23.z.string().min(1).openapi({
description: "Status marked for the baseline",
example: "accepted"
}),
markedById: commonValidations.id.openapi({
description: "Identifier of the user who marked the baseline",
example: "66519e582c2c701cc438ce59"
}),
markedByUsername: import_zod23.z.string().min(1).openapi({
description: "Username of the user who marked the baseline",
example: "Guest"
}),
snapshootId: commonValidations.id.openapi({
description: "Snapshot identifier for the baseline",
example: "6651ec20917e9ce26f7c0849"
}),
id: commonValidations.id
});
var BaselinePutSchema = import_zod23.z.object({
name: import_zod23.z.string().min(1).openapi({
description: "Name of the baseline",
example: "Green Button"
}).optional(),
branch: import_zod23.z.string().min(1).openapi({
description: "Branch name for the baseline",
example: "master"
}).optional(),
browserName: import_zod23.z.string().min(1).openapi({
description: "Browser name used for the baseline",
example: "chrome"
}).optional(),
viewport: import_zod23.z.string().min(1).openapi({
description: "Viewport size used for the baseline",
example: "1366x768"
}).optional(),
os: import_zod23.z.string().min(1).openapi({
description: "Operating system used for the baseline",
example: "macOS"
}).optional(),
createdDate: commonValidations.date.openapi({
description: "Creation date of the baseline",
example: "2024-05-26T10:49:19.896Z"
}).optional(),
lastMarkedDate: commonValidations.date.openapi({
description: "Last marked date of the baseline",
example: "2024-05-26T10:49:19.852Z"
}).optional(),
markedAs: import_zod23.z.string().min(1).openapi({
description: "Status marked for the baseline",
example: "accepted"
}).optional(),
markedById: commonValidations.id.openapi({
description: "Identifier of the user who marked the baseline",
example: "66519e582c2c701cc438ce59"
}).optional(),
markedByUsername: import_zod23.z.string().min(1).openapi({
description: "Username of the user who marked the baseline",
example: "Guest"
}).optional()
});
// src/server/routes/v1/baselines.route.ts
var registry10 = new import_zod_to_openapi16.OpenAPIRegistry();
var router9 = import_express9.default.Router();
registry10.registerPath({
method: "get",
path: "/v1/baselines",
summary: "List of baselines with pagination, and optional filtering and sorting.",
tags: ["Baselines"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(BaselineGetSchema, "Success")
});
router9.get(
"/",
ensureLoggedInOrApiKey(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/baselines"),
get3
);
registry10.registerPath({
method: "put",
path: "/v1/baselines/{id}",
summary: "Only for testing purposes for now",
tags: ["Baselines"],
request: { params: commonValidations.paramsId.params, body: createRequestOpenApiBodySchema(BaselinePutSchema) },
responses: createApiEmptyResponse("Success")
});
router9.put(
"/:id",
ensureLoggedIn(),
validateRequest(getByIdParamsSchema().merge(createRequestBodySchema(BaselinePutSchema)), "put, /v1/baselines/{id}"),
put2
);
var baselines_route_default = router9;
// src/server/routes/v1/suites.route.ts
var import_express10 = __toESM(require("express"));
var import_zod_to_openapi17 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Suite.schema.ts
var import_zod24 = require("zod");
var SuiteGetSchema = import_zod24.z.object({
_id: commonValidations.id,
name: import_zod24.z.string().min(1).openapi({
description: "Name of the suite",
example: "Smoke tests"
}),
tags: import_zod24.z.array(import_zod24.z.string()).openapi({
description: "Tags associated with the suite",
example: []
}),
app: commonValidations.id.openapi({
description: "Application identifier",
example: "666b3b82db17d34ecdbd06f6"
}),
createdDate: commonValidations.date.openapi({
description: "Creation date of the suite",
example: "2024-06-13T18:33:38.617Z"
}),
updatedDate: commonValidations.date.openapi({
description: "Last update date of the suite",
example: "2024-06-13T19:55:40.114Z"
}),
id: commonValidations.id.openapi({
description: "ID of the suite",
example: "666b3b828833d0cf24a670d7"
})
});
// src/server/routes/v1/suites.route.ts
var import_http_status20 = __toESM(require("http-status"));
var registry11 = new import_zod_to_openapi17.OpenAPIRegistry();
var router10 = import_express10.default.Router();
registry11.registerPath({
method: "get",
path: "/v1/suites",
summary: "List of suites",
tags: ["Suites"],
responses: createPaginatedApiResponse(SuiteGetSchema, "Success")
});
router10.get(
"/",
ensureLoggedIn(),
validateRequest(SkipValid, "get, /v1/suites"),
get9
);
registry11.registerPath({
method: "delete",
path: "/v1/suites/{id}",
summary: "Delete a suite by ID",
tags: ["Suites"],
request: commonValidations.paramsId,
responses: {
...createApiResponse(SuiteGetSchema, "Success"),
...createApiResponse(ApiErrorSchema, "ApiError", import_http_status20.default.NOT_FOUND)
}
});
router10.delete(
"/:id",
ensureLoggedIn(),
validateRequest(getByIdParamsSchema(), "delete, /v1/suites/{id}"),
remove10
);
var suites_route_default = router10;
// src/server/routes/v1/settings.route.ts
var import_express11 = __toESM(require("express"));
var import_zod_to_openapi18 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Settings.schema.ts
var import_zod25 = require("zod");
var SettingsGetSchema = import_zod25.z.object({
name: import_zod25.z.string().min(1).openapi({
description: "Name of the setting",
example: "example_setting"
}),
value: import_zod25.z.any().openapi({
description: "Value of the setting",
example: "example_value"
}),
description: import_zod25.z.string().optional().openapi({
description: "Description of the setting",
example: "This is an example setting."
})
});
var SettingsUpdateSchema = import_zod25.z.object({
value: import_zod25.z.any().openapi({
description: "New value for the setting",
example: "new_example_value"
}),
enabled: import_zod25.z.boolean().optional().openapi({
description: "Enable or disable the setting",
example: true
})
});
var SettingsResponseSchema = import_zod25.z.array(
import_zod25.z.object({
_id: commonValidations.id,
name: import_zod25.z.string().min(1).openapi({
description: "Name of the setting",
example: "first_run"
}),
label: import_zod25.z.string().min(1).openapi({
description: "Label of the setting",
example: "First Run"
}),
description: import_zod25.z.string().min(1).openapi({
description: "Description of the setting",
example: "Indicates if the application is running the first time"
}),
type: import_zod25.z.string().min(1).openapi({
description: "Type of the setting",
example: "Boolean"
}),
value: import_zod25.z.any().openapi({
description: "Value of the setting",
example: false
}),
enabled: import_zod25.z.boolean().openapi({
description: "Indicates if the setting is enabled",
example: true
}),
__v: import_zod25.z.number().openapi({
description: "Version key",
example: 0
})
})
);
var SettingsNameParamSchema = import_zod25.z.object({
name: import_zod25.z.string().min(1).openapi({
description: "Name of the setting to update",
example: "example_setting"
})
});
// src/server/routes/v1/settings.route.ts
var registry12 = new import_zod_to_openapi18.OpenAPIRegistry();
var router11 = import_express11.default.Router();
registry12.registerPath({
method: "get",
path: "/v1/settings",
summary: "Get application settings",
tags: ["Settings"],
responses: createApiResponse(SettingsResponseSchema, "Success")
});
router11.get(
"/",
ensureLoggedIn(),
authorization("admin"),
validateRequest(SkipValid, "get, /v1/settings"),
getSettings
);
registry12.registerPath({
method: "patch",
path: "/v1/settings/{name}",
summary: "Update a setting by name",
tags: ["Settings"],
request: { params: SettingsNameParamSchema, body: createRequestOpenApiBodySchema(SettingsUpdateSchema) },
responses: createApiResponse(commonValidations.success, "Success")
});
var validateSchema = createRequestParamsSchema(SettingsNameParamSchema).merge(createRequestBodySchema(SettingsUpdateSchema));
router11.patch(
"/:name",
ensureLoggedIn(),
authorization("admin"),
validateRequest(validateSchema, "patch, /v1/settings/{name}"),
updateSetting
);
var settings_route_default = router11;
// src/server/routes/v1/test_distinct.route.ts
var import_express12 = __toESM(require("express"));
var import_zod_to_openapi19 = require("@asteasolutions/zod-to-openapi");
var registry13 = new import_zod_to_openapi19.OpenAPIRegistry();
var router12 = import_express12.default.Router();
registry13.registerPath({
method: "get",
path: "/v1/test-distinct/{id}",
summary: "[Obsolete use '/v1/test/distict' instead] List of certain unique fields across all tests",
tags: ["Tests"],
request: { params: TestDistinctRequestParamsSchema, query: RequestPaginationSchema },
responses: createPaginatedApiResponse(TestDistinctResponseSchema, "Success")
});
router12.get(
"/:id",
ensureLoggedIn(),
validateRequest(createRequestParamsSchema(TestDistinctRequestParamsSchema), "get, /v1/test-distinct/{id}"),
distinct3
);
var test_distinct_route_default = router12;
// src/server/routes/v1/tasks.route.ts
var import_express13 = __toESM(require("express"));
var import_zod_to_openapi20 = require("@asteasolutions/zod-to-openapi");
var registry14 = new import_zod_to_openapi20.OpenAPIRegistry();
var router13 = import_express13.default.Router();
registry14.registerPath({
method: "get",
path: "/v1/tasks/task_test",
summary: "Test task endpoint",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/task_test",
ensureLoggedIn(),
authorization("admin"),
validateRequest(SkipValid, "get, /v1/tasks/task_test"),
task_test2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/task_handle_old_checks",
summary: "Handle old checks task",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/task_handle_old_checks",
ensureLoggedIn(),
authorization("admin"),
validateRequest(SkipValid, "get, /v1/tasks/task_handle_old_checks"),
task_handle_old_checks2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/task_handle_database_consistency",
summary: "Handle database consistency task",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/task_handle_database_consistency",
ensureLoggedIn(),
authorization("admin"),
validateRequest(SkipValid, "get, /v1/tasks/task_handle_database_consistency"),
task_handle_database_consistency2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/task_remove_old_logs",
summary: "Remove old logs task",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/task_remove_old_logs",
ensureLoggedIn(),
authorization("admin"),
validateRequest(SkipValid, "get, /v1/tasks/task_remove_old_logs"),
task_remove_old_logs2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/loadTestUser",
summary: "Load test user",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/loadTestUser",
ensureLoggedIn(),
validateRequest(SkipValid, "get, /v1/tasks/loadTestUser"),
loadTestUser2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/status",
summary: "Get status task, only for the test cases",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/status",
validateRequest(SkipValid, "get, /v1/tasks/status"),
status2
);
registry14.registerPath({
method: "get",
path: "/v1/tasks/screenshots",
summary: "Get screenshots task",
tags: ["Tasks"],
responses: createApiResponse(SkipValid, "Success")
});
router13.get(
"/screenshots",
validateRequest(SkipValid, "get, /v1/tasks/screenshots"),
screenshots2
);
var tasks_route_default = router13;
// src/server/routes/v1/client.route.ts
var import_express14 = __toESM(require("express"));
var import_zod_to_openapi21 = require("@asteasolutions/zod-to-openapi");
// src/server/schemas/Client.schema.ts
var import_zod26 = require("zod");
var ClientStartSessionSchema = import_zod26.z.object({
name: import_zod26.z.string().min(1).openapi({
description: "Name of the session",
example: "Login test"
}),
app: import_zod26.z.string().min(1).openapi({
description: "Application name",
example: "My project"
}),
tags: import_zod26.z.string().openapi({
description: "Tags associated with the session",
example: '["@smoke", "@PJ-231"]'
}).optional(),
branch: import_zod26.z.string().min(1).openapi({
description: "Branch name",
example: "master"
}),
viewport: import_zod26.z.string().min(1).openapi({
description: "Viewport size",
example: "1366x768"
}),
browser: import_zod26.z.string().min(1).openapi({
description: "Browser name",
example: "chrome"
}),
browserVersion: import_zod26.z.string().min(1).openapi({
description: "Browser version",
example: "125"
}),
browserFullVersion: import_zod26.z.string().min(1).openapi({
description: "Browser full version",
example: "125.12.56.001"
}).optional(),
os: import_zod26.z.string().min(1).openapi({
description: "Operating system",
example: "macOS"
}),
run: import_zod26.z.string().min(1).openapi({
description: "Run name",
example: "Build #123"
}),
runident: import_zod26.z.string().min(1).openapi({
description: "Run identifier",
example: "978ee42c-78f8-40f4-8954-51ebc08d1718"
}),
suite: import_zod26.z.string().min(1).openapi({
description: "Suite name",
example: "Smoke tests"
})
});
var ClientStartSessionResponseSchema = import_zod26.z.object({
name: import_zod26.z.string().openapi({
description: "Name of the session",
example: "Login test"
}),
status: import_zod26.z.string().openapi({
description: "Status of the session",
example: "Running"
}),
browserName: import_zod26.z.string().openapi({
description: "Browser name",
example: "chrome"
}),
browserVersion: import_zod26.z.string().openapi({
description: "Browser version",
example: "125"
}),
branch: import_zod26.z.string().openapi({
description: "Branch name",
example: "master"
}),
tags: import_zod26.z.array(import_zod26.z.string()).openapi({
description: "Tags associated with the session",
example: ["@smoke", "@PJ-231"]
}),
viewport: import_zod26.z.string().openapi({
description: "Viewport size",
example: "1366x768"
}),
os: import_zod26.z.string().openapi({
description: "Operating system",
example: "macOS"
}),
app: import_zod26.z.string().openapi({
description: "Application identifier",
example: "666b3b82db17d34ecdbd06f6"
}),
blinking: import_zod26.z.number().openapi({
description: "Blinking count",
example: 0
}),
updatedDate: import_zod26.z.string().openapi({
description: "Last updated date",
example: "2024-06-13T18:34:28.121Z"
}),
startDate: import_zod26.z.string().openapi({
description: "Start date of the session",
example: "2024-06-13T18:34:28.121Z"
}),
checks: import_zod26.z.array(import_zod26.z.any()).openapi({
description: "Checks associated with the session",
example: []
}),
suite: import_zod26.z.string().openapi({
description: "Suite identifier",
example: "666b3b828833d0cf24a670d7"
}),
run: import_zod26.z.string().openapi({
description: "Run identifier",
example: "666b244a70a6fb0a4368b59e"
}),
_id: commonValidations.id.openapi({
description: "Identifier of the session",
example: "666b3bb49e0c25666d76e0c4"
}),
id: commonValidations.id.openapi({
description: "Identifier of the session",
example: "666b3bb49e0c25666d76e0c4"
})
});
var ClientEndSessionSchema = import_zod26.z.object({
testid: commonValidations.id
});
var ClientCreateCheckSchema = import_zod26.z.object({
testid: commonValidations.id.openapi({
description: "Test identifier",
example: "666b2e1e93ca920ef5985b47"
}),
name: import_zod26.z.string().openapi({
description: "Name of the check",
example: "Login page"
}),
appName: import_zod26.z.string().openapi({
description: "Application name",
example: "My App"
}),
branch: import_zod26.z.string().openapi({
description: "Branch name",
example: "master"
}),
suitename: import_zod26.z.string().openapi({
description: "Suite name",
example: "Smoke tests"
}),
viewport: import_zod26.z.string().openapi({
description: "Viewport size",
example: "1366x768"
}),
browserName: import_zod26.z.string().openapi({
description: "Browser name",
example: "chrome"
}),
browserVersion: import_zod26.z.string().openapi({
description: "Browser version",
example: "125"
}),
browserFullVersion: import_zod26.z.string().openapi({
description: "Full browser version",
example: "125.0.6422.142"
}),
os: import_zod26.z.string().openapi({
description: "Operating system",
example: "macOS"
}),
hashcode: import_zod26.z.string().openapi({
description: "Hash of the snapshot - In the first phase, only the hash is sent, in case the snapshot is already in the Syngrisi database",
example: "ef6ff7c6e6fd536de877c02cf61381e5a1111a24c9d21c1c2a0c0c06fdd2f01271da8880da80a1caa7c123ce3256068c40055753d70bd1dbc558d088f90cd398"
})
});
var SnapshotSchema3 = import_zod26.z.object({
name: import_zod26.z.string().openapi({
description: "Name of the snapshot",
example: "Login page"
}),
filename: import_zod26.z.string().openapi({
description: "Filename of the snapshot",
example: "666b12d859ac872b495af4b0.png"
}),
imghash: import_zod26.z.string().openapi({
description: "Image hash of the snapshot",
example: "ef6ff7c6e6fd536de877c02cf61381e5a1111a24c9d21c1c2a0c0c06fdd2f01271da8880da80a1caa7c123ce3256068c40055753d70bd1dbc558d088f90cd398"
}),
_id: commonValidations.id.openapi({
description: "Identifier of the snapshot",
example: "666b4ebc421977cbf466b478"
}),
createdDate: import_zod26.z.string().openapi({
description: "Creation date",
example: "2024-06-13T19:55:40.068Z"
}),
id: commonValidations.id.openapi({
description: "Identifier of the snapshot",
example: "666b4ebc421977cbf466b478"
})
});
var ClientCreateCheckResponseSchema = import_zod26.z.object({
name: import_zod26.z.string().openapi({
description: "Name of the check",
example: "Login page"
}),
test: commonValidations.id.openapi({
description: "Test identifier",
example: "666b4e74421977cbf466b446"
}),
suite: commonValidations.id.openapi({
description: "Suite identifier",
example: "666b3b828833d0cf24a670d7"
}),
app: commonValidations.id.openapi({
description: "Application identifier",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
branch: import_zod26.z.string().openapi({
description: "Branch name",
example: "master"
}),
baselineId: commonValidations.id.openapi({
description: "Baseline identifier",
example: "666b4ebc421977cbf466b478"
}),
actualSnapshotId: commonValidations.id.openapi({
description: "Actual snapshot identifier",
example: "666b4ebc421977cbf466b478"
}),
updatedDate: import_zod26.z.string().openapi({
description: "Last updated date",
example: "2024-06-13T19:55:40.061Z"
}),
status: import_zod26.z.array(import_zod26.z.string()).openapi({
description: "Status of the check",
example: ["new"]
}),
browserName: import_zod26.z.string().openapi({
description: "Browser name",
example: "chrome"
}),
browserVersion: import_zod26.z.string().openapi({
description: "Browser version",
example: "125"
}),
browserFullVersion: import_zod26.z.string().openapi({
description: "Full browser version",
example: "125.0.6422.142"
}),
viewport: import_zod26.z.string().openapi({
description: "Viewport size",
example: "1366x768"
}),
os: import_zod26.z.string().openapi({
description: "Operating system",
example: "macOS"
}),
result: import_zod26.z.string().openapi({
description: "Result of the check",
example: "{}"
}),
run: commonValidations.id.openapi({
description: "Run identifier",
example: "666b4e74421977cbf466b443"
}),
creatorId: commonValidations.id.openapi({
description: "Creator identifier",
example: "66519e582c2c701cc438ce59"
}),
creatorUsername: import_zod26.z.string().openapi({
description: "Creator username",
example: "Guest"
}),
failReasons: import_zod26.z.array(import_zod26.z.any()).openapi({
description: "Reasons for failure",
example: []
}),
_id: commonValidations.id.openapi({
description: "Identifier of the check",
example: "666b4ebc421977cbf466b47c"
}),
createdDate: import_zod26.z.string().openapi({
description: "Creation date",
example: "2024-06-13T19:55:40.082Z"
}),
currentSnapshot: SnapshotSchema3,
expectedSnapshot: SnapshotSchema3,
lastSuccess: commonValidations.id.openapi({
description: "Identifier of the last successful check",
example: "666b4ebc421977cbf466b47c"
})
});
var ClientGetIdentSchema = import_zod26.z.array(import_zod26.z.string()).openapi({
description: "Set of fields that identify checks and baselines",
example: ["name", "viewport", "browserName", "os", "app", "branch"]
});
var ClientGetBaselinesSchema = import_zod26.z.object({
baselines: import_zod26.z.array(import_zod26.z.object({
id: commonValidations.id,
name: import_zod26.z.string().openapi({
description: "Name of the baseline",
example: "A-A-A"
}),
app: commonValidations.id.openapi({
description: "Application identifier",
example: "6651dd45b9c3e1e0b8c1ce26"
}),
branch: import_zod26.z.string().openapi({
description: "Branch name",
example: "master"
}),
browserName: import_zod26.z.string().openapi({
description: "Browser name",
example: "chrome"
}),
viewport: import_zod26.z.string().openapi({
description: "Viewport size",
example: "1366x768"
}),
os: import_zod26.z.string().openapi({
description: "Operating system",
example: "macOS"
}),
createdDate: import_zod26.z.string().openapi({
description: "Creation date",
example: "2024-06-13T15:59:44.479Z"
}),
lastMarkedDate: import_zod26.z.string().openapi({
description: "Last marked date",
example: "2024-06-13T15:59:44.381Z"
}),
markedAs: import_zod26.z.string().openapi({
description: "Marked status",
example: "accepted"
}),
markedById: commonValidations.id.openapi({
description: "Identifier of the user who marked the baseline",
example: "66519e1682764a892a1a0031"
}),
markedByUsername: import_zod26.z.string().openapi({
description: "Username of the user who marked the baseline",
example: "Administrator"
}),
snapshootId: commonValidations.id.openapi({
description: "Snapshot identifier",
example: "666b12d859ac872b495af4b0"
}),
_id: commonValidations.id.openapi({
description: "Identifier of the baseline",
example: "666b177059ac872b495af63d"
})
}))
});
var ClientGetSnapshotsSchema = import_zod26.z.object({
snapshots: import_zod26.z.array(import_zod26.z.object({
id: commonValidations.id,
name: import_zod26.z.string()
}))
});
// src/server/routes/v1/client.route.ts
var registry15 = new import_zod_to_openapi21.OpenAPIRegistry();
var router14 = import_express14.default.Router();
registry15.registerPath({
method: "post",
path: "/v1/client/startSession",
summary: "Start a client session",
tags: ["Client"],
request: { body: createRequestOpenApiBodySchema(ClientStartSessionSchema) },
responses: createApiResponse(ClientStartSessionResponseSchema, "Success")
});
router14.post(
"/startSession",
ensureApiKey(),
validateRequest(createRequestBodySchema(ClientStartSessionSchema), "/v1/client/startSession"),
startSession2
);
registry15.registerPath({
method: "post",
path: "/v1/client/stopSession/{testid}",
summary: "Stop a client session by test ID",
tags: ["Client"],
request: commonValidations.paramsTestId,
responses: createApiResponse(ClientStartSessionResponseSchema, "Success")
});
router14.post(
"/stopSession/:testid",
ensureApiKey(),
// validateRequest(SkipValid, '/v1/client/stopSession/{testid}'),
validateRequest(getByIdParamsSchema("testid"), "/v1/client/stopSession/{testid}"),
endSession2
);
registry15.registerPath({
method: "post",
path: "/v1/client/createCheck",
summary: "Create a client check",
tags: ["Client"],
request: { body: createRequestOpenApiBodySchema(ClientCreateCheckSchema) },
responses: createApiResponse(ClientCreateCheckResponseSchema, "Success")
});
router14.post(
"/createCheck",
ensureApiKey(),
validateRequest(createRequestBodySchema(ClientCreateCheckSchema), "/v1/client/createCheck"),
createCheck2
);
registry15.registerPath({
method: "get",
path: "/v1/client/getIdent",
summary: "Set of fields that identify checks and baselines",
tags: ["Client"],
responses: createApiResponse(ClientGetIdentSchema, "Success")
});
router14.get(
"/getIdent",
ensureApiKey(),
validateRequest(SkipValid, "get, /v1/client/getIdent"),
getIdent2
);
var ExtRequestBaselineSchema = RequestPaginationSchema.extend(
{
filter: IdentJSONStringSchema
}
);
registry15.registerPath({
method: "get",
path: "/v1/client/baselines",
summary: "Get client baselines",
tags: ["Client"],
// request: { query: RequestPaginationSchema },
request: { query: ExtRequestBaselineSchema },
responses: createPaginatedApiResponse(ClientGetBaselinesSchema, "Success")
});
router14.get(
"/baselines",
ensureApiKey(),
validateRequest(createRequestQuerySchema(ExtRequestBaselineSchema), "get, /v1/client/baselines"),
getBaselines2
);
registry15.registerPath({
method: "get",
path: "/v1/client/snapshots",
summary: "Get client snapshots",
tags: ["Client"],
request: { query: RequestPaginationSchema },
responses: createPaginatedApiResponse(ClientGetSnapshotsSchema, "Success")
});
router14.get(
"/snapshots",
ensureApiKey(),
validateRequest(createRequestQuerySchema(RequestPaginationSchema), "get, /v1/client/snapshots"),
getSnapshots
);
var client_route_default = router14;
// src/server/routes/v1/index.route.ts
var router15 = import_express15.default.Router();
var defaultRoutes = [
{
path: "/auth",
route: auth_route_default
},
{
path: "/client",
route: client_route_default
},
{
path: "/tasks",
route: tasks_route_default
},
{
path: "/users",
route: users_route_default
},
{
path: "/app",
route: app_route_default
},
{
path: "/tests",
route: tests_route_default
},
{
path: "/logs",
route: logs_route_default
},
{
path: "/runs",
route: runs_route_default
},
{
path: "/snapshots",
route: snapshots_route_default
},
{
path: "/checks",
route: checks_route_default
},
{
path: "/baselines",
route: baselines_route_default
},
{
path: "/suites",
route: suites_route_default
},
{
path: "/settings",
route: settings_route_default
},
{
path: "/test-distinct",
route: test_distinct_route_default
}
];
defaultRoutes.forEach((route) => {
router15.use(route.path, route.route);
});
var index_route_default = router15;
//# sourceMappingURL=index.route.js.map