Skip to content

Commit

Permalink
Merge pull request #24 from UWAMakers/inventory
Browse files Browse the repository at this point in the history
Inventory
  • Loading branch information
mrfrase3 authored Jul 20, 2024
2 parents 26d56ef + 6726e3e commit 708eb54
Show file tree
Hide file tree
Showing 32 changed files with 2,239 additions and 20 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"ignore": []
},
"dependencies": {
"@aws-sdk/client-s3": "^3.606.0",
"@aws-sdk/s3-request-presigner": "^3.606.0",
"@casl/ability": "^6.7.0",
"@feathers-nuxt/feathers-notifme": "^1.0.1-beta.1",
"@feathersjs/authentication": "^5.0.23",
Expand All @@ -50,7 +52,9 @@
"@feathersjs/feathers": "^5.0.23",
"@feathersjs/socketio": "^5.0.23",
"@feathersjs/transport-commons": "^5.0.23",
"@imgproxy/imgproxy-node": "^1.0.6",
"@mailchimp/mailchimp_marketing": "^3.0.80",
"add": "^2.0.6",
"axios": "^1.6.7",
"bcrypt": "^5.1.1",
"cli-progress": "^3.12.0",
Expand All @@ -72,7 +76,8 @@
"papaparse": "^5.4.1",
"passport-custom": "^1.1.1",
"serve-favicon": "^2.5.0",
"winston": "^3.12.0"
"winston": "^3.12.0",
"yarn": "^1.22.22"
},
"devDependencies": {
"@feathersjs/cli": "^5.0.23",
Expand Down
24 changes: 19 additions & 5 deletions src/app.hooks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { authenticate } = require('@feathersjs/authentication').hooks;
const { defineAbilitiesFor } = require('./services/authentication/authentication.abilities');
const { discard } = require('feathers-hooks-common');

const stashExisting = async (context) => {
const { service, id, params, path, method } = context;
Expand Down Expand Up @@ -38,11 +39,24 @@ module.exports = {
before: {
all: [doubleCheckAbilities],
find: [],
get: [stashExisting],
create: [],
update: [stashExisting],
patch: [stashExisting],
remove: [stashExisting],
get: [
stashExisting,
],
create: [
discard('createdAt', 'updatedAt'),
],
update: [
stashExisting,
discard('createdAt', 'updatedAt'),
],
patch: [
stashExisting,
discard('createdAt', 'updatedAt'),
],
remove: [
stashExisting,
discard('createdAt', 'updatedAt'),
],
},

after: {
Expand Down
2 changes: 2 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const appHooks = require('./app.hooks');
const channels = require('./channels');
const scheduleJobs = require('./util/scheduleJobs');
const syncWithOldData = require('./util/syncWithOldData');
const objectStore = require('./util/object-store');

const authentication = require('./authentication');

Expand Down Expand Up @@ -83,6 +84,7 @@ app.configure(channels);
app.configure(feathersCasl());
app.configure(scheduleJobs);
app.configure(syncWithOldData);
app.configure(objectStore);

const gitHash = gitDescribeSync().hash;
app.get('/version', (req, res) => {
Expand Down
17 changes: 11 additions & 6 deletions src/hooks/search.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
const { checkContext } = require('feathers-hooks-common');
// const { BadRequest } = require('@feathersjs/errors');

const searchRegex = (search, options = { flags: 'i' }) => {
const { flags } = options;
const escapedSearch = search
const searchRegex = (search, options = { flags: 'i', exact: false }) => {
const { flags, exact } = options;
let escapedSearch = search
.trim()
.replace(/[^\w-\s]/g, '.?')
.split(/\s+/g)
.join();
.join('\\s+');
if (exact) escapedSearch = `^${escapedSearch}$`;
return { $regex: escapedSearch, $options: flags };
};

Expand All @@ -21,7 +22,7 @@ const searchRegex = (search, options = { flags: 'i' }) => {
// };

// eslint-disable-next-line no-unused-vars
module.exports = (fields = [], config) => async (context) => {
module.exports = (fields = [], exactFields = [], config) => async (context) => {
checkContext(context, 'before', ['find']);

const { params } = context;
Expand All @@ -43,13 +44,17 @@ module.exports = (fields = [], config) => async (context) => {
}

const search = searchRegex($search);
const exactSearch = searchRegex($search, { exact: true, flags: 'i' });

params.query = {
...rest,
$and: [
...(params.query.$and || []),
{
$or: fields.map((field) => ({ [field]: search })),
$or: [
...fields.map((field) => ({ [field]: search })),
...exactFields.map((field) => ({ [field]: exactSearch })),
],
},
],
};
Expand Down
34 changes: 34 additions & 0 deletions src/hooks/signFiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { checkContext, getItems } = require('feathers-hooks-common');
const _ = require('lodash');
// const { BadRequest } = require('@feathersjs/errors');

// eslint-disable-next-line no-unused-vars
module.exports = (fields = [], config) => async (context) => {
checkContext(context, 'after', null);

const { app } = context;

const records = _.castArray(getItems(context));

const processFile = async (file) => {
if (['fraser-image', 'fraser-object', 's3'].includes(file.provider)) {
return {
...file,
...(await app.presignGet(file)),
};
}
return file;
};

await Promise.all(records.map((record) => Promise.all(fields.map(async (field) => {
if (!_.get(record, field)) return;
if (Array.isArray(_.get(record, field))) {
_.set(record, field, await Promise.all(_.get(record, field).map(processFile)));
} else {
_.set(record, field, await processFile(_.get(record, field)));
}
}))));

return context;
};

7 changes: 4 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ const logger = require('./logger');
const app = require('./app');
const port = app.get('port');

process.on('unhandledRejection', (reason, p) =>
logger.error('Unhandled Rejection at: Promise ', p, reason)
);
process.on('unhandledRejection', (reason, p) => {
logger.error('Unhandled Rejection at: Promise ', p, reason);
console.error(reason, p);
});

app.listen(port).then(() =>
logger.info(
Expand Down
59 changes: 59 additions & 0 deletions src/models/file.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

module.exports = {
type: {
type: String,
required: true,
enum: [
'image',
'source',
'manual',
'datasheet',
'sop',
'risk-assessment',
'certificate',
'msds',
'specification',
'guide',
'software',
'hardware',
'service',
'warranty',
'map',
'other',
],
},
fileType: {
type: String,
required: true,
enum: [
'image',
'document',
'pdf',
'video',
'audio',
'archive',
'text',
'markdown',
'csv',
'other',
],
},
provider: {
type: String,
required: true,
enum: [
'link',
's3',
'google-drive',
'sharepoint',
'fraser-image',
'fraser-object',
'youtube',
'vimeo',
],
},
name: { type: String, required: true },
description: { type: String },
uri: { type: String },
blurhash: { type: String },
};
1 change: 1 addition & 0 deletions src/models/labels.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = function (app) {
printerId: { type: mongooseClient.Types.ObjectId },
template: { type: String, default: 'default' },
copies: { type: Number, default: 1, min: 1 },
thingId: { type: mongooseClient.Types.ObjectId },
data: {
qrUri: { type: String },
header: { type: String },
Expand Down
29 changes: 29 additions & 0 deletions src/models/things-tags.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// things-tags-model.js - A mongoose model
//
// See http://mongoosejs.com/docs/models.html
// for more of what you can do here.

// a thing is a item/place/anything that can be added the inventory system
module.exports = function (app) {
const modelName = 'thingsTags';
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const schema = new Schema({
name: { type: String, required: true, trim: true },
icon: { type: String },
color: { type: String },
createdAt: { type: Date },
updatedAt: { type: Date },
},
{
timestamps: !process.env.DISABLE_TIMESTAMPS,
});

// This is necessary to avoid model compilation errors in watch mode
// see https://mongoosejs.com/docs/api/connection.html#connection_Connection-deleteModel
if (mongooseClient.modelNames().includes(modelName)) {
mongooseClient.deleteModel(modelName);
}
return mongooseClient.model(modelName, schema);

};
120 changes: 120 additions & 0 deletions src/models/things.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const file = require('./file.model');
// things-model.js - A mongoose model
//
// See http://mongoosejs.com/docs/models.html
// for more of what you can do here.

// a thing is a item/place/anything that can be added the inventory system
module.exports = function (app) {
const modelName = 'things';
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const schema = new Schema({
name: { type: String, required: true, trim: true },
type: {
type: String,
required: true,
enum: [
'consumable', // a thing that can be used up, e.g. a roll of tape
'container', // a thing that can hold other things, e.g. a box or a shelf
'equipment', // tools, machines, etc
'fixture', // tables, chairs, etc
],
},
subType: {
type: String,
enum: [
// consumable
'chemical', // a consumable that is a chemical
'material', // raw materials, e.g. wood, metal, etc
'component', // parts that are used to make other things
'kit', // a collection of things that can be put together to make something
'electronic', // electronic components e.g. resistors, capacitors, etc
'mechanical', // mechanical components e.g. gears, bearings, etc
'stationery', // pens, paper, etc
// container
'box',
'moveable',
'surface',
'shelf',
'cabinet',
'drawer',
'bin',
'room',
'building',
'area',
// equipment
'tool',
'machine',
'device',
'instrument',
'vehicle',
'robot',
'computer',
'software',
'accessory',
'ppe',
// 'stationery', // pens, paper, etc
// fixture
'furniture',
'eyesore',
'decorative',
'person',
],
},
ref: { type: String, unique: true },
description: { type: String, trim: true },
flags: [{
type: String,
enum: [
'bookable',
],
}],
initialCost: { type: Number },
quantity: { type: Number },
costs: [{
name: { type: String, required: true },
description: { type: String },
type: {
type: String,
required: true,
enum: ['purchase', 'rental_fixed', 'rental_hourly', 'consumable']
},
quantity: { type: Number, default: 1 },
value: { type: Number },
}],
mainImage: {
type: new Schema(file),
required: false,
},
images: [file],
resources: [file],
contacts: [{
type: { type: String, required: true, enum: ['owner', 'user', 'maintainer', 'supplier'] },
userId: { type: Schema.Types.ObjectId, ref: 'users' },
name: { type: String, required: true },
avatarUrl: { type: String },
discordId: { type: String },
username: { type: String },
description: { type: String },
inactive: { type: Boolean },
}],
tagIds: [{ type: Schema.Types.ObjectId, ref: 'things-tags' }],
containerId: { type: Schema.Types.ObjectId, ref: 'things' },
containerIds: [{ type: Schema.Types.ObjectId, ref: 'things' }],
childIds: [{ type: Schema.Types.ObjectId, ref: 'things' }],
createdAt: { type: Date },
updatedAt: { type: Date },
},
{
timestamps: !process.env.DISABLE_TIMESTAMPS,
});

// This is necessary to avoid model compilation errors in watch mode
// see https://mongoosejs.com/docs/api/connection.html#connection_Connection-deleteModel
if (mongooseClient.modelNames().includes(modelName)) {
mongooseClient.deleteModel(modelName);
}
return mongooseClient.model(modelName, schema);

};
Loading

0 comments on commit 708eb54

Please sign in to comment.