Initial Commit
This commit is contained in:
commit
204964a44c
9
.env.example
Normal file
9
.env.example
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
DB_LINK="mongo-link-to-connect"
|
||||||
|
|
||||||
|
JWT_SECRET="token"
|
||||||
|
|
||||||
|
APP_URL_CLIENT=https://material-dashboard-react-node.creative-tim.com
|
||||||
|
APP_URL_API=https://node-json-api-free.creative-tim.com/login
|
||||||
|
|
||||||
|
MAILTRAP_USER=
|
||||||
|
MAILTRAP_PASSWORD=
|
||||||
134
.gitignore
vendored
Normal file
134
.gitignore
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
.DS_STORE
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
.nova
|
||||||
BIN
images/admin.jpg
Normal file
BIN
images/admin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
images/creator.jpg
Normal file
BIN
images/creator.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 187 KiB |
BIN
images/member.jpg
Normal file
BIN
images/member.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
86
index.js
Normal file
86
index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import { userModel } from "../../schemas/user.schema.js";
|
||||||
|
import { printerModel } from "../../schemas/printer.schema.js";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("Printers");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
export const listPrintersRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
page = 1,
|
||||||
|
limit = 25
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Calculate the skip value based on the page number and limit
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
// Fetch users with pagination
|
||||||
|
const printers = await printerModel.find().skip(skip).limit(limit);
|
||||||
|
|
||||||
|
logger.trace(`List of printers (Page ${page}, Limit ${limit}):`);
|
||||||
|
res.send(printers);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error listing users:", error);
|
||||||
|
res.status(500).send({ error: error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPrinterRouteHandler = async (req, res) => {
|
||||||
|
const remoteAddress = req.params.remoteAddress;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch the printer with the given remote address
|
||||||
|
const printer = await printerModel.findOne({ remoteAddress });
|
||||||
|
|
||||||
|
if (!printer) {
|
||||||
|
logger.warn(`Printer with remote address ${remoteAddress} not found.`);
|
||||||
|
return res.status(404).send({ error: "Printer not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Printer with remote address ${remoteAddress}:`, printer);
|
||||||
|
res.send(printer);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching printer:", error);
|
||||||
|
res.status(500).send({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editPrinterRouteHandler = async (req, res) => {
|
||||||
|
const remoteAddress = req.params.remoteAddress;
|
||||||
|
const { friendlyName } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch the printer with the given remote address
|
||||||
|
const printer = await printerModel.findOne({ remoteAddress });
|
||||||
|
|
||||||
|
if (!printer) {
|
||||||
|
logger.warn(`Printer with remote address ${remoteAddress} not found.`);
|
||||||
|
return res.status(404).send({ error: "Printer not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Editing printer with remote address ${remoteAddress}:`, printer);
|
||||||
|
try {
|
||||||
|
const result = await printerModel.updateOne(
|
||||||
|
{ remoteAddress: remoteAddress },
|
||||||
|
{ $set: req.body }
|
||||||
|
);
|
||||||
|
if (result.nModified === 0) {
|
||||||
|
logger.error("No printers updated.");
|
||||||
|
res.status(500).send({ error: "No printers updated." });
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating printer:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
res.send("OK");
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error("Error fetching printer:", fetchError);
|
||||||
|
res.status(500).send({ error: fetchError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
11960
package-lock.json
generated
Normal file
11960
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
package.json
Normal file
53
package.json
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"name": "node-json-api-free",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@simplewebauthn/server": "^10.0.0",
|
||||||
|
"@tremor/react": "^3.17.2",
|
||||||
|
"antd": "*",
|
||||||
|
"bcrypt": "*",
|
||||||
|
"body-parser": "*",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "*",
|
||||||
|
"express": "*",
|
||||||
|
"express-session": "^1.18.0",
|
||||||
|
"jsonwebtoken": "*",
|
||||||
|
"log4js": "^6.9.1",
|
||||||
|
"mongodb": "*",
|
||||||
|
"mongoose": "*",
|
||||||
|
"mongoose-sequence": "^6.0.1",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"mysql2": "^2.3.3",
|
||||||
|
"node-cron": "^3.0.2",
|
||||||
|
"nodemailer": "*",
|
||||||
|
"nodemon": "^2.0.16",
|
||||||
|
"passport": "*",
|
||||||
|
"passport-jwt": "*",
|
||||||
|
"passport-local": "*",
|
||||||
|
"pg": "^8.7.3",
|
||||||
|
"random-token": "*",
|
||||||
|
"sequelize": "^6.20.1"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.17.10",
|
||||||
|
"@babel/core": "^7.18.5",
|
||||||
|
"@babel/node": "^7.18.5",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.17.12",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread": "^7.18.0",
|
||||||
|
"@babel/preset-env": "^7.18.2",
|
||||||
|
"@babel/register": "^7.17.7",
|
||||||
|
"sequelize-cli": "^6.4.1",
|
||||||
|
"standard": "^17.1.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start:dev": "nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"seed": "node src/mongo/seedData.js",
|
||||||
|
"clear": "node src/mongo/clearDbs.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
60
src/index.js
Normal file
60
src/index.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import express from "express";
|
||||||
|
import bodyParser from "body-parser";
|
||||||
|
import cors from "cors";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import "./passport.js";
|
||||||
|
import { dbConnect } from "./mongo/index.js";
|
||||||
|
import { apiRoutes, authRoutes, printerRoutes, printJobRoutes, gcodeFileRoutes, fillamentRoutes } from "./routes/index.js";
|
||||||
|
import path from "path";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import cron from "node-cron";
|
||||||
|
import ReseedAction from "./mongo/ReseedAction.js";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 8080;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("App");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
app.use(log4js.connectLogger(logger, { level: "trace" }));
|
||||||
|
|
||||||
|
const whitelist = [process.env.APP_URL_CLIENT];
|
||||||
|
const corsOptions = {
|
||||||
|
origin: function (origin, callback) {
|
||||||
|
if (!origin || whitelist.indexOf(origin) !== -1) {
|
||||||
|
callback(null, true);
|
||||||
|
} else {
|
||||||
|
callback(new Error("Not allowed by CORS"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
credentials: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
dbConnect();
|
||||||
|
|
||||||
|
app.use(cors(corsOptions));
|
||||||
|
app.use(bodyParser.json({ type: "application/json", strict: false, limit: '50mb' }));
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.get("/", function (req, res) {
|
||||||
|
const __dirname = fs.realpathSync(".");
|
||||||
|
res.sendFile(path.join(__dirname, "/src/landing/index.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use("/auth", authRoutes);
|
||||||
|
app.use("/overview", apiRoutes);
|
||||||
|
app.use("/printers", printerRoutes);
|
||||||
|
app.use("/printjobs", printJobRoutes);
|
||||||
|
app.use("/gcodefiles", gcodeFileRoutes);
|
||||||
|
app.use("/fillaments", fillamentRoutes);
|
||||||
|
|
||||||
|
if (process.env.SCHEDULE_HOUR) {
|
||||||
|
cron.schedule(`0 */${process.env.SCHEDULE_HOUR} * * *'`, () => {
|
||||||
|
ReseedAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.listen(PORT, () => logger.info(`Server listening to port ${PORT}`));
|
||||||
77
src/landing/index.html
Normal file
77
src/landing/index.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>Node.js API FREE by Creative Tim & UPDIVISION</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #636b6f;
|
||||||
|
font-family: "Nunito", sans-serif;
|
||||||
|
font-weight: 200;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-height {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-ref {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 84px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links > a {
|
||||||
|
color: #636b6f;
|
||||||
|
padding: 0 25px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-b-md {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="flex-center position-ref full-height">
|
||||||
|
<div class="content">
|
||||||
|
<div class="title m-b-md">Headless CMS with ExpressJS API:FREE</div>
|
||||||
|
|
||||||
|
<div class="links">
|
||||||
|
<a href="https://expressjs.com/" target="_blank">Express.js</a>
|
||||||
|
<a href="https://www.mongodb.com/" target="_blank">MongoDB</a>
|
||||||
|
<a href="https://documenter.getpostman.com/view/8138626/Uze1virp" target="_blank">Documentation</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
36
src/mongo/ReseedAction.js
Normal file
36
src/mongo/ReseedAction.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import { userModel } from "../schemas/user.schema.js";
|
||||||
|
import { dbConnect } from "./index.js";
|
||||||
|
|
||||||
|
const ReseedAction = () => {
|
||||||
|
async function clear() {
|
||||||
|
dbConnect();
|
||||||
|
await userModel.deleteMany({});
|
||||||
|
console.log("DB cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function seedDB() {
|
||||||
|
await clear();
|
||||||
|
const salt = await bcrypt.genSalt(10);
|
||||||
|
const hashPassword = await bcrypt.hash("secret", salt);
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
_id: mongoose.Types.ObjectId(1),
|
||||||
|
name: "Admin",
|
||||||
|
email: "admin@jsonapi.com",
|
||||||
|
password: hashPassword,
|
||||||
|
created_at: new Date(),
|
||||||
|
profile_image: "../../images/admin.jpg",
|
||||||
|
};
|
||||||
|
|
||||||
|
const admin = new userModel(user);
|
||||||
|
await admin.save();
|
||||||
|
|
||||||
|
console.log("DB seeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
seedDB();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReseedAction;
|
||||||
13
src/mongo/clearDbs.js
Normal file
13
src/mongo/clearDbs.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
import { userModel } from "../schemas/user.schema.js";
|
||||||
|
import { dbConnect } from "../mongo/index.js";
|
||||||
|
|
||||||
|
async function clear() {
|
||||||
|
dbConnect();
|
||||||
|
await userModel.deleteMany({});
|
||||||
|
console.log("DB cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
clear().then(() => {
|
||||||
|
mongoose.connection.close();
|
||||||
|
});
|
||||||
18
src/mongo/index.js
Normal file
18
src/mongo/index.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("MongoDB");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
function dbConnect() {
|
||||||
|
mongoose.connection.once("open", () => logger.info("Database connected."));
|
||||||
|
return mongoose.connect(
|
||||||
|
`mongodb://${process.env.DB_LINK}/farmcontrol?retryWrites=true&w=majority`,
|
||||||
|
{ }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { dbConnect };
|
||||||
42
src/mongo/seedData.js
Normal file
42
src/mongo/seedData.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
import { userModel } from "../schemas/user.schema.js";
|
||||||
|
import { printJobModel } from "../schemas/printjob.schema.js";
|
||||||
|
import { dbConnect } from "../mongo/index.js";
|
||||||
|
|
||||||
|
async function seedDB() {
|
||||||
|
dbConnect();
|
||||||
|
const salt = await bcrypt.genSalt(10);
|
||||||
|
const hashPassword = await bcrypt.hash("secret", salt);
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
_id: new mongoose.Types.ObjectId(1),
|
||||||
|
name: "Admin",
|
||||||
|
email: "admin@jsonapi.com",
|
||||||
|
password: hashPassword,
|
||||||
|
created_at: new Date(),
|
||||||
|
profile_image: "../../images/admin.jpg",
|
||||||
|
};
|
||||||
|
|
||||||
|
const admin = new userModel(user);
|
||||||
|
await admin.save();
|
||||||
|
|
||||||
|
const printJob = {
|
||||||
|
_id: new mongoose.Types.ObjectId(1),
|
||||||
|
status : {
|
||||||
|
type: "Queued"
|
||||||
|
},
|
||||||
|
created_at: new Date(),
|
||||||
|
updated_at: new Date(),
|
||||||
|
started_at: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const newPrintJob = new printJobModel(printJob);
|
||||||
|
await newPrintJob.save();
|
||||||
|
|
||||||
|
console.log("DB seeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
seedDB().then(() => {
|
||||||
|
mongoose.connection.close();
|
||||||
|
});
|
||||||
27
src/passport.js
Normal file
27
src/passport.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { ExtractJwt } from "passport-jwt";
|
||||||
|
import passportJWT from "passport-jwt";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import passport from "passport";
|
||||||
|
|
||||||
|
import { userModel } from "./schemas/user.schema.js";
|
||||||
|
const JWTStrategy = passportJWT.Strategy;
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
passport.use(
|
||||||
|
new JWTStrategy(
|
||||||
|
{
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: process.env.JWT_SECRET,
|
||||||
|
},
|
||||||
|
function (jwtPayload, done) {
|
||||||
|
return userModel
|
||||||
|
.findOne({ _id: jwtPayload.id })
|
||||||
|
.then((user) => {
|
||||||
|
return done(null, user);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
return done(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
23
src/routes/api/index.js
Normal file
23
src/routes/api/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
import { getProfileRouteHandler, patchProfileRouteHandler, getDashboardRouteHandler } from "../../services/api/index.js";
|
||||||
|
|
||||||
|
// get main dashboard info profile
|
||||||
|
router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getDashboardRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// get user's profile
|
||||||
|
router.get("/user", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getProfileRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update user's profile
|
||||||
|
router.patch("/", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
patchProfileRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
59
src/routes/auth/index.js
Normal file
59
src/routes/auth/index.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAuthModesHandler,
|
||||||
|
forgotPasswordRouteHandler,
|
||||||
|
loginRouteHandler,
|
||||||
|
registerPasskeyRouteHandler,
|
||||||
|
loginPasskeyRouteHandler,
|
||||||
|
registerRouteHandler,
|
||||||
|
resetPasswordRouteHandler,
|
||||||
|
validateTokenRouteHandler,
|
||||||
|
} from "../../services/auth/index.js";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post("/modes", async (req, res, next) => {
|
||||||
|
const { email } = req.body;
|
||||||
|
await getAuthModesHandler(req, res, email);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/login", async (req, res, next) => {
|
||||||
|
const { email, password } = req.body;
|
||||||
|
await loginRouteHandler(req, res, email, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/validate-token", async (req, res, next) => {
|
||||||
|
const { token } = req.body;
|
||||||
|
await validateTokenRouteHandler(req, res, token);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/logout", (req, res) => {
|
||||||
|
return res.sendStatus(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/register", async (req, res) => {
|
||||||
|
const { name, email, password } = req.body;
|
||||||
|
await registerRouteHandler(req, res, name, email, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/passkey/register", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
await registerPasskeyRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/passkey/login", async (req, res) => {
|
||||||
|
const { email, attestationResponse } = req.body;
|
||||||
|
await loginPasskeyRouteHandler(req, res, email, attestationResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/password-forgot", async (req, res) => {
|
||||||
|
const { email } = req.body;
|
||||||
|
await forgotPasswordRouteHandler(req, res, email);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/password-reset", async (req, res) => {
|
||||||
|
await resetPasswordRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
47
src/routes/fillaments/index.js
Normal file
47
src/routes/fillaments/index.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { parseStringIfNumber } from '../../util/index.js'
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
import { listFillamentsRouteHandler, getFillamentRouteHandler, editFillamentRouteHandler, newFillamentRouteHandler } from "../../services/fillaments/index.js";
|
||||||
|
|
||||||
|
// list of fillaments
|
||||||
|
router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
const { page, limit, property } = req.query;
|
||||||
|
|
||||||
|
const allowedFilters = [
|
||||||
|
'type',
|
||||||
|
'brand',
|
||||||
|
'diameter',
|
||||||
|
'color'
|
||||||
|
]
|
||||||
|
|
||||||
|
const filter = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(req.query)) {
|
||||||
|
for (var i = 0; i < allowedFilters.length; i++) {
|
||||||
|
if (key == allowedFilters[i]) {
|
||||||
|
filter[key] = parseStringIfNumber(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
listFillamentsRouteHandler(req, res, page, limit, property, filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
newFillamentRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getFillamentRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update printer info
|
||||||
|
router.put("/:id", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
editFillamentRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
23
src/routes/gcodefiles/index.js
Normal file
23
src/routes/gcodefiles/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
import { listGCodeFilesRouteHandler, getGCodeFileRouteHandler, editGCodeFileRouteHandler } from "../../services/gcodefiles/index.js";
|
||||||
|
|
||||||
|
// list of printers
|
||||||
|
router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
const { page, limit } = req.body;
|
||||||
|
listGCodeFilesRouteHandler(req, res, page, limit);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getGCodeFileRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update printer info
|
||||||
|
router.put("/:id", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
editGCodeFileRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
9
src/routes/index.js
Normal file
9
src/routes/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import userRoutes from './users/index.js';
|
||||||
|
import apiRoutes from './api/index.js';
|
||||||
|
import authRoutes from './auth/index.js';
|
||||||
|
import printerRoutes from './printers/index.js';
|
||||||
|
import printJobRoutes from './printjobs/index.js';
|
||||||
|
import gcodeFileRoutes from './gcodefiles/index.js'
|
||||||
|
import fillamentRoutes from './fillaments/index.js'
|
||||||
|
|
||||||
|
export { userRoutes, apiRoutes, authRoutes, printerRoutes, printJobRoutes, gcodeFileRoutes, fillamentRoutes };
|
||||||
25
src/routes/printers/index.js
Normal file
25
src/routes/printers/index.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
import { listPrintersRouteHandler, editPrinterRouteHandler, getPrinterRouteHandler } from "../../services/printers/index.js";
|
||||||
|
|
||||||
|
// list of printers
|
||||||
|
router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
const { page, limit } = req.body;
|
||||||
|
listPrintersRouteHandler(req, res, page, limit);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:remoteAddress", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getPrinterRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update printer info
|
||||||
|
router.put("/:remoteAddress", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
editPrinterRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default router;
|
||||||
23
src/routes/printjobs/index.js
Normal file
23
src/routes/printjobs/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import express from "express";
|
||||||
|
import passport from "passport";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
import { listPrintJobsRouteHandler, getPrintJobRouteHandler, editPrintJobRouteHandler } from "../../services/printjobs/index.js";
|
||||||
|
|
||||||
|
// list of printers
|
||||||
|
router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
const { page, limit } = req.body;
|
||||||
|
listPrintJobsRouteHandler(req, res, page, limit);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:jobNumber", passport.authenticate('jwt',{session: false}), (req, res) => {
|
||||||
|
getPrintJobRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update printer info
|
||||||
|
router.put("/:jobNumber", passport.authenticate('jwt',{session: false}), async (req, res) => {
|
||||||
|
editPrintJobRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
22
src/routes/users/index.js
Normal file
22
src/routes/users/index.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import express from 'express';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
res.send({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Smith',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
firstName: 'Stacey',
|
||||||
|
lastName: 'Smith',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
23
src/schemas/fillament.schema.js
Normal file
23
src/schemas/fillament.schema.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const fillamentSchema = 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 },
|
||||||
|
brand: { required: true, type: String },
|
||||||
|
type: { required: true, type: String },
|
||||||
|
price: { required: true, type: Number },
|
||||||
|
diameter: { required: true, type: Number },
|
||||||
|
created_at: { required: true, type: Date },
|
||||||
|
updated_at: { required: true, type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
fillamentSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
fillamentSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const fillamentModel = mongoose.model("Fillament", fillamentSchema);
|
||||||
22
src/schemas/gcodefile.schema.js
Normal file
22
src/schemas/gcodefile.schema.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const gcodeFileSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
gcodeFileName: { required: true, type: String },
|
||||||
|
size: { type: Number, required: false },
|
||||||
|
lines: { type: Number, required: false },
|
||||||
|
fillament: { type: Schema.Types.ObjectId, ref: 'Fillament', required: true },
|
||||||
|
image: { type: Buffer, required: false },
|
||||||
|
printTimeMins: { type: Number, required: false },
|
||||||
|
created_at: { type: Date },
|
||||||
|
updated_at: { type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const gcodeFileModel = mongoose.model("GCodeFile", gcodeFileSchema);
|
||||||
15
src/schemas/passwordResets.schema.js
Normal file
15
src/schemas/passwordResets.schema.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const passwordResetSchema = new mongoose.Schema({
|
||||||
|
email: { required: true, type: String },
|
||||||
|
token: { required: true, type: String },
|
||||||
|
created_at: { type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
passwordResetSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
passwordResetSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const passwordResetModel = mongoose.model("PasswordReset", passwordResetSchema);
|
||||||
22
src/schemas/printer.schema.js
Normal file
22
src/schemas/printer.schema.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const printerSchema = new mongoose.Schema({
|
||||||
|
friendlyName: { required: true, type: String },
|
||||||
|
online: { required: true, type: Boolean },
|
||||||
|
status: {
|
||||||
|
type: { required: true, type: String },
|
||||||
|
percent: { required: false, type: Number },
|
||||||
|
},
|
||||||
|
remoteAddress: { required: true, type: String },
|
||||||
|
hostId: { required: true, type: String },
|
||||||
|
connectedAt: { required: true, type: Date },
|
||||||
|
loadedFillament: { required: true, type: Object }
|
||||||
|
});
|
||||||
|
|
||||||
|
printerSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
printerSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const printerModel = mongoose.model("Printer", printerSchema);
|
||||||
21
src/schemas/printjob.schema.js
Normal file
21
src/schemas/printjob.schema.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const printJobSchema = new mongoose.Schema({
|
||||||
|
status: {
|
||||||
|
type: { required: true, type: String },
|
||||||
|
printer: { type: Schema.Types.ObjectId, ref: 'Printer', required: false },
|
||||||
|
},
|
||||||
|
created_at: { required: true, type: Date },
|
||||||
|
updated_at: { required: true, type: Date },
|
||||||
|
started_at: { required: true, type: Date },
|
||||||
|
gcode_file: { type: Schema.Types.ObjectId, ref: 'GCodeFile', required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
printJobSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
printJobSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const printJobModel = mongoose.model("PrintJob", printJobSchema);
|
||||||
28
src/schemas/user.schema.js
Normal file
28
src/schemas/user.schema.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Binary } from "mongodb";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const userSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
email: { required: true, type: String },
|
||||||
|
email_verified_at: { type: Date },
|
||||||
|
password: { required: true, type: String },
|
||||||
|
webAuthnCredentials: [{
|
||||||
|
id: String,
|
||||||
|
publicKey: Buffer,
|
||||||
|
counter: Number,
|
||||||
|
deviceType: String,
|
||||||
|
backedUp: Boolean,
|
||||||
|
transports: [String]
|
||||||
|
}],
|
||||||
|
profile_image: { type: String },
|
||||||
|
created_at: { type: Date },
|
||||||
|
updated_at: { type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const userModel = mongoose.model("User", userSchema);
|
||||||
92
src/services/api/index.js
Normal file
92
src/services/api/index.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import { userModel } from "../../schemas/user.schema.js";
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export const getDashboardRouteHandler = (req, res) => {
|
||||||
|
const sentData = {
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
res.send(sentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProfileRouteHandler = (req, res) => {
|
||||||
|
const meUser = req.user;
|
||||||
|
|
||||||
|
const stringId = req.user.id;
|
||||||
|
const decId = stringId.substring(4, 8);
|
||||||
|
const intId = parseInt(decId, 16);
|
||||||
|
|
||||||
|
const sentData = {
|
||||||
|
data: {
|
||||||
|
type: 'users',
|
||||||
|
id: intId === 1 ? intId : meUser.id,
|
||||||
|
attributes: {
|
||||||
|
name: meUser.name,
|
||||||
|
email: meUser.email,
|
||||||
|
profile_image: null,
|
||||||
|
createdAt: meUser.createdAt,
|
||||||
|
updateAt: meUser.updateAt
|
||||||
|
},
|
||||||
|
links: {
|
||||||
|
self: `${process.env.APP_URL_API}/users/${meUser.id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(sentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const patchProfileRouteHandler = async (req, res) => {
|
||||||
|
const currentDataOfUser = req.user;
|
||||||
|
const { name, email, newPassword, confirmPassword } = req.body.data.attributes;
|
||||||
|
const foundUser = await userModel.findOne({ email: currentDataOfUser.email});
|
||||||
|
|
||||||
|
if (!foundUser) {
|
||||||
|
res.status(400).json({error: 'No user matches the credentials'});
|
||||||
|
} else {
|
||||||
|
// check password more than 8 characters, new password matched the password confirmation
|
||||||
|
if (newPassword && newPassword < 7 || newPassword != confirmPassword) {
|
||||||
|
res.status(400).json({errors: { password: ["The password should have at lest 8 characters and match the password confirmation."] }});
|
||||||
|
} else if (newPassword && newPassword > 7 && newPassword == confirmPassword) {
|
||||||
|
const salt = await bcrypt.genSalt(10);
|
||||||
|
const hashPassword = await bcrypt.hash(newPassword, salt);
|
||||||
|
try{
|
||||||
|
await userModel.updateOne( { email: foundUser.email }, { $set :{ "name": name, "email": email, "password": hashPassword } });
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
const sentData = {
|
||||||
|
data: {
|
||||||
|
type: 'users',
|
||||||
|
id: foundUser.id,
|
||||||
|
attributes: {
|
||||||
|
name: name,
|
||||||
|
email: email,
|
||||||
|
profile_image: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(sentData);
|
||||||
|
} else if (!newPassword) {
|
||||||
|
try {
|
||||||
|
await userModel.updateOne( { email: foundUser.email }, { $set :{ "name": name, "email": email } });
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
const sentData = {
|
||||||
|
data: {
|
||||||
|
type: 'users',
|
||||||
|
id: foundUser.id,
|
||||||
|
attributes: {
|
||||||
|
name: name,
|
||||||
|
email: email,
|
||||||
|
profile_image: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(sentData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
376
src/services/auth/index.js
Normal file
376
src/services/auth/index.js
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import nodemailer from "nodemailer";
|
||||||
|
import randomToken from "random-token";
|
||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import url from "url";
|
||||||
|
import { userModel } from "../../schemas/user.schema.js";
|
||||||
|
import { passwordResetModel } from "../../schemas/passwordResets.schema.js";
|
||||||
|
import {
|
||||||
|
generateRegistrationOptions,
|
||||||
|
verifyRegistrationResponse,
|
||||||
|
generateAuthenticationOptions,
|
||||||
|
verifyAuthenticationResponse,
|
||||||
|
} from "@simplewebauthn/server";
|
||||||
|
import { isoUint8Array } from "@simplewebauthn/server/helpers";
|
||||||
|
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("Auth");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
let challenges = {};
|
||||||
|
|
||||||
|
const rpName = "Farm Control";
|
||||||
|
const rpID = url.parse(process.env.APP_URL_CLIENT).host;
|
||||||
|
const origin = `https://${rpID}`;
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: "smtp.mailtrap.io",
|
||||||
|
port: 2525,
|
||||||
|
auth: {
|
||||||
|
user: process.env.MAILTRAP_USER,
|
||||||
|
pass: process.env.MAILTRAP_PASSWORD,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function generateToken() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getAuthModesHandler = async (req, res, email) => {
|
||||||
|
let foundUser = await userModel.findOne({ email: email });
|
||||||
|
if (foundUser == null) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: "Invalid email address.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (foundUser.webAuthnCredentials.length > 0) {
|
||||||
|
return res.status(200).json({
|
||||||
|
authModes: ["password", "passkey"],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return res.status(200).json({
|
||||||
|
authModes: ["password"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loginRouteHandler = async (req, res, email, password) => {
|
||||||
|
//Check If User Exists
|
||||||
|
let foundUser = await userModel.findOne({ email: email });
|
||||||
|
if (foundUser == null) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: "Invalid credentials.",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const validPassword = await bcrypt.compare(password, foundUser.password);
|
||||||
|
if (validPassword) {
|
||||||
|
// Generate JWT token
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ id: foundUser.id, email: foundUser.email },
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{
|
||||||
|
expiresIn: "24h",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return res.json({
|
||||||
|
user: {
|
||||||
|
id: foundUser.id,
|
||||||
|
name: foundUser.name,
|
||||||
|
email: foundUser.email,
|
||||||
|
},
|
||||||
|
access_token: token,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: "Invalid credentials.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateTokenRouteHandler = async (req, res, token) => {
|
||||||
|
try {
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
res.status(200).send({
|
||||||
|
status: "OK",
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Token verification error:", err);
|
||||||
|
res.status(401).send("Invalid token");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerPasskeyRouteHandler = async (req, res) => {
|
||||||
|
// check to see if the request has provided a user
|
||||||
|
const user = req.user;
|
||||||
|
if (!user) {
|
||||||
|
// if no user exists
|
||||||
|
return res.status(400).json({ error: "User not specified." });
|
||||||
|
}
|
||||||
|
if (req.body.token) {
|
||||||
|
const options = await generateRegistrationOptions({
|
||||||
|
rpName: rpName,
|
||||||
|
rpID: rpID,
|
||||||
|
userName: user.email,
|
||||||
|
userDisplayName: user.name,
|
||||||
|
excludeCredentials: user.webAuthnCredentials.map(
|
||||||
|
(webAuthnCredential) => ({
|
||||||
|
id: webAuthnCredential.id,
|
||||||
|
transports: webAuthnCredential.transports,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
attestationType: "none",
|
||||||
|
authenticatorSelection: {
|
||||||
|
residentKey: "preferred",
|
||||||
|
userVerification: "preferred",
|
||||||
|
authenticatorAttachment: "platform",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
challenges[user.id] = options.challenge;
|
||||||
|
return res.status(200).send(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedChallenge = challenges[user.id];
|
||||||
|
const attestationResponse = req.body;
|
||||||
|
|
||||||
|
let verification;
|
||||||
|
|
||||||
|
try {
|
||||||
|
verification = await verifyRegistrationResponse({
|
||||||
|
response: attestationResponse,
|
||||||
|
expectedChallenge,
|
||||||
|
expectedOrigin: process.env.APP_URL_CLIENT,
|
||||||
|
expectedRPID: url.parse(process.env.APP_URL_CLIENT).host,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { registrationInfo } = verification;
|
||||||
|
const {
|
||||||
|
credentialID,
|
||||||
|
credentialPublicKey,
|
||||||
|
counter,
|
||||||
|
credentialDeviceType,
|
||||||
|
credentialBackedUp,
|
||||||
|
} = registrationInfo;
|
||||||
|
|
||||||
|
const webAuthnCredential = {
|
||||||
|
id: credentialID,
|
||||||
|
publicKey: Buffer.from(new Uint8Array(credentialPublicKey)),
|
||||||
|
counter,
|
||||||
|
deviceType: credentialDeviceType,
|
||||||
|
backedUp: credentialBackedUp,
|
||||||
|
transports: attestationResponse.response.transports,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(webAuthnCredential);
|
||||||
|
user.webAuthnCredentials.push(webAuthnCredential);
|
||||||
|
|
||||||
|
await user.save();
|
||||||
|
res.status(200).send({ status: "OK" });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verification.verified) {
|
||||||
|
} else {
|
||||||
|
res.status(400).send({ error: "Not verified." });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loginPasskeyRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
email,
|
||||||
|
attestationResponse
|
||||||
|
) => {
|
||||||
|
if (!email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let user = await userModel.findOne({ email: email });
|
||||||
|
if (user == null) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: "Invalid email address.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (attestationResponse) {
|
||||||
|
logger.info("Verfifying challenge...");
|
||||||
|
const expectedChallenge = challenges[user.id];
|
||||||
|
let verification;
|
||||||
|
try {
|
||||||
|
const webAuthnCredentialIndex = user.webAuthnCredentials.findIndex(
|
||||||
|
(cred) => cred.id === attestationResponse.id
|
||||||
|
);
|
||||||
|
const webAuthnCredential = user.webAuthnCredentials[webAuthnCredentialIndex];
|
||||||
|
verification = await verifyAuthenticationResponse({
|
||||||
|
response: attestationResponse,
|
||||||
|
expectedChallenge,
|
||||||
|
expectedOrigin: process.env.APP_URL_CLIENT,
|
||||||
|
expectedRPID: url.parse(process.env.APP_URL_CLIENT).host,
|
||||||
|
authenticator: {
|
||||||
|
credentialID: webAuthnCredential.id,
|
||||||
|
credentialPublicKey: new Uint8Array(webAuthnCredential.publicKey),
|
||||||
|
counter: webAuthnCredential.counter,
|
||||||
|
transports: webAuthnCredential.transports,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
user.webAuthnCredentials[webAuthnCredentialIndex].counter = verification.authenticationInfo.newCounter; // Update connection counter
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
// Generate JWT token
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ id: user.id, email: user.email },
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{
|
||||||
|
expiresIn: "24h",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
},
|
||||||
|
access_token: token,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(400).send({ error });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get options
|
||||||
|
logger.info("Sending authentication options...");
|
||||||
|
const options = await generateAuthenticationOptions({
|
||||||
|
rpID: url.parse(process.env.APP_URL_CLIENT).host,
|
||||||
|
allowCredentials: user.webAuthnCredentials.map((cred) => ({
|
||||||
|
id: cred.id,
|
||||||
|
type: "public-key",
|
||||||
|
transports: cred.transports,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
challenges[user.id] = options.challenge;
|
||||||
|
res.status(200).send(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerRouteHandler = async (req, res, name, email, password) => {
|
||||||
|
// check if user already exists
|
||||||
|
let foundUser = await userModel.findOne({ email: email });
|
||||||
|
if (foundUser) {
|
||||||
|
// does not get the error
|
||||||
|
return res.status(400).json({ message: "Email is already in use" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// check password to exist and be at least 8 characters long
|
||||||
|
if (!password || password.length < 8) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ message: "Password must be at least 8 characters long." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash password to save in db
|
||||||
|
const salt = await bcrypt.genSalt(10);
|
||||||
|
const hashPassword = await bcrypt.hash(password, salt);
|
||||||
|
|
||||||
|
const newUser = new userModel({
|
||||||
|
name: name,
|
||||||
|
email: email,
|
||||||
|
password: hashPassword,
|
||||||
|
});
|
||||||
|
await newUser.save();
|
||||||
|
|
||||||
|
// Generate JWT token
|
||||||
|
const token = jwt.sign({ id: newUser.id, email: newUser.email }, "token", {
|
||||||
|
expiresIn: "24h",
|
||||||
|
});
|
||||||
|
return res.status(200).json({
|
||||||
|
token_type: "Bearer",
|
||||||
|
expires_in: "24h",
|
||||||
|
access_token: token,
|
||||||
|
refresh_token: token,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const forgotPasswordRouteHandler = async (req, res, email) => {
|
||||||
|
let foundUser = await userModel.findOne({ email: email });
|
||||||
|
|
||||||
|
if (!foundUser) {
|
||||||
|
return res.status(400).json({
|
||||||
|
errors: { email: ["The email does not match any existing user."] },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let token = randomToken(20);
|
||||||
|
// send mail with defined transport object
|
||||||
|
let info = await transporter.sendMail({
|
||||||
|
from: "admin@jsonapi.com", // sender address
|
||||||
|
to: email, // list of receivers
|
||||||
|
subject: "Reset Password", // Subject line
|
||||||
|
html: `<p>You requested to change your password.If this request was not made by you please contact us. Access <a href='${process.env.APP_URL_CLIENT}/auth/reset-password?token=${token}&email=${email}'>this link</a> to reste your password </p>`, // html body
|
||||||
|
});
|
||||||
|
const dataSent = {
|
||||||
|
data: "password-forgot",
|
||||||
|
attributes: {
|
||||||
|
redirect_url: `${process.env.APP_URL_API}/password-reset`,
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// save token in db
|
||||||
|
await passwordResetModel.create({
|
||||||
|
email: foundUser.email,
|
||||||
|
token: token,
|
||||||
|
created_at: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(204).json(dataSent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetPasswordRouteHandler = async (req, res) => {
|
||||||
|
const foundUser = await userModel.findOne({
|
||||||
|
email: req.body.data.attributes.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!foundUser || !foundToken) {
|
||||||
|
return res.status(400).json({
|
||||||
|
errors: {
|
||||||
|
email: ["The email or token does not match any existing user."],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { password, password_confirmation } = req.body.data.attributes;
|
||||||
|
// validate password
|
||||||
|
if (password.length < 8) {
|
||||||
|
return res.status(400).json({
|
||||||
|
errors: {
|
||||||
|
password: ["The password should have at lest 8 characters."],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password != password_confirmation) {
|
||||||
|
return res.status(400).json({
|
||||||
|
errors: {
|
||||||
|
password: ["The password and password confirmation must match."],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const salt = await bcrypt.genSalt(10);
|
||||||
|
const hashPassword = await bcrypt.hash(password, salt);
|
||||||
|
|
||||||
|
await passwordResetModel.deleteOne({ email: foundUser.email });
|
||||||
|
|
||||||
|
await userModel.updateOne(
|
||||||
|
{ email: foundUser.email },
|
||||||
|
{ $set: { password: hashPassword } }
|
||||||
|
);
|
||||||
|
return res.sendStatus(204);
|
||||||
|
}
|
||||||
|
};
|
||||||
121
src/services/fillaments/index.js
Normal file
121
src/services/fillaments/index.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import { fillamentModel } from "../../schemas/fillament.schema.js"
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("Fillaments");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
export const listFillamentsRouteHandler = async (req, res, page = 1, limit = 25, property = "", filter = {}) => {
|
||||||
|
try {
|
||||||
|
// Calculate the skip value based on the page number and limit
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
|
||||||
|
let fillament;
|
||||||
|
let aggregateCommand = [];
|
||||||
|
|
||||||
|
if (filter != {}) { // use filtering if present
|
||||||
|
aggregateCommand.push({ $match: filter });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }});
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateCommand.push({ $skip: skip });
|
||||||
|
aggregateCommand.push({ $limit: Number(limit) });
|
||||||
|
|
||||||
|
console.log(aggregateCommand)
|
||||||
|
|
||||||
|
fillament = await fillamentModel.aggregate(aggregateCommand)
|
||||||
|
|
||||||
|
logger.trace(`List of filaments (Page ${page}, Limit ${limit}, Property ${property}):`, fillament);
|
||||||
|
res.send(fillament);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error listing filaments:", error);
|
||||||
|
res.status(500).send({ error: error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFillamentRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the fillament with the given remote address
|
||||||
|
const fillament = await fillamentModel.findOne({
|
||||||
|
_id: id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!fillament) {
|
||||||
|
logger.warn(`Fillament not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Fillament with ID: ${id}:`, fillament);
|
||||||
|
res.send(fillament);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching Fillament:", error);
|
||||||
|
res.status(500).send({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editFillamentRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the fillament with the given remote address
|
||||||
|
const fillament = await fillamentModel.findOne({ _id: id });
|
||||||
|
|
||||||
|
if (!fillament) { // Error handling
|
||||||
|
logger.warn(`Fillament not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Fillament with ID: ${id}:`, fillament);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { created_at, updated_at, started_at, status, ...updateData } = req.body;
|
||||||
|
|
||||||
|
const result = await fillamentModel.updateOne(
|
||||||
|
{ _id: id },
|
||||||
|
{ $set: updateData }
|
||||||
|
);
|
||||||
|
if (result.nModified === 0) {
|
||||||
|
logger.error("No Fillament updated.");
|
||||||
|
res.status(500).send({ error: "No fillaments updated." });
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating fillament:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
res.send("OK");
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error("Error fetching fillament:", fetchError);
|
||||||
|
res.status(500).send({ error: fetchError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const newFillamentRouteHandler = async (req, res) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
let { ...newFillament } = req.body;
|
||||||
|
newFillament = { ...newFillament, created_at: new Date(), updated_at: new Date() }
|
||||||
|
|
||||||
|
const result = await fillamentModel.create(newFillament);
|
||||||
|
if (result.nCreated === 0) {
|
||||||
|
logger.error("No fillament created.");
|
||||||
|
res.status(500).send({ error: "No fillament created." });
|
||||||
|
}
|
||||||
|
res.status(200).send({ status: "ok" });
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating fillament:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
93
src/services/gcodefiles/index.js
Normal file
93
src/services/gcodefiles/index.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import { gcodeFileModel } from "../../schemas/gcodefile.schema.js"
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("GCodeFiles");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
export const listGCodeFilesRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Fetch gcode files and group
|
||||||
|
const gcodeFiles = await gcodeFileModel.aggregate([
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$status",
|
||||||
|
totalQuantity: { $sum: "$quantity" },
|
||||||
|
totalPrice: { $sum: "$price" },
|
||||||
|
orders: { $push: "$$ROOT" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
logger.trace(`List of print jobs (Page ${page}, Limit ${limit}):`);
|
||||||
|
res.send(gcodeFile);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error listing print jobs:", error);
|
||||||
|
res.status(500).send({ error: error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGCodeFileRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the gcodeFile with the given remote address
|
||||||
|
const gcodeFile = await gcodeFileModel.findOne({
|
||||||
|
_id: id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!gcodeFile) {
|
||||||
|
logger.warn(`GCodeFile not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`GCodeFile with ID: ${id}:`, gcodeFile);
|
||||||
|
res.send(gcodeFile);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching GCodeFile:", error);
|
||||||
|
res.status(500).send({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editGCodeFileRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the gcodeFile with the given remote address
|
||||||
|
const gcodeFile = await gcodeFileModel.findOne({ _id: id });
|
||||||
|
|
||||||
|
if (!gcodeFile) { // Error handling
|
||||||
|
logger.warn(`GCodeFile not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`GCodeFile with ID: ${id}:`, gcodeFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { created_at, updated_at, started_at, status, ...updateData } = req.body;
|
||||||
|
|
||||||
|
const result = await gcodeFileModel.updateOne(
|
||||||
|
{ _id: id },
|
||||||
|
{ $set: updateData }
|
||||||
|
);
|
||||||
|
if (result.nModified === 0) {
|
||||||
|
logger.error("No gcodeFile updated.");
|
||||||
|
res.status(500).send({ error: "No gcodeFiles updated." });
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating gcodeFile:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
res.send("OK");
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error("Error fetching gcodeFile:", fetchError);
|
||||||
|
res.status(500).send({ error: fetchError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
86
src/services/printers/index.js
Normal file
86
src/services/printers/index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import { userModel } from "../../schemas/user.schema.js";
|
||||||
|
import { printerModel } from "../../schemas/printer.schema.js";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("Printers");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
export const listPrintersRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
page = 1,
|
||||||
|
limit = 25
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Calculate the skip value based on the page number and limit
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
// Fetch users with pagination
|
||||||
|
const printers = await printerModel.find().skip(skip).limit(limit);
|
||||||
|
|
||||||
|
logger.trace(`List of printers (Page ${page}, Limit ${limit}):`);
|
||||||
|
res.send(printers);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error listing users:", error);
|
||||||
|
res.status(500).send({ error: error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPrinterRouteHandler = async (req, res) => {
|
||||||
|
const remoteAddress = req.params.remoteAddress;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch the printer with the given remote address
|
||||||
|
const printer = await printerModel.findOne({ remoteAddress });
|
||||||
|
|
||||||
|
if (!printer) {
|
||||||
|
logger.warn(`Printer with remote address ${remoteAddress} not found.`);
|
||||||
|
return res.status(404).send({ error: "Printer not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Printer with remote address ${remoteAddress}:`, printer);
|
||||||
|
res.send(printer);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching printer:", error);
|
||||||
|
res.status(500).send({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editPrinterRouteHandler = async (req, res) => {
|
||||||
|
const remoteAddress = req.params.remoteAddress;
|
||||||
|
const { friendlyName } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch the printer with the given remote address
|
||||||
|
const printer = await printerModel.findOne({ remoteAddress });
|
||||||
|
|
||||||
|
if (!printer) {
|
||||||
|
logger.warn(`Printer with remote address ${remoteAddress} not found.`);
|
||||||
|
return res.status(404).send({ error: "Printer not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`Editing printer with remote address ${remoteAddress}:`, printer);
|
||||||
|
try {
|
||||||
|
const result = await printerModel.updateOne(
|
||||||
|
{ remoteAddress: remoteAddress },
|
||||||
|
{ $set: req.body }
|
||||||
|
);
|
||||||
|
if (result.nModified === 0) {
|
||||||
|
logger.error("No printers updated.");
|
||||||
|
res.status(500).send({ error: "No printers updated." });
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating printer:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
res.send("OK");
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error("Error fetching printer:", fetchError);
|
||||||
|
res.status(500).send({ error: fetchError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
88
src/services/printjobs/index.js
Normal file
88
src/services/printjobs/index.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import { printJobModel } from "../../schemas/printjob.schema.js"
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import log4js from "log4js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const logger = log4js.getLogger("PrintJobs");
|
||||||
|
logger.level = process.env.LOG_LEVEL;
|
||||||
|
|
||||||
|
export const listPrintJobsRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
page = 1,
|
||||||
|
limit = 25
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Calculate the skip value based on the page number and limit
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
// Fetch users with pagination
|
||||||
|
const printJobs = await printJobModel.find().skip(skip).limit(limit);
|
||||||
|
|
||||||
|
logger.trace(`List of print jobs (Page ${page}, Limit ${limit}):`);
|
||||||
|
res.send(printJobs);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error listing print jobs:", error);
|
||||||
|
res.status(500).send({ error: error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPrintJobRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the printJob with the given remote address
|
||||||
|
const printJob = await printJobModel.findOne({
|
||||||
|
_id: id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!printJob) {
|
||||||
|
logger.warn(`PrintJob not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`PrintJob with ID: ${id}:`, printJob);
|
||||||
|
res.send(printJob);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching printJob:", error);
|
||||||
|
res.status(500).send({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editPrintJobRouteHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Get ID from params
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
// Fetch the printJob with the given remote address
|
||||||
|
const printJob = await printJobModel.findOne({ _id: id });
|
||||||
|
|
||||||
|
if (!printJob) { // Error handling
|
||||||
|
logger.warn(`PrintJob not found with supplied id.`);
|
||||||
|
return res.status(404).send({ error: "Print job not found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace(`PrintJob with ID: ${id}:`, printJob);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { created_at, updated_at, started_at, status, ...updateData } = req.body;
|
||||||
|
|
||||||
|
const result = await printJobModel.updateOne(
|
||||||
|
{ _id: id },
|
||||||
|
{ $set: updateData }
|
||||||
|
);
|
||||||
|
if (result.nModified === 0) {
|
||||||
|
logger.error("No printJobs updated.");
|
||||||
|
res.status(500).send({ error: "No printJobs updated." });
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.error("Error updating printJob:", updateError);
|
||||||
|
res.status(500).send({ error: updateError.message });
|
||||||
|
}
|
||||||
|
res.send("OK");
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error("Error fetching printJob:", fetchError);
|
||||||
|
res.status(500).send({ error: fetchError.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
8
src/util/index.js
Normal file
8
src/util/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
function parseStringIfNumber(input) {
|
||||||
|
if (typeof input === 'string' && !isNaN(input) && !isNaN(parseFloat(input))) {
|
||||||
|
return parseFloat(input);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {parseStringIfNumber};
|
||||||
Loading…
x
Reference in New Issue
Block a user