Add inventory and management schemas for filament, part, and stock management
- Introduced new schemas for managing inventory, including filamentStock, partStock, stockAudit, stockEvent, and their respective models. - Added management schemas for user, vendor, material, and various document types to enhance data structure and organization. - Implemented necessary fields and relationships to support inventory tracking and management functionalities.
This commit is contained in:
parent
5584e61583
commit
ce15d3dbfc
490
src/database/database.js
Normal file
490
src/database/database.js
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import NodeCache from 'node-cache';
|
||||||
|
import {
|
||||||
|
deleteAuditLog,
|
||||||
|
expandObjectIds,
|
||||||
|
editAuditLog,
|
||||||
|
distributeUpdate,
|
||||||
|
newAuditLog,
|
||||||
|
distributeNew
|
||||||
|
} from './utils.js';
|
||||||
|
import log4js from 'log4js';
|
||||||
|
import { loadConfig } from '../config.js';
|
||||||
|
import { userModel } from './schemas/management/user.schema.js';
|
||||||
|
|
||||||
|
const config = loadConfig();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger('Database');
|
||||||
|
const cacheLogger = log4js.getLogger('Local Cache');
|
||||||
|
logger.level = config.server.logLevel;
|
||||||
|
cacheLogger.level = config.server.logLevel;
|
||||||
|
|
||||||
|
const modelCaches = new Map();
|
||||||
|
const listCache = new NodeCache({
|
||||||
|
stdTTL: 30, // 30 sec expiration
|
||||||
|
checkperiod: 600, // 30 sec periodic cleanup
|
||||||
|
useClones: false // Don't clone objects for better performance
|
||||||
|
});
|
||||||
|
|
||||||
|
function getModelCache(model) {
|
||||||
|
const modelName = model.modelName;
|
||||||
|
const modelCache = modelCaches.get(modelName);
|
||||||
|
if (modelCache == undefined) {
|
||||||
|
logger.trace('Creating new model cache...');
|
||||||
|
const newModelCache = new NodeCache({
|
||||||
|
stdTTL: 30, // 30 sec expiration
|
||||||
|
checkperiod: 30, // 30 sec periodic cleanup
|
||||||
|
useClones: false // Don't clone objects for better performance
|
||||||
|
});
|
||||||
|
modelCaches.set(modelName, newModelCache);
|
||||||
|
return newModelCache;
|
||||||
|
}
|
||||||
|
logger.trace('Getting model cache...');
|
||||||
|
return modelCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const retrieveObjectCache = ({ model, id }) => {
|
||||||
|
cacheLogger.trace('Retrieving:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
const modelCache = getModelCache(model);
|
||||||
|
|
||||||
|
const cachedObject = modelCache.get(id);
|
||||||
|
|
||||||
|
if (cachedObject == undefined) {
|
||||||
|
cacheLogger.trace('Miss:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheLogger.trace('Hit:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
|
||||||
|
return cachedObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const retrieveObjectsCache = ({ model }) => {
|
||||||
|
cacheLogger.trace('Retrieving:', {
|
||||||
|
model: model.modelName
|
||||||
|
});
|
||||||
|
const modelCache = getModelCache(model);
|
||||||
|
|
||||||
|
const modelCacheKeys = modelCache.keys();
|
||||||
|
|
||||||
|
const cachedList = listCache.get(model.modelName);
|
||||||
|
|
||||||
|
if (cachedList == true) {
|
||||||
|
const cachedObjects = modelCacheKeys.map(key => modelCache.get(key));
|
||||||
|
|
||||||
|
cacheLogger.trace('Hit:', {
|
||||||
|
model: model.modelName,
|
||||||
|
length: cachedObjects.length
|
||||||
|
});
|
||||||
|
|
||||||
|
return cachedObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheLogger.trace('Miss:', {
|
||||||
|
model: model.modelName
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateObjectCache = ({ model, id, object }) => {
|
||||||
|
cacheLogger.trace('Updating:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
const modelCache = getModelCache(model);
|
||||||
|
const cachedObject = modelCache.get(id) || {};
|
||||||
|
const mergedObject = _.merge(cachedObject, object);
|
||||||
|
|
||||||
|
modelCache.set(id, mergedObject);
|
||||||
|
|
||||||
|
cacheLogger.trace('Updated:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
|
||||||
|
return mergedObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteObjectCache = ({ model, id }) => {
|
||||||
|
cacheLogger.trace('Deleting:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
|
||||||
|
modelCache.del(id);
|
||||||
|
|
||||||
|
cacheLogger.trace('Deleted:', {
|
||||||
|
model: model.modelName,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
|
||||||
|
return mergedObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateObjectsCache = ({ model, objects }) => {
|
||||||
|
cacheLogger.trace('Updating:', {
|
||||||
|
model: model.modelName,
|
||||||
|
length: objects.length
|
||||||
|
});
|
||||||
|
const modelCache = getModelCache(model);
|
||||||
|
|
||||||
|
objects.forEach(object => {
|
||||||
|
const cachedObject = modelCache.get(object._id) || {};
|
||||||
|
|
||||||
|
const mergedObject = _.merge(cachedObject, object);
|
||||||
|
|
||||||
|
modelCache.set(object._id, mergedObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
listCache.set(model.modelName, true);
|
||||||
|
|
||||||
|
cacheLogger.trace('Updated:', {
|
||||||
|
model: model.modelName,
|
||||||
|
length: objects.length
|
||||||
|
});
|
||||||
|
|
||||||
|
return mergedObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to list objects with aggregation, filtering, search, sorting, and pagination
|
||||||
|
export const listObjects = async ({
|
||||||
|
model,
|
||||||
|
populate = [],
|
||||||
|
filter = {},
|
||||||
|
sort = '',
|
||||||
|
order = 'ascend',
|
||||||
|
project, // optional: override default projection
|
||||||
|
cached = false
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
logger.trace('Listing objects:', {
|
||||||
|
model,
|
||||||
|
populate,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
filter,
|
||||||
|
sort,
|
||||||
|
order,
|
||||||
|
project,
|
||||||
|
cache
|
||||||
|
});
|
||||||
|
|
||||||
|
var cacheKey = undefined;
|
||||||
|
var modelCache = getModelCache(model);
|
||||||
|
|
||||||
|
if (cached == true) {
|
||||||
|
const objectsCache = retrieveObjectsCache({ model });
|
||||||
|
if (objectsCache != undefined) {
|
||||||
|
return objectsCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix: descend should be -1, ascend should be 1
|
||||||
|
const sortOrder = order === 'descend' ? -1 : 1;
|
||||||
|
|
||||||
|
if (!sort || sort === '') {
|
||||||
|
sort = 'createdAt';
|
||||||
|
}
|
||||||
|
// Translate parent._id to parent for Mongoose
|
||||||
|
if (filter['parent._id']) {
|
||||||
|
filter.parent = filter['parent._id'];
|
||||||
|
delete filter['parent._id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate owner._id to owner for Mongoose
|
||||||
|
if (filter['owner._id']) {
|
||||||
|
filter.owner = filter['owner._id'];
|
||||||
|
delete filter['owner._id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use find with population and filter
|
||||||
|
let query = model.find(filter).sort({ [sort]: sortOrder });
|
||||||
|
|
||||||
|
// Handle populate (array or single value)
|
||||||
|
if (populate) {
|
||||||
|
if (Array.isArray(populate)) {
|
||||||
|
for (const pop of populate) {
|
||||||
|
query = query.populate(pop);
|
||||||
|
}
|
||||||
|
} else if (typeof populate === 'string' || typeof populate === 'object') {
|
||||||
|
query = query.populate(populate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle select (projection)
|
||||||
|
if (project) {
|
||||||
|
query = query.select(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query.lean();
|
||||||
|
|
||||||
|
const queryResult = await query;
|
||||||
|
|
||||||
|
const finalResult = expandObjectIds(queryResult);
|
||||||
|
|
||||||
|
updateObjectsCache({ model, objects });
|
||||||
|
|
||||||
|
logger.trace('Retreived from database:', {
|
||||||
|
model,
|
||||||
|
populate,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
filter,
|
||||||
|
sort,
|
||||||
|
order,
|
||||||
|
project,
|
||||||
|
cache
|
||||||
|
});
|
||||||
|
return finalResult;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Object list error:', error);
|
||||||
|
return { error: error, code: 500 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to get a single object by ID
|
||||||
|
export const getObject = async ({ model, id, populate, cached = false }) => {
|
||||||
|
try {
|
||||||
|
logger.trace('Getting object:', {
|
||||||
|
model,
|
||||||
|
id,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cached == true) {
|
||||||
|
const cachedObject = retrieveObjectCache({ model, id });
|
||||||
|
if (cachedObject != undefined) {
|
||||||
|
return cachedObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = model.findById(id).lean();
|
||||||
|
|
||||||
|
// Handle populate (array or single value)
|
||||||
|
if (populate) {
|
||||||
|
if (Array.isArray(populate)) {
|
||||||
|
for (const pop of populate) {
|
||||||
|
query = query.populate(pop);
|
||||||
|
}
|
||||||
|
} else if (typeof populate === 'string' || typeof populate === 'object') {
|
||||||
|
query = query.populate(populate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const finalResult = await query;
|
||||||
|
|
||||||
|
if (!finalResult) {
|
||||||
|
logger.warn('Object not found in database:', {
|
||||||
|
model,
|
||||||
|
id,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace('Retreived object from database:', {
|
||||||
|
model,
|
||||||
|
id,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
|
||||||
|
updateObjectCache({
|
||||||
|
model: model,
|
||||||
|
id: finalResult._id.toString(),
|
||||||
|
object: finalResult
|
||||||
|
});
|
||||||
|
|
||||||
|
return finalResult;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('An error retreiving object:', error.message);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to get a single object by ID
|
||||||
|
export const getObjectByFilter = async ({ model, filter, populate }) => {
|
||||||
|
try {
|
||||||
|
logger.trace('Getting object:', {
|
||||||
|
model,
|
||||||
|
filter,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
|
||||||
|
let query = model.findOne(filter).lean();
|
||||||
|
|
||||||
|
// Handle populate (array or single value)
|
||||||
|
if (populate) {
|
||||||
|
if (Array.isArray(populate)) {
|
||||||
|
for (const pop of populate) {
|
||||||
|
query = query.populate(pop);
|
||||||
|
}
|
||||||
|
} else if (typeof populate === 'string' || typeof populate === 'object') {
|
||||||
|
query = query.populate(populate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const finalResult = await query;
|
||||||
|
|
||||||
|
if (!finalResult) {
|
||||||
|
logger.warn('Object not found in database:', {
|
||||||
|
model,
|
||||||
|
filter,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace('Retreived object from database:', {
|
||||||
|
model,
|
||||||
|
filter,
|
||||||
|
populate
|
||||||
|
});
|
||||||
|
|
||||||
|
updateObjectCache({
|
||||||
|
model: model,
|
||||||
|
id: finalResult._id.toString(),
|
||||||
|
object: finalResult
|
||||||
|
});
|
||||||
|
|
||||||
|
return finalResult;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('An error retreiving object:', error.message);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to edit an object by ID, with audit logging and distribution
|
||||||
|
export const editObject = async ({
|
||||||
|
model,
|
||||||
|
id,
|
||||||
|
updateData,
|
||||||
|
owner = undefined,
|
||||||
|
ownerType = undefined,
|
||||||
|
populate
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
// Determine parentType from model name
|
||||||
|
const parentType = model.modelName ? model.modelName : 'unknown';
|
||||||
|
// Fetch the and update object
|
||||||
|
var query = model.findByIdAndUpdate(id, updateData).lean();
|
||||||
|
|
||||||
|
if (populate) {
|
||||||
|
if (Array.isArray(populate)) {
|
||||||
|
for (const pop of populate) {
|
||||||
|
query = query.populate(pop);
|
||||||
|
}
|
||||||
|
} else if (typeof populate === 'string' || typeof populate === 'object') {
|
||||||
|
query = query.populate(populate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousObject = await query;
|
||||||
|
|
||||||
|
if (!previousObject) {
|
||||||
|
return { error: `${parentType} not found.`, code: 404 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousExpandedObject = expandObjectIds(previousObject);
|
||||||
|
|
||||||
|
if (owner != undefined && ownerType != undefined) {
|
||||||
|
// Audit log before update
|
||||||
|
await editAuditLog(
|
||||||
|
previousExpandedObject,
|
||||||
|
{ ...previousExpandedObject, ...updateData },
|
||||||
|
id,
|
||||||
|
parentType,
|
||||||
|
owner,
|
||||||
|
ownerType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distribute update
|
||||||
|
await distributeUpdate(updateData, id, parentType);
|
||||||
|
|
||||||
|
updateObjectCache({
|
||||||
|
model: model,
|
||||||
|
id: id.toString(),
|
||||||
|
object: { ...previousExpandedObject, ...updateData }
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...previousExpandedObject, ...updateData };
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('editObject error:', error);
|
||||||
|
return { error: error.message, code: 500 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to create a new object
|
||||||
|
export const newObject = async ({
|
||||||
|
model,
|
||||||
|
newData,
|
||||||
|
owner = null,
|
||||||
|
ownerType = undefined
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
const parentType = model.modelName ? model.modelName : 'unknown';
|
||||||
|
|
||||||
|
const result = await model.create(newData);
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
return { error: 'No object created.', code: 500 };
|
||||||
|
}
|
||||||
|
const created = result;
|
||||||
|
|
||||||
|
if (owner != undefined && ownerType != undefined) {
|
||||||
|
await newAuditLog(newData, created._id, parentType, owner, ownerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
await distributeNew(created._id, parentType);
|
||||||
|
|
||||||
|
updateObjectCache({
|
||||||
|
model: model,
|
||||||
|
id: created._id.toString(),
|
||||||
|
object: { _id: created._id, ...newData }
|
||||||
|
});
|
||||||
|
|
||||||
|
return created;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('newObject error:', error);
|
||||||
|
return { error: error.message, code: 500 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reusable function to delete an object by ID, with audit logging and distribution
|
||||||
|
export const deleteObject = async ({
|
||||||
|
model,
|
||||||
|
id,
|
||||||
|
owner = null,
|
||||||
|
ownerType = undefined
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
const parentType = model.modelName ? model.modelName : 'unknown';
|
||||||
|
// Delete the object
|
||||||
|
const result = await model.findByIdAndDelete(id);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return { error: `${parentType} not found.`, code: 404 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner != undefined && ownerType != undefined) {
|
||||||
|
// Audit log the deletion
|
||||||
|
await deleteAuditLog(result, id, parentType, owner, ownerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteObjectCache({ model: model, id: id.toString() });
|
||||||
|
|
||||||
|
// Distribute the deletion event
|
||||||
|
await distributeUpdate({ deleted: true }, id, parentType);
|
||||||
|
|
||||||
|
return { deleted: true, id: id.toString() };
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('deleteObject error:', error);
|
||||||
|
return { error: error.message, code: 500 };
|
||||||
|
}
|
||||||
|
};
|
||||||
33
src/database/schemas/inventory/filamentstock.schema.js
Normal file
33
src/database/schemas/inventory/filamentstock.schema.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main filamentStock schema
|
||||||
|
const filamentStockSchema = new Schema(
|
||||||
|
{
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true },
|
||||||
|
percent: { type: String, required: true },
|
||||||
|
},
|
||||||
|
startingWeight: {
|
||||||
|
net: { type: Number, required: true },
|
||||||
|
gross: { type: Number, required: true },
|
||||||
|
},
|
||||||
|
currentWeight: {
|
||||||
|
net: { type: Number, required: true },
|
||||||
|
gross: { type: Number, required: true },
|
||||||
|
},
|
||||||
|
filament: { type: mongoose.Schema.Types.ObjectId, ref: 'filament' },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
filamentStockSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
filamentStockSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const filamentStockModel = mongoose.model('filamentStock', filamentStockSchema);
|
||||||
25
src/database/schemas/inventory/partstock.schema.js
Normal file
25
src/database/schemas/inventory/partstock.schema.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main partStock schema
|
||||||
|
const partStockSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: { type: String, required: true },
|
||||||
|
fileName: { type: String, required: false },
|
||||||
|
part: { type: mongoose.Schema.Types.ObjectId, ref: 'part' },
|
||||||
|
startingQuantity: { type: Number, required: true },
|
||||||
|
currentQuantity: { type: Number, required: true },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
partStockSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
partStockSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const partStockModel = mongoose.model('partStock', partStockSchema);
|
||||||
38
src/database/schemas/inventory/stockaudit.schema.js
Normal file
38
src/database/schemas/inventory/stockaudit.schema.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const stockAuditItemSchema = new Schema({
|
||||||
|
type: { type: String, enum: ['filament', 'part'], required: true },
|
||||||
|
stock: { type: Schema.Types.ObjectId, required: true },
|
||||||
|
expectedQuantity: { type: Number, required: true },
|
||||||
|
actualQuantity: { type: Number, required: true },
|
||||||
|
notes: { type: String },
|
||||||
|
});
|
||||||
|
|
||||||
|
const stockAuditSchema = new Schema(
|
||||||
|
{
|
||||||
|
type: { type: String, required: true },
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
enum: ['pending', 'in_progress', 'completed', 'cancelled'],
|
||||||
|
default: 'pending',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
notes: { type: String },
|
||||||
|
items: [stockAuditItemSchema],
|
||||||
|
createdBy: { type: Schema.Types.ObjectId, ref: 'user', required: true },
|
||||||
|
completedAt: { type: Date },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
stockAuditSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
stockAuditSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const stockAuditModel = mongoose.model('stockAudit', stockAuditSchema);
|
||||||
43
src/database/schemas/inventory/stockevent.schema.js
Normal file
43
src/database/schemas/inventory/stockevent.schema.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const stockEventSchema = new Schema(
|
||||||
|
{
|
||||||
|
value: { type: Number, required: true },
|
||||||
|
current: { type: Number, required: true },
|
||||||
|
unit: { type: String, required: true },
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
refPath: 'parentType',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
parentType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: ['filamentStock', 'partStock', 'productStock'], // Add other models as needed
|
||||||
|
},
|
||||||
|
owner: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
refPath: 'ownerType',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
ownerType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: ['user', 'subJob', 'stockAudit'],
|
||||||
|
},
|
||||||
|
timestamp: { type: Date, default: Date.now },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
stockEventSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
stockEventSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const stockEventModel = mongoose.model('stockEvent', stockEventSchema);
|
||||||
64
src/database/schemas/management/auditlog.schema.js
Normal file
64
src/database/schemas/management/auditlog.schema.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const auditLogSchema = new Schema(
|
||||||
|
{
|
||||||
|
changes: {
|
||||||
|
old: { type: Object, required: true },
|
||||||
|
new: { type: Object, required: true }
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
refPath: 'parentType',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
parentType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: [
|
||||||
|
'printer',
|
||||||
|
'job',
|
||||||
|
'subJob',
|
||||||
|
'filamentStock',
|
||||||
|
'stockEvent',
|
||||||
|
'vendor',
|
||||||
|
'part',
|
||||||
|
'product',
|
||||||
|
'material',
|
||||||
|
'filament',
|
||||||
|
'gcodeFile',
|
||||||
|
'noteType',
|
||||||
|
'note',
|
||||||
|
'user',
|
||||||
|
'host'
|
||||||
|
] // Add other models as needed
|
||||||
|
},
|
||||||
|
owner: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
refPath: 'ownerType',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
ownerType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: ['user', 'printer', 'host']
|
||||||
|
},
|
||||||
|
operation: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: ['edit', 'new', 'delete']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
auditLogSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
auditLogSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const auditLogModel = mongoose.model('auditLog', auditLogSchema);
|
||||||
33
src/database/schemas/management/documentsize.schema.js
Normal file
33
src/database/schemas/management/documentsize.schema.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const documentSizeSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
documentSizeSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
documentSizeSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const documentSizeModel = mongoose.model('documentSize', documentSizeSchema);
|
||||||
61
src/database/schemas/management/documenttemplate.schema.js
Normal file
61
src/database/schemas/management/documenttemplate.schema.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const documentTemplateSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
objectType: { type: String, required: false },
|
||||||
|
tags: [{ type: String }],
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'documentTemplate',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
documentSize: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'documentSize',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
documentPrinters: [
|
||||||
|
{
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'documentPrinter',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '<Container></Container>',
|
||||||
|
},
|
||||||
|
testObject: {
|
||||||
|
type: Schema.Types.Mixed,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
documentTemplateSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
documentTemplateSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const documentTemplateModel = mongoose.model('documentTemplate', documentTemplateSchema);
|
||||||
26
src/database/schemas/management/filament.schema.js
Normal file
26
src/database/schemas/management/filament.schema.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const filamentSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
barcode: { required: false, type: String },
|
||||||
|
url: { required: false, type: String },
|
||||||
|
image: { required: false, type: Buffer },
|
||||||
|
color: { required: true, type: String },
|
||||||
|
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
||||||
|
type: { required: true, type: String },
|
||||||
|
cost: { required: true, type: Number },
|
||||||
|
diameter: { required: true, type: Number },
|
||||||
|
density: { required: true, type: Number },
|
||||||
|
createdAt: { required: true, type: Date },
|
||||||
|
updatedAt: { required: true, type: Date },
|
||||||
|
emptySpoolWeight: { required: true, type: Number },
|
||||||
|
});
|
||||||
|
|
||||||
|
filamentSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
filamentSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const filamentModel = mongoose.model('filament', filamentSchema);
|
||||||
66
src/database/schemas/management/host.schema.js
Normal file
66
src/database/schemas/management/host.schema.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
// Define the device schema
|
||||||
|
const deviceInfoSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
os: {
|
||||||
|
platform: { type: String },
|
||||||
|
type: { type: String },
|
||||||
|
release: { type: String },
|
||||||
|
arch: { type: String },
|
||||||
|
hostname: { type: String },
|
||||||
|
uptime: { type: Number }
|
||||||
|
},
|
||||||
|
cpu: {
|
||||||
|
cores: { type: Number },
|
||||||
|
model: { type: String },
|
||||||
|
speedMHz: { type: Number }
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
totalGB: { type: String }, // stored as string from .toFixed(2), could also use Number
|
||||||
|
freeGB: { type: String }
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
type: mongoose.Schema.Types.Mixed // since it's an object with dynamic interface names
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
uid: { type: Number },
|
||||||
|
gid: { type: Number },
|
||||||
|
username: { type: String },
|
||||||
|
homedir: { type: String },
|
||||||
|
shell: { type: String }
|
||||||
|
},
|
||||||
|
process: {
|
||||||
|
nodeVersion: { type: String },
|
||||||
|
pid: { type: Number },
|
||||||
|
cwd: { type: String },
|
||||||
|
execPath: { type: String }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const hostSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
tags: [{ required: false, type: String }],
|
||||||
|
online: { required: true, type: Boolean, default: false },
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true, default: 'offline' },
|
||||||
|
message: { type: String, required: false },
|
||||||
|
percent: { type: Number, required: false }
|
||||||
|
},
|
||||||
|
active: { required: true, type: Boolean, default: true },
|
||||||
|
connectedAt: { required: false, type: Date },
|
||||||
|
authCode: { required: false, type: String },
|
||||||
|
otp: { required: false, type: String },
|
||||||
|
otpExpiresAt: { required: false, type: Date },
|
||||||
|
deviceInfo: deviceInfoSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
hostSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
hostSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const hostModel = mongoose.model('host', hostSchema);
|
||||||
16
src/database/schemas/management/material.schema.js
Normal file
16
src/database/schemas/management/material.schema.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
const materialSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
url: { required: false, type: String },
|
||||||
|
image: { required: false, type: Buffer },
|
||||||
|
tags: [{ type: String }],
|
||||||
|
});
|
||||||
|
|
||||||
|
materialSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
materialSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const materialModel = mongoose.model('material', materialSchema);
|
||||||
32
src/database/schemas/management/notetype.schema.js
Normal file
32
src/database/schemas/management/notetype.schema.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const noteTypeSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
noteTypeSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
noteTypeSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const noteTypeModel = mongoose.model('noteType', noteTypeSchema);
|
||||||
27
src/database/schemas/management/part.schema.js
Normal file
27
src/database/schemas/management/part.schema.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main part schema
|
||||||
|
const partSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: { type: String, required: true },
|
||||||
|
fileName: { type: String, required: false },
|
||||||
|
product: { type: mongoose.Schema.Types.ObjectId, ref: 'product' },
|
||||||
|
globalPricing: { type: Boolean, default: true },
|
||||||
|
priceMode: { type: String, default: 'margin' },
|
||||||
|
amount: { type: Number, required: false },
|
||||||
|
margin: { type: Number, required: false },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
partSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
partSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const partModel = mongoose.model('part', partSchema);
|
||||||
26
src/database/schemas/management/product.schema.js
Normal file
26
src/database/schemas/management/product.schema.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main product schema
|
||||||
|
const productSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: { type: String, required: true },
|
||||||
|
tags: [{ type: String }],
|
||||||
|
version: { type: String },
|
||||||
|
priceMode: { type: String, default: 'margin' },
|
||||||
|
margin: { type: Number, required: false },
|
||||||
|
amount: { type: Number, required: false },
|
||||||
|
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
// Add virtual id getter
|
||||||
|
productSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
productSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const productModel = mongoose.model('product', productSchema);
|
||||||
20
src/database/schemas/management/user.schema.js
Normal file
20
src/database/schemas/management/user.schema.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
const userSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
username: { required: true, type: String },
|
||||||
|
name: { required: true, type: String },
|
||||||
|
firstName: { required: false, type: String },
|
||||||
|
lastName: { required: false, type: String },
|
||||||
|
email: { required: true, type: String },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
userSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const userModel = mongoose.model('user', userSchema);
|
||||||
21
src/database/schemas/management/vendor.schema.js
Normal file
21
src/database/schemas/management/vendor.schema.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
const vendorSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
name: { required: true, type: String },
|
||||||
|
website: { required: false, type: String },
|
||||||
|
email: { required: false, type: String },
|
||||||
|
phone: { required: false, type: String },
|
||||||
|
contact: { required: false, type: String },
|
||||||
|
country: { required: false, type: String },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
vendorSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
vendorSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const vendorModel = mongoose.model('vendor', vendorSchema);
|
||||||
41
src/database/schemas/misc/note.schema.js
Normal file
41
src/database/schemas/misc/note.schema.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const noteSchema = new mongoose.Schema({
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
noteType: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'noteType',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'user',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
noteSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
noteSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const noteModel = mongoose.model('note', noteSchema);
|
||||||
24
src/database/schemas/production/gcodefile.schema.js
Normal file
24
src/database/schemas/production/gcodefile.schema.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const gcodeFileSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
gcodeFileName: { required: false, type: String },
|
||||||
|
gcodeFileInfo: { required: true, type: Object },
|
||||||
|
size: { type: Number, required: false },
|
||||||
|
filament: { type: Schema.Types.ObjectId, ref: 'filament', required: true },
|
||||||
|
parts: [{ type: Schema.Types.ObjectId, ref: 'part', required: true }],
|
||||||
|
cost: { type: Number, required: false },
|
||||||
|
createdAt: { type: Date },
|
||||||
|
updatedAt: { type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.index({ name: 'text', brand: 'text' });
|
||||||
|
|
||||||
|
gcodeFileSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const gcodeFileModel = mongoose.model('gcodeFile', gcodeFileSchema);
|
||||||
34
src/database/schemas/production/job.schema.js
Normal file
34
src/database/schemas/production/job.schema.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const jobSchema = new mongoose.Schema({
|
||||||
|
state: {
|
||||||
|
type: { required: true, type: String },
|
||||||
|
},
|
||||||
|
printers: [{ type: Schema.Types.ObjectId, ref: 'printer', required: false }],
|
||||||
|
createdAt: { required: true, type: Date },
|
||||||
|
updatedAt: { required: true, type: Date },
|
||||||
|
startedAt: { required: false, type: Date },
|
||||||
|
finishedAt: { required: false, type: Date },
|
||||||
|
gcodeFile: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'gcodeFile',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
quantity: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 1,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
subJobs: [{ type: Schema.Types.ObjectId, ref: 'subJob', required: false }],
|
||||||
|
notes: [{ type: Schema.Types.ObjectId, ref: 'note', required: false }],
|
||||||
|
});
|
||||||
|
|
||||||
|
jobSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
jobSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const jobModel = mongoose.model('job', jobSchema);
|
||||||
72
src/database/schemas/production/printer.schema.js
Normal file
72
src/database/schemas/production/printer.schema.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the moonraker connection schema
|
||||||
|
const moonrakerSchema = new Schema(
|
||||||
|
{
|
||||||
|
host: { type: String, required: true },
|
||||||
|
port: { type: Number, required: true },
|
||||||
|
protocol: { type: String, required: true },
|
||||||
|
apiKey: { type: String, default: null, required: false }
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Define the alert schema
|
||||||
|
const alertSchema = new Schema(
|
||||||
|
{
|
||||||
|
priority: { type: String, required: true }, // order to show
|
||||||
|
type: { type: String, required: true } // selectFilament, error, info, message,
|
||||||
|
},
|
||||||
|
{ timestamps: true, _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Define the main FDM printer schema
|
||||||
|
const printerSchema = new Schema(
|
||||||
|
{
|
||||||
|
name: { type: String, required: true },
|
||||||
|
online: { type: Boolean, required: true, default: false },
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true, default: 'offline' },
|
||||||
|
progress: { type: Number, required: false, default: 0 }
|
||||||
|
},
|
||||||
|
connectedAt: { type: Date, default: null },
|
||||||
|
loadedFilament: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'filament',
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
moonraker: { type: moonrakerSchema, required: true },
|
||||||
|
tags: [{ type: String }],
|
||||||
|
firmware: { type: String },
|
||||||
|
currentJob: { type: Schema.Types.ObjectId, ref: 'job' },
|
||||||
|
currentSubJob: { type: Schema.Types.ObjectId, ref: 'subJob' },
|
||||||
|
currentFilamentStock: { type: Schema.Types.ObjectId, ref: 'filamentStock' },
|
||||||
|
subJobs: [{ type: Schema.Types.ObjectId, ref: 'subJob' }],
|
||||||
|
vendor: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'vendor',
|
||||||
|
default: null,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
host: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'host',
|
||||||
|
default: null,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
alerts: [alertSchema]
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
printerSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
printerSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const printerModel = mongoose.model('printer', printerSchema);
|
||||||
50
src/database/schemas/production/subjob.schema.js
Normal file
50
src/database/schemas/production/subjob.schema.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const subJobSchema = new mongoose.Schema({
|
||||||
|
printer: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'printer',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
job: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'job',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
subJobId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
gcodeFile: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'gcodeFile',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: { required: true, type: String },
|
||||||
|
percent: { required: false, type: Number },
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
startedAt: { required: false, type: Date },
|
||||||
|
finishedAt: { required: false, type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
subJobSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
subJobSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const subJobModel = mongoose.model('subJob', subJobSchema);
|
||||||
90
src/utils.js
Normal file
90
src/utils.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { editObject } from './database/database.js';
|
||||||
|
import { hostModel } from './database/schemas/management/host.schema.js';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
import { loadConfig } from './config.js';
|
||||||
|
import { userModel } from './database/schemas/management/user.schema.js';
|
||||||
|
import { documentSizeModel } from './database/schemas/management/documentsize.schema.js';
|
||||||
|
import { documentTemplateModel } from './database/schemas/management/documenttemplate.schema.js';
|
||||||
|
import { printerModel } from './database/schemas/production/printer.schema.js';
|
||||||
|
import { subJobModel } from './database/schemas/production/subjob.schema.js';
|
||||||
|
import { jobModel } from './database/schemas/production/job.schema.js';
|
||||||
|
import { filamentStockModel } from './database/schemas/inventory/filamentstock.schema.js';
|
||||||
|
|
||||||
|
const config = loadConfig();
|
||||||
|
|
||||||
|
const authCodeLength = 64;
|
||||||
|
|
||||||
|
const modelList = [
|
||||||
|
hostModel,
|
||||||
|
userModel,
|
||||||
|
documentSizeModel,
|
||||||
|
documentTemplateModel,
|
||||||
|
printerModel,
|
||||||
|
jobModel,
|
||||||
|
subJobModel,
|
||||||
|
filamentStockModel
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function generateHostOTP(id) {
|
||||||
|
const otp = crypto.randomInt(0, 1000000).toString().padStart(6, '0'); // 0 to 999999
|
||||||
|
const expiresAt = new Date(
|
||||||
|
Date.now() + (config.otpExpiryMins || 2) * 60 * 1000
|
||||||
|
); // 2 minutes in ms
|
||||||
|
|
||||||
|
const otpHost = await editObject({
|
||||||
|
model: hostModel,
|
||||||
|
id: id,
|
||||||
|
updateData: { otp: otp, otpExpiresAt: expiresAt }
|
||||||
|
});
|
||||||
|
|
||||||
|
return otpHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateAuthCode() {
|
||||||
|
return nanoid(authCodeLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateEtcId() {
|
||||||
|
return nanoid(24);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getChangedValues(oldObj, newObj, old = false) {
|
||||||
|
const changes = {};
|
||||||
|
|
||||||
|
// Check all keys in the new object
|
||||||
|
for (const key in newObj) {
|
||||||
|
// Skip if the key is _id or timestamps
|
||||||
|
if (key === '_id' || key === 'createdAt' || key === 'updatedAt') continue;
|
||||||
|
|
||||||
|
const oldVal = oldObj ? oldObj[key] : undefined;
|
||||||
|
const newVal = newObj[key];
|
||||||
|
|
||||||
|
// If both values are objects (but not arrays or null), recurse
|
||||||
|
if (
|
||||||
|
oldVal &&
|
||||||
|
newVal &&
|
||||||
|
typeof oldVal === 'object' &&
|
||||||
|
typeof newVal === 'object' &&
|
||||||
|
!Array.isArray(oldVal) &&
|
||||||
|
!Array.isArray(newVal) &&
|
||||||
|
oldVal !== null &&
|
||||||
|
newVal !== null
|
||||||
|
) {
|
||||||
|
const nestedChanges = this.getChangedValues(oldVal, newVal, old);
|
||||||
|
if (Object.keys(nestedChanges).length > 0) {
|
||||||
|
changes[key] = nestedChanges;
|
||||||
|
}
|
||||||
|
} else if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
||||||
|
// If the old value is different from the new value, include it
|
||||||
|
changes[key] = old ? oldVal : newVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModelByName(modelName) {
|
||||||
|
return modelList.filter(model => model.modelName == modelName);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user