166 lines
6.3 KiB
JavaScript

import dotenv from 'dotenv';
import log4js from 'log4js';
import mongoose from 'mongoose';
import { jobModel } from '../../schemas/production/job.schema.js';
import { subJobModel } from '../../schemas/production/subjob.schema.js';
import { printerModel } from '../../schemas/production/printer.schema.js';
import { filamentModel } from '../../schemas/management/filament.schema.js';
import { gcodeFileModel } from '../../schemas/production/gcodefile.schema.js';
import { partModel } from '../../schemas/management/part.schema.js';
import { productModel } from '../../schemas/management/product.schema.js';
import { vendorModel } from '../../schemas/management/vendor.schema.js';
import { filamentStockModel } from '../../schemas/inventory/filamentstock.schema.js';
import { stockEventModel } from '../../schemas/inventory/stockevent.schema.js';
import { stockAuditModel } from '../../schemas/inventory/stockaudit.schema.js';
import { partStockModel } from '../../schemas/inventory/partstock.schema.js';
import { auditLogModel } from '../../schemas/management/auditlog.schema.js';
import { userModel } from '../../schemas/management/user.schema.js';
import { noteTypeModel } from '../../schemas/management/notetype.schema.js';
import { noteModel } from '../../schemas/misc/note.schema.js';
dotenv.config();
const logger = log4js.getLogger('Jobs');
logger.level = process.env.LOG_LEVEL;
// Map prefixes to models and id fields
const PREFIX_MODEL_MAP = {
PRN: { model: printerModel, idField: '_id', type: 'printer' },
FIL: { model: filamentModel, idField: '_id', type: 'filament' },
SPL: { model: null, idField: '_id', type: 'spool' }, // No spool model found
GCF: { model: gcodeFileModel, idField: '_id', type: 'gcodefile' },
JOB: { model: jobModel, idField: '_id', type: 'job' },
PRT: { model: partModel, idField: '_id', type: 'part' },
PRD: { model: productModel, idField: '_id', type: 'product' },
VEN: { model: vendorModel, idField: '_id', type: 'vendor' },
SJB: { model: subJobModel, idField: '_id', type: 'subjob' },
FLS: { model: filamentStockModel, idField: '_id', type: 'filamentstock' },
SEV: { model: stockEventModel, idField: '_id', type: 'stockevent' },
SAU: { model: stockAuditModel, idField: '_id', type: 'stockaudit' },
PTS: { model: partStockModel, idField: '_id', type: 'partstock' },
PDS: { model: null, idField: '_id', type: 'productstock' }, // No productStockModel found
ADL: { model: auditLogModel, idField: '_id', type: 'auditlog' },
USR: { model: userModel, idField: '_id', type: 'user' },
NTY: { model: noteTypeModel, idField: '_id', type: 'notetype' },
NTE: { model: noteModel, idField: '_id', type: 'note' },
};
// Helper function to build search filter from query parameters
const buildSearchFilter = (params) => {
const filter = {};
for (const [key, value] of Object.entries(params)) {
// Skip pagination and limit parameters as they're not search filters
if (key === 'limit' || key === 'page') continue;
// Handle different field types
if (key === 'name') {
filter.name = { $regex: value, $options: 'i' }; // Case-insensitive search
} else if (key === 'id' || key === '_id') {
if (mongoose.Types.ObjectId.isValid(value)) {
filter._id = value;
}
} else if (key === 'tags') {
filter.tags = { $in: [new RegExp(value, 'i')] };
} else if (key === 'state') {
filter['state.type'] = value;
} else if (key.includes('.')) {
// Handle nested fields like 'state.type', 'address.city', etc.
filter[key] = { $regex: value, $options: 'i' };
} else {
// For all other fields, do a case-insensitive search
filter[key] = { $regex: value, $options: 'i' };
}
}
return filter;
};
const trimSpotlightObject = (object) => {
return {
_id: object._id,
name: object.name || undefined,
state: object.state && object?.state.type ? { type: object.state.type } : undefined,
tags: object.tags || undefined,
email: object.email || undefined,
color: object.color || undefined,
updatedAt: object.updatedAt || undefined,
};
};
export const getSpotlightRouteHandler = async (req, res) => {
try {
const query = req.params.query;
const queryParams = req.query;
if (query.length < 3) {
res.status(200).send([]);
return;
}
const prefix = query.substring(0, 3);
const delimiter = query.substring(3, 4);
const suffix = query.substring(4);
if (delimiter == ':') {
const prefixEntry = PREFIX_MODEL_MAP[prefix];
if (!prefixEntry || !prefixEntry.model) {
res.status(400).send({ error: 'Invalid or unsupported prefix' });
return;
}
const { model, idField } = prefixEntry;
// Validate ObjectId if the idField is '_id'
if (idField === '_id' && !mongoose.Types.ObjectId.isValid(suffix)) {
res.status(404).send({ error: `${prefix} not found` });
return;
}
// Find the object by the correct field
const queryObj = {};
queryObj[idField] = suffix.toLowerCase();
let doc = await model.findOne(queryObj).lean();
if (!doc) {
res.status(404).send({ error: `${prefix} not found` });
return;
}
// Build the response with only the required fields
const response = trimSpotlightObject(doc);
res.status(200).send(response);
return;
}
console.log(queryParams);
if (Object.keys(queryParams).length > 0) {
const prefixEntry = PREFIX_MODEL_MAP[prefix];
console.log(prefixEntry);
if (!prefixEntry || !prefixEntry.model) {
res.status(400).send({ error: 'Invalid or unsupported prefix' });
return;
}
const { model } = prefixEntry;
// Use req.query for search parameters
if (Object.keys(queryParams).length === 0) {
res.status(400).send({ error: 'No search parameters provided' });
return;
}
// Build search filter
const searchFilter = buildSearchFilter(queryParams);
// Perform search with limit
const limit = parseInt(req.query.limit) || 10;
const docs = await model.find(searchFilter).limit(limit).sort({ updatedAt: -1 }).lean();
// Format response
const response = docs.map((doc) => trimSpotlightObject(doc));
res.status(200).send(response);
return;
}
} catch (error) {
logger.error('Error in spotlight lookup:', error);
res.status(500).send({ error: error });
}
};