building a foundation and breaking prisma
Co-authored-by: Spacelord <Spacelord09@users.noreply.github.com>
This commit is contained in:
commit
18a4393765
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
config.js
|
||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
config.json
|
||||||
|
dist
|
20
.prettierrc.json
Normal file
20
.prettierrc.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 8,
|
||||||
|
"useTabs": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"embeddedLanguageFormatting": "auto",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"htmlWhitespaceSensitivity": "css",
|
||||||
|
"insertPragma": false,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"printWidth": 200,
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"requirePragma": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
12
allowedStaticPaths.json
Normal file
12
allowedStaticPaths.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"allowedStaticFiles": [
|
||||||
|
"bootstrap-icons/font/bootstrap-icons.css",
|
||||||
|
"bootstrap/dist/css/bootstrap.min.css",
|
||||||
|
"bootstrap/dist/js/bootstrap.bundle.min.js",
|
||||||
|
"jquery/dist/jquery.min.js",
|
||||||
|
"darkreader/darkreader.js",
|
||||||
|
"bootstrap-icons/font/fonts/bootstrap-icons.woff2",
|
||||||
|
"bootstrap/dist/css/bootstrap.min.css.map"
|
||||||
|
],
|
||||||
|
"debugMode": false
|
||||||
|
}
|
52
eslintrc.json
Normal file
52
eslintrc.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"extends": ["eslint:recommended", "prettier"],
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2021
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||||
|
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"comma-spacing": "error",
|
||||||
|
"comma-style": "error",
|
||||||
|
"curly": ["error", "multi-line", "consistent"],
|
||||||
|
"dot-location": ["error", "property"],
|
||||||
|
"handle-callback-err": "off",
|
||||||
|
"indent": ["error", "tab"],
|
||||||
|
"keyword-spacing": "error",
|
||||||
|
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||||
|
"max-statements-per-line": ["error", { "max": 2 }],
|
||||||
|
"no-console": "off",
|
||||||
|
"no-empty-function": "error",
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
"no-inline-comments": "error",
|
||||||
|
"no-lonely-if": "error",
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||||
|
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||||
|
"no-trailing-spaces": ["error"],
|
||||||
|
"no-var": "error",
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"prefer-const": "error",
|
||||||
|
"quotes": ["error", "single"],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"space-before-function-paren": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"anonymous": "never",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"space-in-parens": "error",
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": "error",
|
||||||
|
"spaced-comment": "error",
|
||||||
|
"yoda": "error"
|
||||||
|
}
|
||||||
|
}
|
2221
package-lock.json
generated
Normal file
2221
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
Normal file
38
package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "assetflow",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"prestart": "npm run build",
|
||||||
|
"start": "node .",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"assets",
|
||||||
|
"inventory",
|
||||||
|
"storage"
|
||||||
|
],
|
||||||
|
"author": "[Project-Name-Here]",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^4.13.0",
|
||||||
|
"bootstrap": "^5.3.0-alpha3",
|
||||||
|
"bootstrap-icons": "^1.10.5",
|
||||||
|
"eta": "^2.0.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"jquery": "^3.6.4",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"prisma": "^4.13.0",
|
||||||
|
"signale": "^1.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/lodash": "^4.14.194",
|
||||||
|
"@types/signale": "^1.4.4",
|
||||||
|
"eslint": "^8.39.0",
|
||||||
|
"eslint-config-prettier": "^8.8.0",
|
||||||
|
"typescript": "^5.0.4"
|
||||||
|
}
|
||||||
|
}
|
60
prisma/schema.prisma
Normal file
60
prisma/schema.prisma
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Category {
|
||||||
|
Light
|
||||||
|
Audio
|
||||||
|
Laptop
|
||||||
|
Adapter
|
||||||
|
Other
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
normal
|
||||||
|
borrowed
|
||||||
|
stolen
|
||||||
|
lost
|
||||||
|
}
|
||||||
|
|
||||||
|
model Item {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
SKU String @unique
|
||||||
|
Amount Int
|
||||||
|
Comment String?
|
||||||
|
name String
|
||||||
|
manufacturer String
|
||||||
|
category Category
|
||||||
|
status Status
|
||||||
|
StorageLocation StorageLocation @relation(fields: [storageLocationId], references: [id])
|
||||||
|
storageLocationId Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
model StorageLocation {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
storageBuilding StorageBuilding @relation(fields: [storageBuildingId], references: [id])
|
||||||
|
storageBuildingId Int
|
||||||
|
Item Item[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model StorageBuilding {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
street String
|
||||||
|
houseNumber String
|
||||||
|
zipCode String
|
||||||
|
city String
|
||||||
|
country String
|
||||||
|
StorageLocation StorageLocation[]
|
||||||
|
}
|
83
src/assets/configHandler.ts
Normal file
83
src/assets/configHandler.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export type configObject = Record<any, any>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible to save/edit config files.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class config
|
||||||
|
* @typedef {config}
|
||||||
|
*/
|
||||||
|
export default class config {
|
||||||
|
#configPath: string;
|
||||||
|
//global = {[key: string] : string}
|
||||||
|
global: configObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of config.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} configPath Path to config file.
|
||||||
|
* @param {object} configPreset Default config object with default values.
|
||||||
|
*/
|
||||||
|
constructor(configPath: string, configPreset: object) {
|
||||||
|
this.#configPath = configPath;
|
||||||
|
this.global = configPreset;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read config
|
||||||
|
const data = fs.readFileSync(this.#configPath, 'utf8');
|
||||||
|
|
||||||
|
// Extend config with missing parameters from configPreset.
|
||||||
|
this.global = _.defaultsDeep(JSON.parse(data), this.global);
|
||||||
|
// Save config.
|
||||||
|
this.save_config();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Could not read config file at ' + this.#configPath + ' due to: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the jsonified config object to the config file.
|
||||||
|
*/
|
||||||
|
save_config() {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(this.#configPath, JSON.stringify(this.global, null, 8));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Could not write config file at ' + this.#configPath + ' due to: ' + err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Successfully written config file to ' + this.#configPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG: If file does'nt exist -> fail.
|
||||||
|
// ToDo: Check for SyntaxError on fileread and ask if the user wants to continue -> overwrite everything. This behavior is currently standard.
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
**** Example ****
|
||||||
|
|
||||||
|
const default_config = {
|
||||||
|
token: 'your-token-goes-here',
|
||||||
|
clientId: '',
|
||||||
|
devserverID: '',
|
||||||
|
devmode: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
import configHandler from './assets/config.js';
|
||||||
|
const config = new configHandler(__path + '/config.json', default_config);
|
||||||
|
|
||||||
|
console.log('Base Config:');
|
||||||
|
console.log(config.global);
|
||||||
|
|
||||||
|
console.log('Add some new key to config and call save_config.');
|
||||||
|
config.global.NewKey = 'ThisIsANewKey!'
|
||||||
|
config.save_config()
|
||||||
|
|
||||||
|
console.log('Complete Config:');
|
||||||
|
console.log(config.global);
|
||||||
|
*/
|
31
src/frontend/publicInfoPage.eta.html
Normal file
31
src/frontend/publicInfoPage.eta.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>AssetFlow - Info about item</title>
|
||||||
|
<meta name="description" content="A simple HTML5 Template for new projects." />
|
||||||
|
<meta name="author" content="[Project-Name-Here]" />
|
||||||
|
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
|
||||||
|
<script src="/static/jquery/dist/jquery.min.js"></script>
|
||||||
|
<link href="/static/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/static/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<script src="/static/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="ui raised very padded text container segment">
|
||||||
|
<h2 class="ui header"><%= it.name %></h2>
|
||||||
|
<p><strong>Category:</strong> <%= it.category%></p>
|
||||||
|
<p><strong>Amount:</strong> <%= it.Amount %></p>
|
||||||
|
<p><strong>SKU:</strong> <%= it.SKU %></p>
|
||||||
|
<p><strong>Comment:</strong> <%= it.comment %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
0
src/frontend/style.css
Normal file
0
src/frontend/style.css
Normal file
139
src/index.ts
Normal file
139
src/index.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { Signale } from 'signale';
|
||||||
|
import ConfigHandler from './assets/configHandler';
|
||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
import * as Eta from 'eta';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { Status, Category } from '@prisma/client';
|
||||||
|
import * as Path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import routes from './routes/index.js'
|
||||||
|
|
||||||
|
// Get app directory.
|
||||||
|
const __path = process.argv[1];
|
||||||
|
|
||||||
|
const logger_settings = {
|
||||||
|
disabled: false,
|
||||||
|
logLevel: 'info',
|
||||||
|
scope: 'Core',
|
||||||
|
stream: process.stdout,
|
||||||
|
displayFilename: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const coreLogger = new Signale(logger_settings);
|
||||||
|
const log = {
|
||||||
|
core: coreLogger,
|
||||||
|
db: coreLogger.scope('DB'),
|
||||||
|
web: coreLogger.scope('WEB')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a new config instance.
|
||||||
|
const config = new ConfigHandler(__path + '/config.json', {
|
||||||
|
db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
|
||||||
|
http_listen_address: '127.0.0.1',
|
||||||
|
http_port: 3000
|
||||||
|
});
|
||||||
|
|
||||||
|
const prisma = new PrismaClient({
|
||||||
|
datasources: {
|
||||||
|
db: {
|
||||||
|
url: config.global.db_connection_string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.get('/dev/fillWithDemoData', (req, res) => {
|
||||||
|
// fill database with demo data
|
||||||
|
prisma.StorageBuilding.create({
|
||||||
|
data: {
|
||||||
|
name: "Test Storage Building",
|
||||||
|
street: "Test Street",
|
||||||
|
houseNumber: "1",
|
||||||
|
zipCode: "12345",
|
||||||
|
city: "Test City",
|
||||||
|
country: "Test Country",
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
prisma.StorageLocation.create({
|
||||||
|
data: {
|
||||||
|
name: "Test Storage Location",
|
||||||
|
StorageBuilding: {
|
||||||
|
connect: {
|
||||||
|
id: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
prisma.item
|
||||||
|
.create({
|
||||||
|
data: {
|
||||||
|
SKU: 'ee189749',
|
||||||
|
Amount: 1,
|
||||||
|
name: 'Test Item',
|
||||||
|
manufacturer: 'Test Manufacturer',
|
||||||
|
category: Category.Other,
|
||||||
|
status: Status.normal,
|
||||||
|
storageLocation: {
|
||||||
|
connect: {
|
||||||
|
id: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.send('Demo data added');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
res.send('Error adding demo data: ' + err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
res.send('Demo data added (not)');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/:id', (req, res) => {
|
||||||
|
// retrieve data from database using id from url
|
||||||
|
prisma.item
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
SKU: req.params.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((item) => {
|
||||||
|
if (item) {
|
||||||
|
Eta.renderFile(__path + '/src/frontend/publicInfoPage.eta.html', item).then((html) => {
|
||||||
|
res.send(html);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.send('Item not found');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load from allowsStaticPaths.json file
|
||||||
|
const allowedURLs: Array<string> = JSON.parse(fs.readFileSync("allowedStaticPaths.json", "utf8")).allowedStaticFiles;
|
||||||
|
const recordedURLs: Array<string> = [];
|
||||||
|
const debugMode: boolean = JSON.parse(fs.readFileSync("allowedStaticPaths.json", "utf8")).debugMode;
|
||||||
|
|
||||||
|
app.use('/static/*', function handleModuleFiles(req: Request, res: Response) {
|
||||||
|
if(debugMode) {
|
||||||
|
res.sendFile(Path.join(__dirname, 'node_modules', req.params[0]));
|
||||||
|
recordedURLs.push(req.params[0]);
|
||||||
|
log.web.debug(recordedURLs);
|
||||||
|
} else {
|
||||||
|
if (allowedURLs.indexOf(req.params[0]) > -1) {
|
||||||
|
res.sendFile(Path.join(__dirname, 'node_modules', req.params[0]));
|
||||||
|
} else {
|
||||||
|
log.web.warn('Attempt to access restricted asset file ' + req.params[0]);
|
||||||
|
res.status(403).json({ status: 'error', reason: 'Access to restricted asset file denied' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log(recordedURLs)
|
||||||
|
});
|
||||||
|
|
||||||
|
routes(app);
|
||||||
|
|
||||||
|
app.listen(config.global.http_port, config.global.http_listen_address, () => {
|
||||||
|
log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`);
|
||||||
|
});
|
9
src/routes/api/index.ts
Normal file
9
src/routes/api/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import express from 'express';
|
||||||
|
const Router = express.Router();
|
||||||
|
|
||||||
|
import testRoute from './test.js';
|
||||||
|
|
||||||
|
Router.use("/api/test", testRoute)
|
||||||
|
|
||||||
|
|
||||||
|
export default Router;
|
4
src/routes/api/test.ts
Normal file
4
src/routes/api/test.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
export default (req: Request, res: Response) => {
|
||||||
|
res.status(200).send("Test Successful!");
|
||||||
|
};
|
8
src/routes/frontend/index.ts
Normal file
8
src/routes/frontend/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import express from 'express';
|
||||||
|
const Router = express.Router();
|
||||||
|
|
||||||
|
import testRoute from './test.js';
|
||||||
|
|
||||||
|
Router.use("/test", testRoute)
|
||||||
|
|
||||||
|
export default Router;
|
4
src/routes/frontend/test.ts
Normal file
4
src/routes/frontend/test.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
export default (req: Request, res: Response) => {
|
||||||
|
res.status(200).send("Test Successful!");
|
||||||
|
};
|
8
src/routes/index.ts
Normal file
8
src/routes/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Express } from 'express';
|
||||||
|
import frontend_routes from './frontend/index.js';
|
||||||
|
import api_routes from './frontend/index.js';
|
||||||
|
|
||||||
|
export default (app: Express) => {
|
||||||
|
app.use('/', frontend_routes);
|
||||||
|
app.use('/api', api_routes);
|
||||||
|
};
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es6",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"paths": {
|
||||||
|
"*": [
|
||||||
|
"node_modules/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user