Compare commits

...

4 Commits

11 changed files with 427 additions and 501 deletions

View File

@ -47,7 +47,7 @@ logger.level = process.env.LOG_LEVEL;
app.use(log4js.connectLogger(logger, { level: 'trace' }));
const whitelist = [process.env.APP_URL_CLIENT];
const whitelist = [process.env.APP_URL_CLIENT, process.env.APP_URL_ELECTRON_CLIENT];
const corsOptions = {
origin: function (origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {

View File

@ -1,32 +1,34 @@
import express from 'express';
import { isAuthenticated } from '../../keycloak.js';
import { parseFilter } from '../../utils.js';
import { getFilter, convertPropertiesString } from '../../utils.js';
const router = express.Router();
import {
listFilamentStocksRouteHandler,
getFilamentStockRouteHandler,
editFilamentStockRouteHandler,
newFilamentStockRouteHandler,
deleteFilamentStockRouteHandler,
listFilamentStocksByPropertiesRouteHandler,
} from '../../services/inventory/filamentstocks.js';
// list of filamentStocks
// list of filament stocks
router.get('/', isAuthenticated, (req, res) => {
const { page, limit, property, sort, order } = req.query;
const { page, limit, property, search, sort, order } = req.query;
const allowedFilters = ['filament', 'state', 'startingWeight', 'currentWeight'];
const filter = getFilter(req.query, allowedFilters);
listFilamentStocksRouteHandler(req, res, page, limit, property, filter, search, sort, order);
});
const allowedFilters = ['filament', 'filament._id'];
var filter = {};
for (const [key, value] of Object.entries(req.query)) {
for (var i = 0; i < allowedFilters.length; i++) {
if (key == allowedFilters[i]) {
const parsedFilter = parseFilter(key, value);
filter = { ...filter, ...parsedFilter };
router.get('/properties', isAuthenticated, (req, res) => {
let properties = convertPropertiesString(req.query.properties);
const allowedFilters = ['filament', 'state.type'];
const filter = getFilter(req.query, allowedFilters, false);
var masterFilter = {};
if (req.query.masterFilter) {
masterFilter = JSON.parse(req.query.masterFilter);
}
}
}
listFilamentStocksRouteHandler(req, res, page, limit, property, filter, sort, order);
listFilamentStocksByPropertiesRouteHandler(req, res, properties, filter, masterFilter);
});
router.post('/', isAuthenticated, (req, res) => {
@ -37,4 +39,12 @@ router.get('/:id', isAuthenticated, (req, res) => {
getFilamentStockRouteHandler(req, res);
});
router.put('/:id', isAuthenticated, async (req, res) => {
editFilamentStockRouteHandler(req, res);
});
router.delete('/:id', isAuthenticated, async (req, res) => {
deleteFilamentStockRouteHandler(req, res);
});
export default router;

View File

@ -1,34 +1,34 @@
import express from 'express';
import { isAuthenticated } from '../../keycloak.js';
import { parseFilter } from '../../utils.js';
import { convertPropertiesString, getFilter } from '../../utils.js';
const router = express.Router();
import {
listUsersRouteHandler,
listUsersByPropertiesRouteHandler,
getUserRouteHandler,
editUserRouteHandler,
} from '../../services/management/users.js';
// list of users
// list of document templates
router.get('/', isAuthenticated, (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const allowedFilters = ['username', 'name', 'firstName', 'lastName'];
var filter = {};
for (const [key, value] of Object.entries(req.query)) {
for (var i = 0; i < allowedFilters.length; i++) {
if (key == allowedFilters[i]) {
const parsedFilter = parseFilter(key, value);
filter = { ...filter, ...parsedFilter };
}
}
}
const allowedFilters = ['name', 'username', 'firstName', 'lastName', 'email'];
const filter = getFilter(req.query, allowedFilters);
listUsersRouteHandler(req, res, page, limit, property, filter, search, sort, order);
});
router.get('/properties', isAuthenticated, (req, res) => {
let properties = convertPropertiesString(req.query.properties);
const allowedFilters = [];
const filter = getFilter(req.query, allowedFilters, false);
var masterFilter = {};
if (req.query.masterFilter) {
masterFilter = JSON.parse(req.query.masterFilter);
}
listUsersByPropertiesRouteHandler(req, res, properties, filter, masterFilter);
});
router.get('/:id', isAuthenticated, (req, res) => {
getUserRouteHandler(req, res);
});

View File

@ -12,28 +12,16 @@ import {
const router = express.Router();
router.get('/login', async (req, res) => {
loginRouteHandler(req, res);
router.get('/:redirectType/login', async (req, res) => {
loginRouteHandler(req, res, req.params.redirectType);
});
router.get('/app/login', async (req, res) => {
loginRouteHandler(req, res, true);
router.get('/:redirectType/callback', async (req, res) => {
loginCallbackRouteHandler(req, res, req.params.redirectType);
});
router.get('/callback', async (req, res) => {
loginCallbackRouteHandler(req, res);
});
router.get('/app/callback', async (req, res) => {
loginCallbackRouteHandler(req, res, true);
});
router.get('/token', async (req, res) => {
loginTokenRouteHandler(req, res);
});
router.get('/app/token', async (req, res) => {
loginTokenRouteHandler(req, res, true);
router.get('/:redirectType/token', async (req, res) => {
loginTokenRouteHandler(req, res, req.params.redirectType);
});
router.get('/refresh', async (req, res) => {

View File

@ -4,21 +4,29 @@ import { isAuthenticated } from '../../keycloak.js';
const router = express.Router();
import {
listJobsRouteHandler,
listJobsByPropertiesRouteHandler,
getJobRouteHandler,
newJobRouteHandler,
deleteJobRouteHandler,
getJobStatsRouteHandler,
} from '../../services/production/jobs.js';
import { getFilter } from '../../utils.js';
import { convertPropertiesString, getFilter } from '../../utils.js';
// list of jobs
router.get('/', isAuthenticated, (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const allowedFilters = ['country'];
const allowedFilters = ['state'];
const filter = getFilter(req.query, allowedFilters);
listJobsRouteHandler(req, res, page, limit, property, filter, search, sort, order);
});
router.get('/properties', isAuthenticated, (req, res) => {
let properties = convertPropertiesString(req.query.properties);
const allowedFilters = ['state'];
const filter = getFilter(req.query, allowedFilters, false);
listJobsByPropertiesRouteHandler(req, res, properties, filter);
});
router.post('/', isAuthenticated, (req, res) => {
newJobRouteHandler(req, res);
});

View File

@ -1,11 +1,15 @@
import dotenv from 'dotenv';
import { filamentStockModel } from '../../schemas/inventory/filamentstock.schema.js';
import { filamentModel } from '../../schemas/management/filament.schema.js';
import { stockEventModel } from '../../schemas/inventory/stockevent.schema.js';
import log4js from 'log4js';
import mongoose from 'mongoose';
import { distributeNew, flatternObjectIds, getAuditLogs, newAuditLog } from '../../utils.js';
import {
deleteObject,
listObjects,
getObject,
editObject,
newObject,
listObjectsByProperties,
} from '../../database/database.js';
dotenv.config();
const logger = log4js.getLogger('Filament Stocks');
@ -18,173 +22,137 @@ export const listFilamentStocksRouteHandler = async (
limit = 25,
property = '',
filter = {},
search = '',
sort = '',
order = 'ascend'
) => {
try {
// Calculate the skip value based on the page number and limit
const skip = (page - 1) * limit;
let filamentStock;
let aggregateCommand = [];
aggregateCommand.push({
$lookup: {
from: 'filaments', // The collection name (usually lowercase plural)
localField: 'filament', // The field in your current model
foreignField: '_id', // The field in the products collection
as: 'filament', // The output field name
},
const result = await listObjects({
model: filamentStockModel,
page,
limit,
property,
filter,
search,
sort,
order,
populate: [{ path: 'filament' }],
});
aggregateCommand.push({ $unwind: '$filament' });
if (filter != {}) {
// use filtering if present
aggregateCommand.push({ $match: filter });
if (result?.error) {
logger.error('Error listing filament stocks.');
res.status(result.code).send(result);
return;
}
if (property != '') {
aggregateCommand.push({ $group: { _id: `$${property}` } }); // group all same properties
aggregateCommand.push({ $project: { _id: 0, [property]: '$_id' } }); // rename _id to the property name
} else {
aggregateCommand.push({ $project: { image: 0, url: 0 } });
logger.debug(`List of filament stocks (Page ${page}, Limit ${limit}). Count: ${result.length}`);
res.send(result);
};
export const listFilamentStocksByPropertiesRouteHandler = async (
req,
res,
properties = '',
filter = {},
masterFilter = {}
) => {
const result = await listObjectsByProperties({
model: filamentStockModel,
properties,
filter,
populate: ['filament'],
masterFilter,
});
if (result?.error) {
logger.error('Error listing filament stocks.');
res.status(result.code).send(result);
return;
}
// Add sorting if sort parameter is provided
if (sort) {
const sortOrder = order === 'descend' ? -1 : 1;
aggregateCommand.push({ $sort: { [sort]: sortOrder } });
}
aggregateCommand.push({ $skip: skip });
aggregateCommand.push({ $limit: Number(limit) });
console.log(aggregateCommand);
filamentStock = await filamentStockModel.aggregate(aggregateCommand);
logger.trace(
`List of filamentStocks (Page ${page}, Limit ${limit}, Property ${property}):`,
filamentStock
);
res.send(filamentStock);
} catch (error) {
logger.error('Error listing filament stocks:', error);
res.status(500).send({ error: error });
}
logger.debug(`List of filament stocks. Count: ${result.length}`);
res.send(result);
};
export const getFilamentStockRouteHandler = async (req, res) => {
try {
const id = req.params.id;
const result = await getObject({
model: filamentStockModel,
id,
populate: [{ path: 'filament' }],
});
if (result?.error) {
logger.warn(`Filament Stock not found with supplied id.`);
return res.status(result.code).send(result);
}
logger.debug(`Retreived filament stock with ID: ${id}`);
res.send(result);
};
export const editFilamentStockRouteHandler = async (req, res) => {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
// Fetch the filamentStock with the given remote address
const filamentStock = await filamentStockModel
.findOne({
_id: id,
})
.populate('filament');
if (!filamentStock) {
logger.warn(`Filament stock not found with supplied id.`);
return res.status(404).send({ error: 'Print job not found.' });
logger.trace(`Filament Stock with ID: ${id}`);
const updateData = {};
// Create audit log before updating
const result = await editObject({
model: filamentStockModel,
id,
updateData,
user: req.user,
});
if (result.error) {
logger.error('Error editing filament stock:', result.error);
res.status(result).send(result);
return;
}
logger.trace(`Filament stock with ID: ${id}:`, filamentStock);
logger.debug(`Edited filament stock with ID: ${id}`);
const auditLogs = await getAuditLogs(id);
res.send({ ...filamentStock._doc, auditLogs: auditLogs });
} catch (error) {
logger.error('Error fetching filament stock:', error);
res.status(500).send({ error: error.message });
}
res.send(result);
};
export const newFilamentStockRouteHandler = async (req, res) => {
var filament = null;
try {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.body.filament._id);
// Fetch the filament with the given remote address
filament = await filamentModel.findOne({
_id: id,
});
if (!filament) {
logger.warn(`Filament not found with supplied id.`);
return res.status(404).send({ error: 'Filament not found.' });
}
logger.trace(`Filament with ID: ${id}:`, filament);
} catch (error) {
logger.error('Error fetching filament:', error);
return res.status(500).send({ error: error.message });
}
try {
logger.warn(req.body);
const startingWeight = req.body.startingWeight; // { net, gross }
if (!startingWeight || typeof startingWeight.gross !== 'number') {
return res.status(400).send({ error: 'startingWeight.gross is required' });
}
// Calculate net if not provided
const net =
typeof startingWeight.net === 'number'
? startingWeight.net
: startingWeight.gross - filament.emptySpoolWeight;
const starting = {
gross: startingWeight.gross,
net: net,
};
const newFilamentStock = {
startingWeight: starting,
currentWeight: { ...starting },
filament: req.body.filament,
state: {
type: 'unconsumed',
percent: '0', // schema requires string
},
};
const result = await filamentStockModel.create(flatternObjectIds(newFilamentStock));
if (!result) {
logger.error('No filament stock created.');
return res.status(500).send({ error: 'No filament stock created.' });
}
await newAuditLog(newFilamentStock, result._id, 'filamentStock', req.user);
await distributeNew(result._id, 'filamentStock');
console.log(result);
// Create initial stock event (optional, but keep logic if needed)
const stockEvent = {
value: starting.net,
current: starting.net,
unit: 'g',
parent: result,
parentType: 'filamentStock',
owner: req.user,
ownerType: 'user',
createdAt: new Date(),
const newData = {
updatedAt: new Date(),
startingWeight: req.body.startingWeight,
currentWeight: req.body.currentWeight,
filament: req.body.filament,
};
const eventResult = await stockEventModel.create(flatternObjectIds(stockEvent));
if (!eventResult) {
logger.error('Failed to create initial stock event.');
return res.status(500).send({ error: 'Failed to create initial stock event.' });
const result = await newObject({
model: filamentStockModel,
newData,
user: req.user,
});
if (result.error) {
logger.error('No filament stock created:', result.error);
return res.status(result.code).send(result);
}
await newAuditLog(stockEvent, eventResult._id, 'stockEvent', req.user);
logger.debug(`New filament stock with ID: ${result._id}`);
return res.send({ status: 'ok' });
} catch (updateError) {
logger.error('Error adding filament stock:', updateError);
return res.status(500).send({ error: updateError.message });
}
res.send(result);
};
export const deleteFilamentStockRouteHandler = async (req, res) => {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
logger.trace(`Filament Stock with ID: ${id}`);
const result = await deleteObject({
model: filamentStockModel,
id,
user: req.user,
});
if (result.error) {
logger.error('No filament stock deleted:', result.error);
return res.status(result.code).send(result);
}
logger.debug(`Deleted filament stock with ID: ${result._id}`);
res.send(result);
};

View File

@ -1,15 +1,14 @@
import dotenv from 'dotenv';
import dotenv, { populate } from 'dotenv';
import { filamentModel } from '../../schemas/management/filament.schema.js';
import log4js from 'log4js';
import mongoose from 'mongoose';
import {
newAuditLog,
editAuditLog,
distributeUpdate,
flatternObjectIds,
distributeNew,
} from '../../utils.js';
import { listObjectsByProperties } from '../../database/database.js';
getObject,
listObjects,
listObjectsByProperties,
editObject,
newObject,
} from '../../database/database.js';
dotenv.config();
const logger = log4js.getLogger('Filaments');
@ -26,69 +25,26 @@ export const listFilamentsRouteHandler = async (
sort = '',
order = 'ascend'
) => {
try {
// Calculate the skip value based on the page number and limit
const skip = (page - 1) * limit;
let filament;
let aggregateCommand = [];
if (search) {
// Add a text search match stage for name and brand fields
aggregateCommand.push({
$match: {
$text: {
$search: search,
},
},
});
}
aggregateCommand.push({
$lookup: {
from: 'vendors', // The collection name (usually lowercase plural)
localField: 'vendor', // The field in your current model
foreignField: '_id', // The field in the products collection
as: 'vendor', // The output field name
},
const result = await listObjects({
model: filamentModel,
page,
limit,
property,
filter,
search,
sort,
order,
populate: ['vendor'],
});
aggregateCommand.push({ $unwind: '$vendor' });
if (filter != {}) {
// use filtering if present
aggregateCommand.push({ $match: filter });
if (result?.error) {
logger.error('Error listing filaments.');
res.status(result.code).send(result);
return;
}
if (property != '') {
aggregateCommand.push({ $group: { _id: `$${property}` } }); // group all same properties
aggregateCommand.push({ $project: { _id: 0, [property]: '$_id' } }); // rename _id to the property name
} else {
aggregateCommand.push({ $project: { image: 0, url: 0 } });
}
// Add sorting if sort parameter is provided
if (sort) {
const sortOrder = order === 'descend' ? -1 : 1;
aggregateCommand.push({ $sort: { [sort]: sortOrder } });
}
aggregateCommand.push({ $skip: skip });
aggregateCommand.push({ $limit: Number(limit) });
console.log(aggregateCommand);
filament = await filamentModel.aggregate(aggregateCommand);
logger.trace(
`List of filaments (Page ${page}, Limit ${limit}, Property ${property}):`,
filament
);
res.send(filament);
} catch (error) {
logger.error('Error listing filaments:', error);
res.status(500).send({ error: error });
}
logger.debug(`List of filaments (Page ${page}, Limit ${limit}). Count: ${result.length}`);
res.send(result);
};
export const listFilamentsByPropertiesRouteHandler = async (
@ -115,46 +71,26 @@ export const listFilamentsByPropertiesRouteHandler = async (
};
export const getFilamentRouteHandler = async (req, res) => {
try {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
// Fetch the filament with the given remote address
const filament = await filamentModel
.findOne({
_id: id,
})
.populate('vendor');
if (!filament) {
const id = req.params.id;
const result = await getObject({
model: filamentModel,
id,
populate: 'vendor',
});
if (result?.error) {
logger.warn(`Filament not found with supplied id.`);
return res.status(404).send({ error: 'Print job not found.' });
}
logger.trace(`Filament with ID: ${id}:`, filament);
res.send({ ...filament._doc });
} catch (error) {
logger.error('Error fetching Filament:', error);
res.status(500).send({ error: error.message });
return res.status(result.code).send(result);
}
logger.debug(`Retreived filament with ID: ${id}`);
res.send(result);
};
export const editFilamentRouteHandler = async (req, res) => {
try {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
// Fetch the filament with the given remote address
const filament = await filamentModel.findOne({ _id: id });
if (!filament) {
// Error handling
logger.warn(`Filament not found with supplied id.`);
return res.status(404).send({ error: 'Print job not found.' });
}
logger.trace(`Filament with ID: ${id}`);
logger.trace(`Filament with ID: ${id}:`, filament);
try {
const updateData = {
updatedAt: new Date(),
name: req.body.name,
@ -169,35 +105,26 @@ export const editFilamentRouteHandler = async (req, res) => {
density: req.body.density,
emptySpoolWeight: req.body.emptySpoolWeight,
};
const result = await editObject({
model: filamentModel,
id,
updateData,
user: req.user,
});
// Create audit log before updating
await editAuditLog(filament.toObject(), updateData, id, 'filament', req.user);
const result = await filamentModel.updateOne(
{ _id: id },
{ $set: flatternObjectIds(updateData) }
);
if (result.nModified === 0) {
logger.error('No Filament updated.');
return res.status(500).send({ error: 'No filaments updated.' });
if (result.error) {
logger.error('Error editing filament:', result.error);
res.status(result).send(result);
return;
}
await distributeUpdate(updateData, id, 'filament');
} catch (updateError) {
logger.error('Error updating filament:', updateError);
return res.status(500).send({ error: updateError.message });
}
logger.debug(`Edited filament with ID: ${id}`);
return res.send('OK');
} catch (fetchError) {
logger.error('Error fetching filament:', fetchError);
return res.status(500).send({ error: fetchError.message });
}
res.send(result);
};
export const newFilamentRouteHandler = async (req, res) => {
try {
const newFilament = {
const newData = {
createdAt: new Date(),
updatedAt: new Date(),
name: req.body.name,
@ -213,20 +140,17 @@ export const newFilamentRouteHandler = async (req, res) => {
emptySpoolWeight: req.body.emptySpoolWeight,
};
const result = await filamentModel.create(flatternObjectIds(newFilament));
if (result.nCreated === 0) {
logger.error('No filament created.');
res.status(500).send({ error: 'No filament created.' });
const result = await newObject({
model: filamentModel,
newData,
user: req.user,
});
if (result.error) {
logger.error('No filament created:', result.error);
return res.status(result.code).send(result);
}
// Create audit log for new filament
await newAuditLog(newFilament, result._id, 'filament', req.user);
await distributeNew(result._id, 'filament');
logger.debug(`New filament with ID: ${result._id}`);
res.status(200).send({ status: 'ok' });
} catch (updateError) {
logger.error('Error updating filament:', updateError);
res.status(500).send({ error: updateError.message });
}
res.send(result);
};

View File

@ -47,11 +47,18 @@ export const listHostsRouteHandler = async (
res.send(result);
};
export const listHostsByPropertiesRouteHandler = async (req, res, properties = '', filter = {}) => {
export const listHostsByPropertiesRouteHandler = async (
req,
res,
properties = '',
filter = {},
masterFilter = {}
) => {
const result = await listObjectsByProperties({
model: hostModel,
properties,
filter,
masterFilter,
});
if (result?.error) {
@ -88,8 +95,9 @@ export const editHostRouteHandler = async (req, res) => {
updatedAt: new Date(),
name: req.body.name,
active: req.body.active,
tags: req.body.tags,
};
// Create audit log before updating
const result = await editObject({
model: hostModel,
id,

View File

@ -2,7 +2,12 @@ import dotenv from 'dotenv';
import { userModel } from '../../schemas/management/user.schema.js';
import log4js from 'log4js';
import mongoose from 'mongoose';
import { distributeUpdate, editAuditLog } from '../../utils.js';
import {
listObjects,
listObjectsByProperties,
getObject,
editObject,
} from '../../database/database.js';
dotenv.config();
@ -20,122 +25,93 @@ export const listUsersRouteHandler = async (
sort = '',
order = 'ascend'
) => {
try {
// Calculate the skip value based on the page number and limit
const skip = (page - 1) * limit;
let user;
let aggregateCommand = [];
if (search) {
// Add a text search match stage for name and brand fields
aggregateCommand.push({
$match: {
$text: {
$search: search,
},
},
const result = await listObjects({
model: userModel,
page,
limit,
property,
filter,
search,
sort,
order,
});
if (result?.error) {
logger.error('Error listing users.');
res.status(result.code).send(result);
return;
}
if (filter != {}) {
// use filtering if present
aggregateCommand.push({ $match: filter });
logger.debug(`List of users (Page ${page}, Limit ${limit}). Count: ${result.length}`);
res.send(result);
};
export const listUsersByPropertiesRouteHandler = async (
req,
res,
properties = '',
filter = {},
masterFilter = {}
) => {
const result = await listObjectsByProperties({
model: userModel,
properties,
filter,
masterFilter,
});
if (result?.error) {
logger.error('Error listing users.');
res.status(result.code).send(result);
return;
}
if (property != '') {
aggregateCommand.push({ $group: { _id: `$${property}` } }); // group all same properties
aggregateCommand.push({ $project: { _id: 0, [property]: '$_id' } }); // rename _id to the property name
}
// Add sorting if sort parameter is provided
if (sort) {
const sortOrder = order === 'descend' ? -1 : 1;
aggregateCommand.push({ $sort: { [sort]: sortOrder } });
}
aggregateCommand.push({ $skip: skip });
aggregateCommand.push({ $limit: Number(limit) });
console.log(aggregateCommand);
user = await userModel.aggregate(aggregateCommand);
logger.trace(`List of users (Page ${page}, Limit ${limit}, Property ${property}):`, user);
res.send(user);
} catch (error) {
logger.error('Error listing users:', error);
res.status(500).send({ error: error });
}
logger.debug(`List of users. Count: ${result.length}`);
res.send(result);
};
export const getUserRouteHandler = async (req, res) => {
try {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
// Fetch the user with the given ID
const user = await userModel.findOne({
_id: id,
const id = req.params.id;
const result = await getObject({
model: userModel,
id,
});
if (!user) {
if (result?.error) {
logger.warn(`User not found with supplied id.`);
return res.status(404).send({ error: 'User not found.' });
}
logger.trace(`User with ID: ${id}:`, user);
res.send({ ...user._doc });
} catch (error) {
logger.error('Error fetching User:', error);
res.status(500).send({ error: error.message });
return res.status(result.code).send(result);
}
logger.debug(`Retreived user with ID: ${id}`);
res.send(result);
};
export const editUserRouteHandler = async (req, res) => {
try {
// Get ID from params
const id = new mongoose.Types.ObjectId(req.params.id);
// Fetch the user with the given ID
const user = await userModel.findOne({ _id: id });
if (!user) {
// Error handling
logger.warn(`User not found with supplied id.`);
return res.status(404).send({ error: 'User not found.' });
}
logger.trace(`User with ID: ${id}`);
logger.trace(`User with ID: ${id}:`, user);
try {
const updateData = {
updatedAt: new Date(),
username: req.body.username,
name: req.body.name,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
};
console.log(req.user);
// Create audit log before updating
await editAuditLog(user.toObject(), updateData, id, 'user', req.user);
const result = await editObject({
model: userModel,
id,
updateData,
user: req.user,
});
const result = await userModel.updateOne({ _id: id }, { $set: updateData });
if (result.nModified === 0) {
logger.error('No User updated.');
res.status(500).send({ error: 'No users updated.' });
if (result.error) {
logger.error('Error editing user:', result.error);
res.status(result).send(result);
return;
}
await distributeUpdate(updateData, id, 'user');
} catch (updateError) {
logger.error('Error updating user:', updateError);
res.status(500).send({ error: updateError.message });
}
res.send('OK');
} catch (fetchError) {
logger.error('Error fetching user:', fetchError);
res.status(500).send({ error: fetchError.message });
}
logger.debug(`Edited user with ID: ${id}`);
res.send(result);
};

View File

@ -32,6 +32,7 @@ const lookupUserByToken = async (token) => {
// Check cache first
const cachedUser = tokenUserCache.get(token);
if (cachedUser) {
console.log(cachedUser);
logger.debug(`User found in token cache for token: ${token.substring(0, 20)}...`);
return cachedUser;
}
@ -79,14 +80,14 @@ const removeUserFromTokenCache = (token) => {
};
// Login handler
export const loginRouteHandler = (req, res, isApp = false) => {
export const loginRouteHandler = (req, res, redirectType = 'web') => {
// Get the redirect URL from form data or default to production overview
const redirectUrl = req.query.redirect_uri || '/production/overview';
// Store the original URL to redirect after login
const authUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`;
const callBackState = isApp ? '/auth/app/callback' : '/auth/callback';
const callbackUrl = encodeURIComponent(`${process.env.APP_URL_API}${callBackState}`);
const callBackState = `/auth/${redirectType}/callback`;
const callbackUrl = `${process.env.APP_URL_API}${callBackState}`;
const state = encodeURIComponent(redirectUrl);
logger.warn(req.query.redirect_uri);
@ -138,7 +139,7 @@ const fetchAndStoreUser = async (req, token) => {
};
// Function to exchange authorization code for tokens, fetch user, and set session
export const loginTokenRouteHandler = async (req, res, isApp = false) => {
export const loginTokenRouteHandler = async (req, res, redirectType = 'web') => {
const code = req.query.code;
if (!code) {
return res.status(400).json({ error: 'Authorization code missing' });
@ -153,7 +154,7 @@ export const loginTokenRouteHandler = async (req, res, isApp = false) => {
// Otherwise, start the request and store the promise
const tokenPromise = (async () => {
const callBackState = isApp ? '/auth/app/callback' : '/auth/callback';
const callBackState = `/auth/${redirectType}/callback`;
const callbackUrl = `${process.env.APP_URL_API}${callBackState}`;
const tokenUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`;
@ -191,12 +192,13 @@ export const loginTokenRouteHandler = async (req, res, isApp = false) => {
const userAndTokenData = await tokenPromise;
res.status(200).json(userAndTokenData);
} catch (err) {
res.status(500).json({ error: err.message });
var error = err?.response?.data?.error_description || err.message;
res.status(err?.status || 500).json({ error: error });
}
};
// Login callback handler
export const loginCallbackRouteHandler = async (req, res, isApp = false) => {
export const loginCallbackRouteHandler = async (req, res, redirectType = 'web') => {
// Don't use keycloak.protect() here as it expects an already authenticated session
// Extract the code and state from the query parameters
@ -207,9 +209,21 @@ export const loginCallbackRouteHandler = async (req, res, isApp = false) => {
return res.status(400).send('Authorization code missing');
}
const appUrl = isApp
? 'farmcontrol://app'
: process.env.APP_URL_CLIENT || 'http://localhost:3000';
var appUrl;
switch (redirectType) {
case 'web':
appUrl = process.env.APP_URL_CLIENT || 'http://localhost:3000';
break;
case 'app-scheme':
appUrl = 'farmcontrol://app';
break;
case 'app-localhost':
appUrl = process.env.APP_DEV_AUTH_CLIENT || 'http://localhost:3500';
break;
default:
appUrl = process.env.APP_URL_CLIENT || 'http://localhost:3000';
break;
}
const redirectUriRaw = `${appUrl}${state}`;
let redirectUri;
try {
@ -227,7 +241,7 @@ export const loginCallbackRouteHandler = async (req, res, isApp = false) => {
}
// Save session and redirect to the original URL
req.session.save(async () => {
if (isApp) {
if (redirectType == 'app-scheme') {
// Read HTML template and inject redirectUri
const templatePath = resolve(process.cwd(), 'src/services/misc/applaunch.html');
let html = readFileSync(templatePath, 'utf8');

View File

@ -2,7 +2,13 @@ import dotenv from 'dotenv';
import mongoose from 'mongoose';
import { jobModel } from '../../schemas/production/job.schema.js';
import log4js from 'log4js';
import { deleteObject, getObject, listObjects, newObject } from '../../database/database.js';
import {
deleteObject,
getObject,
listObjects,
listObjectsByProperties,
newObject,
} from '../../database/database.js';
import { subJobModel } from '../../schemas/production/subjob.schema.js';
dotenv.config();
@ -42,6 +48,30 @@ export const listJobsRouteHandler = async (
res.send(result);
};
export const listJobsByPropertiesRouteHandler = async (
req,
res,
properties = '',
filter = {},
masterFilter = {}
) => {
const result = await listObjectsByProperties({
model: jobModel,
properties,
filter,
masterFilter,
});
if (result?.error) {
logger.error('Error listing jobs.');
res.status(result.code).send(result);
return;
}
logger.debug(`List of jobs. Count: ${result.length}`);
res.send(result);
};
export const getJobRouteHandler = async (req, res) => {
const id = req.params.id;
const result = await getObject({