Commit 628e5bf2 authored by abergavenny's avatar abergavenny
Browse files

Version 1.0.0

parent b463e8cc
import { validationResult } from 'express-validator'
import { success, warning } from '../helpers/index.js'
import { ResponseCode } from '../ENUMS.js'
import { Simulation } from '../db/index.js'
export const getSimulation = async (req, res) => {
const validationErrors = validationResult(req)
if (!validationErrors.isEmpty()) {
return warning(res, { code: ResponseCode.ValidationError })
}
const { id } = req.params
const result = await Simulation.findById({ _id: id })
success(res, { data: result })
}
export const getSimulations = async (req, res) => {
const result = await Simulation.find()
success(res, { data: result })
}
import { validationResult } from 'express-validator'
import { Building, User } from '../db/index.js'
import { getUserFromRequest, success, warning } from '../helpers/index.js'
import { hashString } from '../utils/crypto.js'
import { ResponseCode } from '../ENUMS.js'
export const allowOrRevokeSharing = async (req, res) => {
const validationErrors = validationResult(req)
if (!validationErrors.isEmpty()) {
return warning(res, { code: ResponseCode.ValidationError })
}
const { accessor, role } = getUserFromRequest(req)
const { id } = req.params
const { sharingAllowed } = req.body
if (accessor === id || role === 'administrator') {
const result = await User.findOneAndUpdate({ _id: id, role: 'user' }, {
sharingAllowed,
updatedAt: Date.now()
}, {
new: true,
runValidators: true
}).select('_id sharingAllowed role')
if (result) {
return success(res, { code: ResponseCode.UpdateSuccess })
}
return warning(res, { code: ResponseCode.NotFound })
}
warning(res, { code: ResponseCode.NotAllowed })
}
export const getUser = async (req, res) => {
const validationErrors = validationResult(req)
if (!validationErrors.isEmpty()) {
return warning(res, { code: ResponseCode.ValidationError })
}
const { id } = req.params
const result = await User.findById(id).select('_id email username role sharingData createdAt updatedAt')
if (result) {
return success(res, { data: result })
}
warning(res, { code: ResponseCode.NotFound })
}
export const getUsers = async (req, res) => {
const { accessor } = getUserFromRequest(req)
const building = await Building.find({ owner: accessor })
if (building) {
const buildingIds = building.map(element => element._id)
const result = await User.find({ linkedTo: { $in: buildingIds } }).select('_id email username role sharingData createdAt updatedAt')
if (result) {
return success(res, { data: result })
}
}
warning(res, { code: ResponseCode })
}
export const updatePassword = async (req, res) => {
const validationErrors = validationResult(req)
if (!validationErrors.isEmpty()) {
return warning(res, { code: ResponseCode.ValidationError })
}
const { id } = req.params
const { newPassword } = req.body
const result = await User.findOneAndUpdate({ _id: id }, {
encryptedPassword: hashString(newPassword),
updatedAt: Date.now()
}, {
new: true,
runValidators: true
})
if (result) {
return success(res, { code: ResponseCode.UpdateSuccess })
}
warning(res, { code: ResponseCode.NotFound })
}
export const updateUser = async (req, res) => {
const validationErrors = validationResult(req)
if (!validationErrors.isEmpty()) {
return warning(res, { code: ResponseCode.ValidationError })
}
const { accessor, role } = getUserFromRequest(req)
const { id } = req.params
const { email } = req.body
if (accessor === id || role === 'administrator') {
const result = await User.findOneAndUpdate({ _id: id, role: 'user' }, {
email,
updatedAt: Date.now()
}, {
new: true,
runValidators: true
}).select('_id email role')
if (result) {
return success(res, { code: ResponseCode.UpdateSuccess })
}
return warning(res, { code: ResponseCode.NotFound })
}
warning(res, { code: ResponseCode.NotAllowed })
}
import config from '../config/appConfig.js'
export function getConnectionString () {
if (config.db.uri) {
return config.db.uri
}
return `mongodb://${config.db.user}:${config.db.pass}@${config.db.host}:${config.db.port}/${config.db.name}`
}
export const connectionOptions = {
dbName: config.db.name,
user: config.db.user,
pass: config.db.pass,
serverSelectionTimeoutMS: 3000, // default 30000
autoIndex: true
}
export { apartmentDataSchema } from './models/apartmentData.js' // Initialize before loading buildingSchema
export { buildingDataSchema } from './models/buildingData.js' // Initialize before loading buildingSchema
export { imageSchema } from './models/image.js' // Initialize before loading buildingSchema
export { default as Apartment } from './models/apartment.js'
export { apartmentSchema } from './models/apartment.js'
export { default as Building } from './models/building.js'
export { buildingSchema } from './models/building.js'
export { default as Management } from './models/management.js'
export { managementSchema } from './models/management.js'
export { default as Simulation } from './models/simulation.js'
export { simulationSchema } from './models/simulation.js'
export { default as User } from './models/user.js'
export { userSchema } from './models/user.js'
import mongoose from 'mongoose'
import { apartmentDataSchema, imageSchema } from '../index.js'
export const apartmentSchema = new mongoose.Schema({
name: {
type: String,
default: null
},
data: {
type: apartmentDataSchema,
default: {}
},
image: {
type: imageSchema,
default: {}
},
linkedTo: mongoose.SchemaTypes.ObjectId,
owner: mongoose.SchemaTypes.ObjectId,
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
},
updatedAt: {
type: Date,
default: () => Date.now()
}
})
export default mongoose.model('Apartment', apartmentSchema)
import mongoose from 'mongoose'
export const apartmentDataSchema = new mongoose.Schema({
// Heating
apartmentComment: {
type: String,
default: null
},
area: {
type: Number,
default: null
},
ceilingHeight: {
type: Number,
default: null
},
centralHeating: {
type: Boolean,
default: false
},
heatDemand: {
type: Number,
default: null
},
heatingType: {
type: String,
default: null
},
largeHeatingElements: {
type: Number,
default: null
},
mediumHeatingElements: {
type: Number,
default: null
},
panelHeating: {
type: Number,
default: null
},
powerDemand: {
type: Number,
default: null
},
smallHeatingElements: {
type: Number,
default: null
},
workingHeatingInstallations: {
type: Number,
default: null
},
// Windows
averageWindowAge: {
type: Number,
default: null
},
averageWindowCondition: {
type: String,
default: null
},
largeWindows: {
type: Number,
default: null
},
mediumWindows: {
type: Number,
default: null
},
roofWindows: {
type: Number,
default: null
},
smallWindows: {
type: Number,
default: null
},
windowComment: {
type: String,
default: null
},
windowFrame: {
type: String,
default: null
},
windowGlazing: {
type: String,
default: null
}
}, {
_id: false
})
import mongoose from 'mongoose'
import { apartmentSchema, buildingDataSchema } from '../index.js'
export const buildingSchema = new mongoose.Schema({
prefix: {
type: String,
lowercase: true,
default: null
},
name: {
type: String,
default: null
},
address: {
type: String,
default: null
},
gmlId: {
type: String,
default: null
},
data: {
type: buildingDataSchema,
default: {}
},
apartments: [apartmentSchema],
owner: mongoose.SchemaTypes.ObjectId,
setupCompleted: {
type: Boolean,
default: false
},
createdBy: mongoose.SchemaTypes.ObjectId,
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
},
updatedAt: {
type: Date,
default: () => Date.now()
}
})
export default mongoose.model('Building', buildingSchema)
import mongoose from 'mongoose'
export const buildingDataSchema = new mongoose.Schema({
// Characteristics
yearOfConstruction: {
type: Number,
default: null
},
numberOfFloors: {
type: Number,
default: null
},
livingSpace: {
type: Number,
default: null
},
listedBuilding: {
type: Boolean,
default: null
},
energyPerformanceCertificate: {
type: Number,
default: null
},
characteristicsComment: {
type: String,
default: null
},
// Heating
selfContainedCentralHeating: {
type: Boolean,
default: null
},
heatingInstallation: {
type: String,
default: null
},
heatingConsumption: {
type: Number,
default: null
},
pipeSystem: {
type: String,
default: null
},
solarHeat: {
type: Boolean,
default: null
},
solarHeatArea: {
type: Number,
default: null
},
photovoltaic: {
type: Boolean,
default: null
},
photovoltaicArea: {
type: Number,
default: null
},
photovoltaicYield: {
type: Number,
default: null
},
heatingInstallationComment: {
type: String,
default: null
},
// Refurbishment: facade
facadeInsulatingMaterial: {
type: String,
default: null
},
facadeInsulatingMaterialThickness: {
type: Number,
default: null
},
buildingStructure: {
type: String,
default: null
},
buildingStructureThickness: {
type: Number,
default: null
},
facadeNorth: {
type: Boolean,
default: null
},
facadeEast: {
type: Boolean,
default: null
},
facadeWest: {
type: Boolean,
default: null
},
facadeSouth: {
type: Boolean,
default: null
},
facadeRefurbishmentComment: {
type: String,
default: null
},
// Refurbishment: roof
flatRoof: {
type: Boolean,
default: null
},
roofArea: {
type: Number,
default: null
},
heatedAttic: {
type: Boolean,
default: null
},
clouding: {
type: Number,
default: null
},
roofInsulatingMaterial: {
type: String,
default: null
},
roofInsulatingMaterialThickness: {
type: Number,
default: null
},
roofRefurbishmentComment: {
type: String,
default: null
},
// Refurbishment: basement
basementInsulatingMaterial: {
type: String,
default: null
},
basementInsulatingMaterialThickness: {
type: Number,
default: null
},
insulatedBasementCeiling: {
type: Boolean,
default: null
},
insulatedBasementFloor: {
type: Boolean,
default: null
},
heatedBasement: {
type: Boolean,
default: null
},
basementRefurbishmentComment: {
type: String,
default: null
}
}, {
_id: false
})
export default null
import mongoose from 'mongoose'
export const imageSchema = new mongoose.Schema({
name: {
type: String,
default: null
},
originalName: {
type: String,
default: null
},
mimeType: {
type: String,
default: null
},
size: {
type: Number,
default: 0
},
buffer: {
data: Buffer,
contentType: String
},
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
},
updatedAt: {
type: Date,
default: () => Date.now()
}
})
export default mongoose.model('Image', imageSchema)
import mongoose from 'mongoose'
export const managementSchema = new mongoose.Schema({
prefixList: {
type: Array
},
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
}
}, {
collection: 'management'
})
managementSchema.statics.initManagementSettings = async function (...args) {
const exists = await this.find()
if (exists.length !== 0) {
return null
}
return await this.create({ prefixList: [] })
}
export default mongoose.model('Management', managementSchema)
import mongoose from 'mongoose'
export const simulationSchema = new mongoose.Schema({
building: mongoose.SchemaTypes.ObjectId,
gmlId: {
type: String,
default: null
},
prefetched: {
type: Object,
default: {}
},
result: {
type: Object,
default: {}
},
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
}
})
export default mongoose.model('Simulation', simulationSchema)
import mongoose from 'mongoose'
import { User } from '../index.js'
import { matchEncryptedString } from '../../utils/crypto.js'
export const userSchema = new mongoose.Schema({
email: {
type: String,
immutable: true,
lowercase: true
},
username: {
type: String,
immutable: true
},
encryptedPassword: {
type: String,
minlength: [8, 'TOO_FEW_CHARACTERS'],
required: [true, 'PASSWORD_REQUIRED']
},
role: {
type: String,
required: true
},
linkedTo: mongoose.SchemaTypes.ObjectId,
apartments: [mongoose.SchemaTypes.ObjectId],
sharingAllowed: {
type: Boolean,
default: false
},
lastLogin: {
type: Date,
default: null
},
confirmationToken: {
type: String,
default: null
},
confirmationExpireAt: {
type: Date,
default: null
},
emailConfirmed: {
type: Boolean,
default: false
},
resetToken: {
type: String,
default: null
},
resetExpireAt: {
type: Date,
default: null
},
lastResetRequest: {
type: Date,
default: null
},
createdBy: mongoose.SchemaTypes.ObjectId,
createdAt: {
type: Date,
immutable: true,
default: () => Date.now()
},
updatedAt: {
type: Date,
default: () => Date.now()
}
})
userSchema.methods.comparePassword = async function (password) {
return matchEncryptedString(password, this.password) || false
}
userSchema.statics.verifyPasswordResetAndUpdate = async function (token, data) {
const user = await this.findOne({ resetToken: token })
if (user) {
return await this.findOneAndUpdate({ _id: user._id }, data, {
new: true,
runValidators: true
})
}
return null
}
userSchema.statics.createBuildingUser = async function (accessor, data) {
const isOwner = await this.findOne({ _id: accessor })
if (isOwner) {
const exists = await User.findOne({ username: data.username })
if (exists) return null
return await this.create(data)
}
return null
}
userSchema.statics.createNewAccount = async function (email, data) {
const exists = await User.findOne({ email })
if (exists) return null
const user = await User.create(data)
return user
}
userSchema.statics.findUserAndVerify = async function (emailOrUsername, password) {
const byUsername = await this.findOne({ username: emailOrUsername }).exec()
const byEmail = await this.findOne({ email: emailOrUsername }).exec()
const user = byUsername || byEmail
if (user && matchEncryptedString(password, user.encryptedPassword)) {
return this.findOne({
$or: [{ username: user.username }, { email: user.email || '2ad98ac316a093cd32f2b05296d98e3b' }]
})
}
return null
}
export default mongoose.model('User', userSchema)
import dotenv from 'dotenv'
import dotenvExpand from 'dotenv-expand'
const env = dotenv.config()
dotenvExpand.expand(env)
export const getUserFromRequest = (req) => {
const { role, userId } = req.user
return {
accessor: userId,
role
}
}
export const getUsernameFromEmail = (email) => {
const seq = email.split('@')
return seq[0]
}
export const handleRouteError = (err, res) => {
if (err.name === 'MongoServerError') {
res.status(400).json({ type: 'ERROR', message: 'Bad Request', name: err.message })
} else {
res.status(400).json({ type: 'ERROR', message: 'Bad Request', name: err.name, description: err.message })
}
}
export const success = (res, data, code = 200) => {
res.status(code).json({
status: 'SUCCESS',
...data
})
}
export const warning = (res, data, code = 200) => {
res.status(code).json({
status: 'WARNING',
...data
})
}
import nodemailer from 'nodemailer'
class Mailer {
constructor () {
this._transport = null
this._isTest = false
this._isDisabled = false
}
async init (options = null, mode = null) {
let transportOptions = options
if (mode === null) this._isDisabled = true
const isEmpty = !options?.host || !options?.port || !options?.user || !options?.host
if (isEmpty || !options) {
const account = await nodemailer.createTestAccount()
transportOptions = {
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: account.user,
pass: account.pass
}
}
this._isTest = true
}
this._transport = nodemailer.createTransport(transportOptions)
}
async sendMail (to, subject, message, html = null) {
try {
if (!this._transport) throw new Error('Call Mailer.init()')
const mail = {
from: 'sender@example.com', // DEVINFO Hier die entsprechende Absenderadresse einfügen
to,
subject,
text: message
}
if (this._isDisabled) {
mail.from = 'sender@example.com'
mail.to = 'receiver@example.com'
mail.subject = 'Test Message Subject'
mail.text = 'Test Message Body'
}
const info = await this._transport.sendMail(mail)
console.log('LOG Message:', info)
if (this._isTest) {
console.log('LOG Test Account URL:', nodemailer.getTestMessageUrl(info))
}
} catch (error) {
console.log('ERROR:', error.name)
}
}
}
export default Mailer
import './env.js'
import runServer from './server.js'
runServer()
export default (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next)
}
import passport from 'passport'
import { ExtractJwt, Strategy } from 'passport-jwt'
import config from '../config/appConfig.js'
import { User } from '../db/index.js'
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.jwt.secrect
}
passport.use(new Strategy(options, async (payload, done) => {
try {
const result = await User.findOne({ _id: payload.userId })
if (result) {
const { _id, linkedTo, role } = result
return done(null, {
buildingId: linkedTo.toHexString(),
role,
userId: _id.toHexString(),
valid: true
})
}
return done(null, false)
} catch (err) {
done(err, false)
}
}))
export function setupAuthentication (app) {
app.use(passport.initialize())
}
export default passport.authenticate('jwt', { session: false })
export default async function errorHandler (err, req, res, next) {
if (err) {
console.log('Error - app crash:', err)
}
res.status(400).json({ type: 'ERROR', message: 'Bad Request', name: err.message })
}
import { ALLOWED_ROLES } from '../CONSTANTS.js'
import { getUserFromRequest } from '../helpers/index.js'
export default async function permissionHandler (req, res, next) {
const { role } = getUserFromRequest(req)
if (ALLOWED_ROLES.indexOf(role) === -1) {
res.status(400).json({
status: 'WARNING',
code: 'NOT_ALLOWED'
})
return
}
return next()
}
import express from 'express'
import { checkSchema } from 'express-validator'
import multer from 'multer'
import { createApartment, getApartment, getApartments, getFile, updateFiles, updateHeatingData, updateWindowData } from '../../controllers/apartments.js'
import asyncHandler from '../../middleware/asyncHandler.js'
import permissionHandler from '../../middleware/permissionHandler.js'
import { createApartmentSchema, heatingSchema, windowSchema } from '../../schemas/apartments.js'
import { idSchema } from '../../schemas/index.js'
const router = express.Router()
const storage = multer.memoryStorage()
const fileUpload = multer({
storage,
limits: {
fieldSize: 5000000,
parts: 2
}
})
router.get('/', asyncHandler(getApartments))
router.get('/:id', checkSchema(idSchema), asyncHandler(getApartment))
router.get('/:id/files', checkSchema(idSchema), asyncHandler(getFile))
router.post('/', permissionHandler, checkSchema(createApartmentSchema), asyncHandler(createApartment))
router.put('/:id/heating', checkSchema(heatingSchema), asyncHandler(updateHeatingData))
router.put('/:id/files', fileUpload.single('image'), asyncHandler(updateFiles))
router.put('/:id/windows', checkSchema(windowSchema), asyncHandler(updateWindowData))
export default router
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment