@@ -0,0 +1,4 @@
|
||||
#ignore each file in folder that starts with Archive_
|
||||
Archive_*
|
||||
#ignore each folder that starts with Archive_
|
||||
Archive_*/**
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
/dist/generated/prisma
|
||||
@@ -1 +0,0 @@
|
||||
Ez a backend readme fájlja
|
||||
+2223
-6
File diff suppressed because it is too large
Load Diff
+46
-47
@@ -1,4 +1,6 @@
|
||||
# ipaddr.js — an IPv6 and IPv4 address manipulation library [](https://travis-ci.org/whitequark/ipaddr.js)
|
||||
# ipaddr.js — an IPv6 and IPv4 address manipulation library
|
||||
|
||||
[](https://github.com/whitequark/ipaddr.js/actions?query=workflow%3A%22CI+Tests%22)
|
||||
|
||||
ipaddr.js is a small (1.9K minified and gzipped) library for manipulating
|
||||
IP addresses in JavaScript environments. It runs on both CommonJS runtimes
|
||||
@@ -19,25 +21,34 @@ or
|
||||
|
||||
`bower install ipaddr.js`
|
||||
|
||||
## Older Node support
|
||||
|
||||
Use 2.x release for nodejs versions 10+.
|
||||
Use the 1.x release for versions of nodejs older than 10.
|
||||
|
||||
## API
|
||||
|
||||
ipaddr.js defines one object in the global scope: `ipaddr`. In CommonJS,
|
||||
it is exported from the module:
|
||||
|
||||
```js
|
||||
var ipaddr = require('ipaddr.js');
|
||||
const ipaddr = require('ipaddr.js');
|
||||
```
|
||||
|
||||
The API consists of several global methods and two classes: ipaddr.IPv6 and ipaddr.IPv4.
|
||||
|
||||
### Global methods
|
||||
|
||||
There are three global methods defined: `ipaddr.isValid`, `ipaddr.parse` and
|
||||
`ipaddr.process`. All of them receive a string as a single parameter.
|
||||
There are four global methods defined: `ipaddr.isValid`, `ipaddr.isValidCIDR`,
|
||||
`ipaddr.parse`, and `ipaddr.process`. All of them receive a string as a single
|
||||
parameter.
|
||||
|
||||
The `ipaddr.isValid` method returns `true` if the address is a valid IPv4 or
|
||||
IPv6 address, and `false` otherwise. It does not throw any exceptions.
|
||||
|
||||
The `ipaddr.isValidCIDR` method returns `true` if the address is a valid IPv4 or
|
||||
IPv6 address in CIDR notation, and `false` otherwise. It does not throw any exceptions.
|
||||
|
||||
The `ipaddr.parse` method returns an object representing the IP address,
|
||||
or throws an `Error` if the passed string is not a valid representation of an
|
||||
IP address.
|
||||
@@ -67,40 +78,34 @@ Note that this method:
|
||||
* returns a compact representation (when it is applicable)
|
||||
|
||||
A `match(range, bits)` method can be used to check if the address falls into a
|
||||
certain CIDR range.
|
||||
Note that an address can be (obviously) matched only against an address of the same type.
|
||||
certain CIDR range. Note that an address can be (obviously) matched only against an address of the same type.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:db8:1234::1");
|
||||
var range = ipaddr.parse("2001:db8::");
|
||||
const addr = ipaddr.parse('2001:db8:1234::1');
|
||||
const range = ipaddr.parse('2001:db8::');
|
||||
|
||||
addr.match(range, 32); // => true
|
||||
```
|
||||
|
||||
Alternatively, `match` can also be called as `match([range, bits])`. In this way,
|
||||
it can be used together with the `parseCIDR(string)` method, which parses an IP
|
||||
address together with a CIDR range.
|
||||
Alternatively, `match` can also be called as `match([range, bits])`. In this way, it can be used together with the `parseCIDR(string)` method, which parses an IP address together with a CIDR range.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:db8:1234::1");
|
||||
const addr = ipaddr.parse('2001:db8:1234::1');
|
||||
|
||||
addr.match(ipaddr.parseCIDR("2001:db8::/32")); // => true
|
||||
addr.match(ipaddr.parseCIDR('2001:db8::/32')); // => true
|
||||
```
|
||||
|
||||
A `range()` method returns one of predefined names for several special ranges defined
|
||||
by IP protocols. The exact names (and their respective CIDR ranges) can be looked up
|
||||
in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"`
|
||||
(the default one) and `"reserved"`.
|
||||
A `range()` method returns one of predefined names for several special ranges defined by IP protocols. The exact names (and their respective CIDR ranges) can be looked up in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"` (the default one) and `"reserved"`.
|
||||
|
||||
You can match against your own range list by using
|
||||
`ipaddr.subnetMatch(address, rangeList, defaultName)` method. It can work with a mix of IPv6 or IPv4 addresses, and accepts a name-to-subnet map as the range list. For example:
|
||||
|
||||
```js
|
||||
var rangeList = {
|
||||
const rangeList = {
|
||||
documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ],
|
||||
tunnelProviders: [
|
||||
[ ipaddr.parse('2001:470::'), 32 ], // he.net
|
||||
@@ -110,38 +115,32 @@ var rangeList = {
|
||||
ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown'); // => "tunnelProviders"
|
||||
```
|
||||
|
||||
The addresses can be converted to their byte representation with `toByteArray()`.
|
||||
(Actually, JavaScript mostly does not know about byte buffers. They are emulated with
|
||||
arrays of numbers, each in range of 0..255.)
|
||||
The addresses can be converted to their byte representation with `toByteArray()`. (Actually, JavaScript mostly does not know about byte buffers. They are emulated with arrays of numbers, each in range of 0..255.)
|
||||
|
||||
```js
|
||||
var bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com
|
||||
const bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com
|
||||
bytes // => [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, <zeroes...>, 0x00, 0x68 ]
|
||||
```
|
||||
|
||||
The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them
|
||||
have the same interface for both protocols, and are similar to global methods.
|
||||
The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them have the same interface for both protocols, and are similar to global methods.
|
||||
|
||||
`ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address
|
||||
for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser.
|
||||
`ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser.
|
||||
|
||||
`ipaddr.IPvX.isValid(string)` uses the same format for parsing as the POSIX `inet_ntoa` function, which accepts unusual formats like `0xc0.168.1.1` or `0x10000000`. The function `ipaddr.IPv4.isValidFourPartDecimal(string)` validates the IPv4 address and also ensures that it is written in four-part decimal format.
|
||||
|
||||
[IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L186
|
||||
[IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L71
|
||||
[IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L530
|
||||
[IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L182
|
||||
|
||||
#### IPv6 properties
|
||||
|
||||
Sometimes you will want to convert IPv6 not to a compact string representation (with
|
||||
the `::` substitution); the `toNormalizedString()` method will return an address where
|
||||
all zeroes are explicit.
|
||||
Sometimes you will want to convert IPv6 not to a compact string representation (with the `::` substitution); the `toNormalizedString()` method will return an address where all zeroes are explicit.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:0db8::0001");
|
||||
addr.toString(); // => "2001:db8::1"
|
||||
addr.toNormalizedString(); // => "2001:db8:0:0:0:0:0:1"
|
||||
const addr = ipaddr.parse('2001:0db8::0001');
|
||||
addr.toString(); // => '2001:db8::1'
|
||||
addr.toNormalizedString(); // => '2001:db8:0:0:0:0:0:1'
|
||||
```
|
||||
|
||||
The `isIPv4MappedAddress()` method will return `true` if this address is an IPv4-mapped
|
||||
@@ -150,14 +149,14 @@ one, and `toIPv4Address()` will return an IPv4 object address.
|
||||
To access the underlying binary representation of the address, use `addr.parts`.
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:db8:10::1234:DEAD");
|
||||
const addr = ipaddr.parse('2001:db8:10::1234:DEAD');
|
||||
addr.parts // => [0x2001, 0xdb8, 0x10, 0, 0, 0, 0x1234, 0xdead]
|
||||
```
|
||||
|
||||
A IPv6 zone index can be accessed via `addr.zoneId`:
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:db8::%eth0");
|
||||
const addr = ipaddr.parse('2001:db8::%eth0');
|
||||
addr.zoneId // => 'eth0'
|
||||
```
|
||||
|
||||
@@ -168,7 +167,7 @@ addr.zoneId // => 'eth0'
|
||||
To access the underlying representation of the address, use `addr.octets`.
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("192.168.1.1");
|
||||
const addr = ipaddr.parse('192.168.1.1');
|
||||
addr.octets // => [192, 168, 1, 1]
|
||||
```
|
||||
|
||||
@@ -183,17 +182,17 @@ ipaddr.IPv4.parse('255.192.164.0').prefixLengthFromSubnetMask() == null
|
||||
`subnetMaskFromPrefixLength()` will return an IPv4 netmask for a valid CIDR prefix length.
|
||||
|
||||
```js
|
||||
ipaddr.IPv4.subnetMaskFromPrefixLength(24) == "255.255.255.0"
|
||||
ipaddr.IPv4.subnetMaskFromPrefixLength(29) == "255.255.255.248"
|
||||
ipaddr.IPv4.subnetMaskFromPrefixLength(24) == '255.255.255.0'
|
||||
ipaddr.IPv4.subnetMaskFromPrefixLength(29) == '255.255.255.248'
|
||||
```
|
||||
|
||||
`broadcastAddressFromCIDR()` will return the broadcast address for a given IPv4 interface and netmask in CIDR notation.
|
||||
```js
|
||||
ipaddr.IPv4.broadcastAddressFromCIDR("172.0.0.1/24") == "172.0.0.255"
|
||||
ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/24') == '172.0.0.255'
|
||||
```
|
||||
`networkAddressFromCIDR()` will return the network address for a given IPv4 interface and netmask in CIDR notation.
|
||||
```js
|
||||
ipaddr.IPv4.networkAddressFromCIDR("172.0.0.1/24") == "172.0.0.0"
|
||||
ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/24') == '172.0.0.0'
|
||||
```
|
||||
|
||||
#### Conversion
|
||||
@@ -206,28 +205,28 @@ while for IPv6 it has to be an array of sixteen 8-bit values.
|
||||
|
||||
For example:
|
||||
```js
|
||||
var addr = ipaddr.fromByteArray([0x7f, 0, 0, 1]);
|
||||
addr.toString(); // => "127.0.0.1"
|
||||
const addr = ipaddr.fromByteArray([0x7f, 0, 0, 1]);
|
||||
addr.toString(); // => '127.0.0.1'
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
var addr = ipaddr.fromByteArray([0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
addr.toString(); // => "2001:db8::1"
|
||||
const addr = ipaddr.fromByteArray([0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
addr.toString(); // => '2001:db8::1'
|
||||
```
|
||||
|
||||
Both objects also offer a `toByteArray()` method, which returns an array in network byte order (MSB).
|
||||
|
||||
For example:
|
||||
```js
|
||||
var addr = ipaddr.parse("127.0.0.1");
|
||||
const addr = ipaddr.parse('127.0.0.1');
|
||||
addr.toByteArray(); // => [0x7f, 0, 0, 1]
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
var addr = ipaddr.parse("2001:db8::1");
|
||||
const addr = ipaddr.parse('2001:db8::1');
|
||||
addr.toByteArray(); // => [0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
|
||||
```
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1028
-645
File diff suppressed because it is too large
Load Diff
+13
-10
@@ -1,6 +1,7 @@
|
||||
declare module "ipaddr.js" {
|
||||
type IPv4Range = 'unicast' | 'unspecified' | 'broadcast' | 'multicast' | 'linkLocal' | 'loopback' | 'carrierGradeNat' | 'private' | 'reserved';
|
||||
type IPv6Range = 'unicast' | 'unspecified' | 'linkLocal' | 'multicast' | 'loopback' | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'reserved';
|
||||
type IPvXRangeDefaults = 'unicast' | 'unspecified' | 'multicast' | 'linkLocal' | 'loopback' | 'reserved' | 'benchmarking' | 'amt';
|
||||
type IPv4Range = IPvXRangeDefaults | 'broadcast' | 'carrierGradeNat' | 'private' | 'as112';
|
||||
type IPv6Range = IPvXRangeDefaults | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'as112v6' | 'orchid2' | 'droneRemoteIdProtocolEntityTags';
|
||||
|
||||
interface RangeList<T> {
|
||||
[name: string]: [T, number] | [T, number][];
|
||||
@@ -15,19 +16,20 @@ declare module "ipaddr.js" {
|
||||
}
|
||||
|
||||
namespace Address {
|
||||
export function isValid(addr: string): boolean;
|
||||
export function fromByteArray(bytes: number[]): IPv4 | IPv6;
|
||||
export function isValid(addr: string): boolean;
|
||||
export function isValidCIDR(addr: string): boolean;
|
||||
export function parse(addr: string): IPv4 | IPv6;
|
||||
export function parseCIDR(mask: string): [IPv4 | IPv6, number];
|
||||
export function process(addr: string): IPv4 | IPv6;
|
||||
export function subnetMatch(addr: IPv4, rangeList: RangeList<IPv4>, defaultName?: string): string;
|
||||
export function subnetMatch(addr: IPv6, rangeList: RangeList<IPv6>, defaultName?: string): string;
|
||||
export function subnetMatch(addr: IPv4 | IPv6, rangeList: RangeList<IPv4 | IPv6>, defaultName?: string): string;
|
||||
|
||||
export class IPv4 extends IP {
|
||||
static broadcastAddressFromCIDR(addr: string): IPv4;
|
||||
static isIPv4(addr: string): boolean;
|
||||
static isValidFourPartDecimal(addr: string): boolean;
|
||||
static isValid(addr: string): boolean;
|
||||
static isValidCIDR(addr: string): boolean;
|
||||
static isValidFourPartDecimal(addr: string): boolean;
|
||||
static networkAddressFromCIDR(addr: string): IPv4;
|
||||
static parse(addr: string): IPv4;
|
||||
static parseCIDR(addr: string): [IPv4, number];
|
||||
@@ -36,8 +38,7 @@ declare module "ipaddr.js" {
|
||||
octets: number[]
|
||||
|
||||
kind(): 'ipv4';
|
||||
match(addr: IPv4, bits: number): boolean;
|
||||
match(mask: [IPv4, number]): boolean;
|
||||
match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean;
|
||||
range(): IPv4Range;
|
||||
subnetMatch(rangeList: RangeList<IPv4>, defaultName?: string): string;
|
||||
toIPv4MappedAddress(): IPv6;
|
||||
@@ -47,6 +48,8 @@ declare module "ipaddr.js" {
|
||||
static broadcastAddressFromCIDR(addr: string): IPv6;
|
||||
static isIPv6(addr: string): boolean;
|
||||
static isValid(addr: string): boolean;
|
||||
static isValidCIDR(addr: string): boolean;
|
||||
static networkAddressFromCIDR(addr: string): IPv6;
|
||||
static parse(addr: string): IPv6;
|
||||
static parseCIDR(addr: string): [IPv6, number];
|
||||
static subnetMaskFromPrefixLength(prefix: number): IPv6;
|
||||
@@ -56,11 +59,11 @@ declare module "ipaddr.js" {
|
||||
|
||||
isIPv4MappedAddress(): boolean;
|
||||
kind(): 'ipv6';
|
||||
match(addr: IPv6, bits: number): boolean;
|
||||
match(mask: [IPv6, number]): boolean;
|
||||
match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean;
|
||||
range(): IPv6Range;
|
||||
subnetMatch(rangeList: RangeList<IPv6>, defaultName?: string): string;
|
||||
toIPv4Address(): IPv4;
|
||||
toRFC5952String(): string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -1,22 +1,24 @@
|
||||
{
|
||||
"name": "ipaddr.js",
|
||||
"description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.",
|
||||
"version": "1.9.1",
|
||||
"version": "2.2.0",
|
||||
"author": "whitequark <whitequark@whitequark.org>",
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"coffee-script": "~1.12.6",
|
||||
"nodeunit": "^0.11.3",
|
||||
"uglify-js": "~3.0.19"
|
||||
"eslint": "^8.57.0",
|
||||
"uglify-es": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "cake build test"
|
||||
"lint": "npx eslint lib",
|
||||
"lintfix": "npx eslint --fix lib test",
|
||||
"build": "npx uglifyjs --compress --mangle --wrap=window -o ipaddr.min.js lib/ipaddr.js",
|
||||
"test": "node --test"
|
||||
},
|
||||
"files": [
|
||||
"lib/",
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"ipaddr.min.js"
|
||||
],
|
||||
@@ -28,7 +30,7 @@
|
||||
"repository": "git://github.com/whitequark/ipaddr.js",
|
||||
"main": "./lib/ipaddr.js",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
"node": ">= 10"
|
||||
},
|
||||
"license": "MIT",
|
||||
"types": "./lib/ipaddr.js.d.ts"
|
||||
|
||||
Generated
+2240
-8
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,36 @@
|
||||
{
|
||||
"name": "serpentrace_backend",
|
||||
"version": "1.0.0",
|
||||
"main": "index.mjs",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start:dev": "nodemon ./src/index.mjs",
|
||||
"start": "nodemon ./src/index.mjs"
|
||||
"start": "node dist/index.js",
|
||||
"dev": "nodemon --exec ts-node src/index.ts",
|
||||
"build": "tsc -p tsconfig.json && node ./prisma/prisma-migrate-all.js",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"migrate:all": "node ./prisma/prisma-migrate-all.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"express": "^5.1.0"
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/cookie-parser": "^1.4.9",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.2",
|
||||
"@types/jsonwebtoken": "^9.0.9",
|
||||
"@types/node": "^22.15.21",
|
||||
"bcrypt": "^6.0.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"minio": "^8.0.5",
|
||||
"typeorm": "^0.3.24",
|
||||
"typescript": "^5.8.3",
|
||||
"winston": "^3.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
},
|
||||
"type": "module"
|
||||
"nodemon": "^3.1.10",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { Company } from './company.entity';
|
||||
import { QBank } from './qbank.entity';
|
||||
|
||||
// Get the database type from environment variables or default to MariaDB
|
||||
const dbType = process.env.DB_TYPE || 'mariadb';
|
||||
|
||||
// Create the options object with correct typing
|
||||
const options: DataSourceOptions = {
|
||||
type: dbType as any, // Using 'as any' to bypass TypeScript's strict type checking
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: Number(process.env.DB_PORT) || 3306,
|
||||
username: process.env.DB_USERNAME || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_DATABASE || 'serpent_race',
|
||||
synchronize: process.env.NODE_ENV !== 'production',
|
||||
logging: process.env.NODE_ENV !== 'production',
|
||||
entities: [User, Company, QBank],
|
||||
migrations: [],
|
||||
subscribers: [],
|
||||
};
|
||||
|
||||
// Create the DataSource with properly typed options
|
||||
export const AppDataSourceOne = new DataSource(options);
|
||||
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
OneToMany
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity('companies')
|
||||
export class Company {
|
||||
@PrimaryGeneratedColumn()
|
||||
CompanyId!: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
Name!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
ContactFirstName!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
ContactLastName!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
ContactEmail!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
FirstAPI!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
TokenAPI!: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
RegDate!: Date;
|
||||
|
||||
// Relation to User entity (optional)
|
||||
@OneToMany(() => User, user => user.company)
|
||||
users!: User[];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity('q_bank')
|
||||
export class QBank {
|
||||
@PrimaryGeneratedColumn()
|
||||
QBankId!: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255 })
|
||||
Title!: string;
|
||||
|
||||
@Column()
|
||||
Creator!: number;
|
||||
|
||||
@Column({ type: 'datetime' })
|
||||
Creation_Date!: Date;
|
||||
|
||||
@Column({ type: 'int' })
|
||||
no_question!: number;
|
||||
|
||||
@ManyToOne(() => User, user => user.questionBanks)
|
||||
@JoinColumn({ name: 'Creator' })
|
||||
creator!: User;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
JoinColumn
|
||||
} from 'typeorm';
|
||||
import { Company } from './company.entity';
|
||||
import { QBank } from './qbank.entity';
|
||||
|
||||
// User entity for the SerpentRace API
|
||||
// This entity represents a user in the system, with fields for personal information,
|
||||
|
||||
@Entity('users')
|
||||
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, unique: true })
|
||||
Username!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
FirstName!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
LastName!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
Password!: string; // Consider hashing this password before storing
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, unique: true })
|
||||
Email!: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
RegDate!: Date;
|
||||
|
||||
@Column({ nullable: true })
|
||||
CompanyId: number = 0;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
CompanyToken: string = '';
|
||||
|
||||
@ManyToOne(() => Company, (company) => company.users)
|
||||
@JoinColumn({ name: 'CompanyId' })
|
||||
company!: Company;
|
||||
|
||||
@OneToMany(() => QBank, qbank => qbank.creator)
|
||||
questionBanks!: QBank[];
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//User class for the all the user related operations in the SerpentRace Backend
|
||||
import { comparePasswords, hashPassword } from '../middleware/secure';
|
||||
import { FindOptionsWhere, Equal } from 'typeorm';
|
||||
import { AppDataSourceOne } from '../DB/Datasource';
|
||||
import { User } from '../DB/user.entity';
|
||||
import { Company } from '../DB/company.entity';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export class CompanyService {
|
||||
private companyRepository = AppDataSourceOne.getRepository(Company);
|
||||
|
||||
async createCompany(company: Company): Promise<Company> {
|
||||
return this.companyRepository.save(company);
|
||||
}
|
||||
|
||||
private async findCompanyById(id: number): Promise<Company | null> {
|
||||
const where: FindOptionsWhere<Company> = { CompanyId: Equal(id) };
|
||||
return this.companyRepository.findOneBy(where);
|
||||
}
|
||||
|
||||
private async findCompanyByName(name: string): Promise<Company | null> {
|
||||
const where: FindOptionsWhere<Company> = { Name: Equal(name) };
|
||||
return this.companyRepository.findOneBy(where);
|
||||
}
|
||||
|
||||
async getCompanyDetails(req: Request, res: Response): Promise<void> {
|
||||
const companyId = parseInt(req.params.companyid);
|
||||
if (isNaN(companyId)) {
|
||||
res.status(400).json({ message: 'Invalid company ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
const company = await this.findCompanyById(companyId);
|
||||
if (!company) {
|
||||
res.status(404).json({ message: 'Company not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json(company);
|
||||
}
|
||||
|
||||
async updateCompany(req: Request, res: Response): Promise<void> {
|
||||
const companyId = parseInt(req.params.companyid);
|
||||
if (isNaN(companyId)) {
|
||||
res.status(400).json({ message: 'Invalid company ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
const company = await this.findCompanyById(companyId);
|
||||
if (!company) {
|
||||
res.status(404).json({ message: 'Company not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedData = req.body;
|
||||
Object.assign(company, updatedData);
|
||||
|
||||
const updatedCompany = await this.companyRepository.save(company);
|
||||
res.status(200).json(updatedCompany);
|
||||
}
|
||||
async deleteCompany(req: Request, res: Response): Promise<void> {
|
||||
const companyId = parseInt(req.params.companyid);
|
||||
if (isNaN(companyId)) {
|
||||
res.status(400).json({ message: 'Invalid company ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
const company = await this.findCompanyById(companyId);
|
||||
if (!company) {
|
||||
res.status(404).json({ message: 'Company not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
await this.companyRepository.remove(company);
|
||||
res.status(204).send();
|
||||
}
|
||||
|
||||
private async addUserToCompany(userId: number, companyId: number): Promise<void> {
|
||||
const user = await AppDataSourceOne.getRepository(User).findOneBy({ id: userId });
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const company = await this.findCompanyById(companyId);
|
||||
if (!company) {
|
||||
throw new Error('Company not found');
|
||||
}
|
||||
|
||||
user.CompanyId = companyId;
|
||||
await AppDataSourceOne.getRepository(User).save(user);
|
||||
// Optionally, you can also add the user to the company's user list if needed
|
||||
if (!company.users) {
|
||||
company.users = [];
|
||||
}
|
||||
company.users.push(user);
|
||||
}
|
||||
private async removeUserFromCompany(userId: number, companyId: number): Promise<void> {
|
||||
const user = await AppDataSourceOne.getRepository(User).findOneBy({ id: userId });
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const company = await this.findCompanyById(companyId);
|
||||
if (!company) {
|
||||
throw new Error('Company not found');
|
||||
}
|
||||
|
||||
user.CompanyId = 0; // Remove association
|
||||
user.CompanyToken = ''; // Remove association
|
||||
await AppDataSourceOne.getRepository(User).save(user);
|
||||
|
||||
// Optionally, remove the user from the company's user list
|
||||
company.users = company.users?.filter(u => u.id !== userId) || [];
|
||||
await this.companyRepository.save(company);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//User class for the all the user related operations in the SerpentRace Backend
|
||||
import { comparePasswords, hashPassword } from '../middleware/secure';
|
||||
import { FindOptionsWhere, Equal } from 'typeorm';
|
||||
import { AppDataSourceOne } from '../DB/Datasource';
|
||||
import { createToken } from '../middleware/auth';
|
||||
import { User } from '../DB/user.entity';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export class UserService {
|
||||
private userRepository = AppDataSourceOne.getRepository(User);
|
||||
|
||||
async createUser(user : User): Promise<User> {
|
||||
// Hash the password before saving the user
|
||||
user.Password = await hashPassword(user.Password);
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
private async findUserByEmail(email: string): Promise<User | null> {
|
||||
const where: FindOptionsWhere<User> = { Email: Equal(email) };
|
||||
return this.userRepository.findOneBy(where);
|
||||
}
|
||||
|
||||
private async findUserByUsername(username: string): Promise<User | null> {
|
||||
const where: FindOptionsWhere<User> = { Username: Equal(username) };
|
||||
return this.userRepository.findOneBy(where);
|
||||
}
|
||||
|
||||
private async findUserById(id: number): Promise<User | null> {
|
||||
const where: FindOptionsWhere<User> = { id: Equal(id) };
|
||||
return this.userRepository.findOneBy(where);
|
||||
}
|
||||
|
||||
async authenticateUser(req: Request, res: Response): Promise<void> {
|
||||
const { String_data, Password } = req.body;
|
||||
if (!String_data || !Password) {
|
||||
res.status(400).json({ message: 'Email and password are required' });
|
||||
return;
|
||||
}
|
||||
// Check if String_data is an email or username
|
||||
let user: User | null = null;
|
||||
if (String_data.includes('@')) {
|
||||
user = await this.findUserByEmail(String_data.toLowerCase());
|
||||
}
|
||||
else {
|
||||
user = await this.findUserByUsername(String_data);
|
||||
}
|
||||
// If user is not found or password does not match, return 401
|
||||
if (!user || !(await comparePasswords(Password, user.Password))) {
|
||||
res.status(401).json({ message: 'Invalid credentials' });
|
||||
return;
|
||||
}
|
||||
|
||||
const token = createToken(user);
|
||||
// Set the token in the response header
|
||||
res.cookie("jwt", token, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "none",
|
||||
maxAge: 60 * 60 * 1000
|
||||
});
|
||||
res.status(200).json({ message: 'Authentication successful' });
|
||||
}
|
||||
|
||||
async getUserDetails(req: Request, res: Response): Promise<void> {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) {
|
||||
res.status(401).json({ message: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const user = await this.userRepository.findOneBy({ id: userId });
|
||||
if (!user) {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
res.json(user);
|
||||
}
|
||||
|
||||
async updateUser(req: Request, res: Response): Promise<void> {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) {
|
||||
res.status(401).json({ message: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const user = await this.findUserById(userId);
|
||||
if (!user) {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(user, req.body);
|
||||
|
||||
if (req.body.Password) {
|
||||
user.Password = await hashPassword(req.body.Password);
|
||||
}
|
||||
await this.userRepository.save(user);
|
||||
res.json({ message: 'User details updated successfully' });
|
||||
}
|
||||
|
||||
async deleteUser(req: Request, res: Response): Promise<void> {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) {
|
||||
res.status(401).json({ message: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const user = await this.findUserById(userId);
|
||||
if (!user) {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
await this.userRepository.remove(user);
|
||||
res.json({ message: 'User deleted successfully' });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//Router for user-related routes
|
||||
import { Router } from 'express';
|
||||
import { UserService } from './User';
|
||||
import { auth } from '../middleware/auth';
|
||||
import { User } from '../DB/user.entity';
|
||||
|
||||
const userRouter = Router();
|
||||
const userService = new UserService();
|
||||
// Route to create a new user
|
||||
userRouter.post('/create', async (req, res) => {
|
||||
try {
|
||||
const user = await userService.createUser(req.body);
|
||||
res.status(201).json({ message: 'User created successfully'});
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error creating user', error });
|
||||
}
|
||||
});
|
||||
|
||||
// Route to authenticate a user
|
||||
userRouter.post('/authenticate', async (req, res) => {
|
||||
try {
|
||||
await userService.authenticateUser(req, res);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error authenticating user', error });
|
||||
}
|
||||
});
|
||||
|
||||
// Route to get user details (protected route)
|
||||
userRouter.get('/details', auth, async (req, res) => {
|
||||
try {
|
||||
await userService.getUserDetails(req, res);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error fetching user details', error });
|
||||
}
|
||||
});
|
||||
|
||||
// Route to update user details (protected route)
|
||||
userRouter.put('/update', auth, async (req, res) =>{
|
||||
try{
|
||||
await userService.updateUser(req, res);
|
||||
}
|
||||
catch (error) {
|
||||
res.status(500).json({ message: 'Error updating user', error });
|
||||
}
|
||||
});
|
||||
// Route to delete a user (protected route)
|
||||
userRouter.delete('/delete', auth, async (req, res) => {
|
||||
try {
|
||||
await userService.deleteUser(req, res);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error deleting user', error });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//export default userRouter;
|
||||
export { userRouter };
|
||||
@@ -0,0 +1,9 @@
|
||||
import Express from 'express';
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import express, { request } from 'express';
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.use(express.json())
|
||||
|
||||
app.listen(PORT, '0.0.0.0', ()=>{
|
||||
console.log(`Running on Port: ${PORT}`)
|
||||
});
|
||||
|
||||
app.get("/api/", (req, res) => {
|
||||
res.send("KÖRTE");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
console.log(process.env.DATABASE_URL_1 as string);
|
||||
import express from 'express';
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { mainRouter } from './router';
|
||||
import { AppDataSourceOne } from './DB/Datasource';
|
||||
|
||||
|
||||
const app = express();
|
||||
// Initialize the database connection
|
||||
AppDataSourceOne.initialize();
|
||||
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL,
|
||||
credentials: true
|
||||
}));
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use('/', mainRouter);
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on port 3000');
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
export function auth(req: Request, res: Response, next: NextFunction): void {
|
||||
// Read token from cookie named 'jwt'
|
||||
const token = req.cookies.jwt;
|
||||
|
||||
if (!token) {
|
||||
res.status(401).json({ message: "No token provided" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET || "secret");
|
||||
|
||||
if (typeof decoded === "string") {
|
||||
res.status(401).json({ message: "Invalid token payload" });
|
||||
return;
|
||||
}
|
||||
|
||||
req.user = decoded;
|
||||
|
||||
// Check if expiring soon & refresh if needed
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (decoded.exp && decoded.exp - now < 60 * 5) {
|
||||
const { iat, exp, ...payload } = decoded;
|
||||
const newToken = createToken(payload);
|
||||
res.cookie("jwt", newToken, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "none",
|
||||
maxAge: 60 * 60 * 1000, // 1 hour
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (err) {
|
||||
res.status(401).json({ message: "Invalid token" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export function createToken(user: any): string {
|
||||
return jwt.sign({ id: user.id, username: user.username }, process.env.JWT_SECRET || 'secret', {
|
||||
expiresIn: '1h',
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//hash password
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
return bcrypt.hash(password, salt);
|
||||
}
|
||||
|
||||
export async function comparePasswords(password: string, hash: string): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//collection of all routes
|
||||
import { Router } from 'express';
|
||||
import { userRouter } from '../User/UserRouter';
|
||||
// import { companyRouter } from '../User/CompanyRouter';
|
||||
|
||||
const router = Router();
|
||||
// Use the user router for user-related routes
|
||||
router.use('/user', userRouter);
|
||||
// Use the company router for company-related routes
|
||||
// router.use('/company', companyRouter);
|
||||
// Add more routers as needed
|
||||
// Export the main router
|
||||
export { router as mainRouter };
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "libReplacement": true, /* Enable lib replacement. */
|
||||
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
+14
-6
@@ -1,10 +1,18 @@
|
||||
POSTGRES_USER=Admin
|
||||
POSTGRES_PASSWORD=QwEr12345.
|
||||
POSTGRES_DB_1=Default
|
||||
POSTGRES_DB_2=USERS
|
||||
POSTGRES_DB_3=QUESTIONS
|
||||
POSTGRES_DB_4=STATISTICS
|
||||
DB_PASSWORD=QwEr12345.
|
||||
Default_User=Admin
|
||||
Default_Password=Admin.Admin
|
||||
# Database configuration
|
||||
DB_1=Default
|
||||
DB_2=QUESTIONS
|
||||
DB_3=STATISTICS
|
||||
DB_4=USERS
|
||||
|
||||
DB_PSWD_1=42vM2ftPy1YetSz9AdAHkayxKvQHuV9Wh0nT8c5DKYt
|
||||
DB_PSWD_2=3cYzPBoLtPindO53Eh3cA80DqqjYvq7CJOzA2Eik00k
|
||||
DB_PSWD_3=HyJcDLDW3ZYXbMLzlkL4zWqWwyhExQ4XvVCuT8ihld0
|
||||
DB_PSWD_4=965o6d3Mz7YlgI8GkJkiZD6PIpTZfaBzIheZX3nIpY3
|
||||
|
||||
# Ports configuration
|
||||
BACKEND_PORT=3000
|
||||
FRONTEND_PORT=5173
|
||||
DATABASE_PORT=80
|
||||
@@ -1,55 +0,0 @@
|
||||
-- Adminer 5.3.0 PostgreSQL 17.5 dump
|
||||
|
||||
DROP TABLE IF EXISTS "Code";
|
||||
DROP SEQUENCE IF EXISTS "Code_CODE_ID_seq";
|
||||
CREATE SEQUENCE "Code_CODE_ID_seq" INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
|
||||
|
||||
CREATE TABLE "public"."Code" (
|
||||
"CODE_ID" integer DEFAULT nextval('"Code_CODE_ID_seq"') NOT NULL,
|
||||
"CODE" character varying(60) NOT NULL,
|
||||
"Time" timestamp NOT NULL,
|
||||
CONSTRAINT "Code_pkey" PRIMARY KEY ("CODE_ID")
|
||||
)
|
||||
WITH (oids = false);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS "Company";
|
||||
DROP SEQUENCE IF EXISTS "Company_C_ID_seq";
|
||||
CREATE SEQUENCE "Company_C_ID_seq" INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
|
||||
|
||||
CREATE TABLE "public"."Company" (
|
||||
"C_ID" integer DEFAULT nextval('"Company_C_ID_seq"') NOT NULL,
|
||||
"Name" character varying(30) NOT NULL,
|
||||
"ContactName" character varying(30) NOT NULL,
|
||||
"ContactEmail" character varying(30) NOT NULL,
|
||||
"FirstAPI" text NOT NULL,
|
||||
"TokenAPI" text NOT NULL,
|
||||
CONSTRAINT "Company_pkey" PRIMARY KEY ("C_ID")
|
||||
)
|
||||
WITH (oids = false);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS "User";
|
||||
DROP SEQUENCE IF EXISTS "User_id_seq";
|
||||
CREATE SEQUENCE "User_id_seq" INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
|
||||
|
||||
CREATE TABLE "public"."User" (
|
||||
"id" integer DEFAULT nextval('"User_id_seq"') NOT NULL,
|
||||
"UName" character varying(30) NOT NULL,
|
||||
"PSWD" character varying(30) NOT NULL,
|
||||
"RegDate" timestamp NOT NULL,
|
||||
"Email" character varying(40) NOT NULL,
|
||||
"Name" character varying(30) NOT NULL,
|
||||
"AUTH" boolean DEFAULT false NOT NULL,
|
||||
"REG_CODE" integer NOT NULL,
|
||||
"C_Id" integer NOT NULL,
|
||||
"C_Token" character varying(60) NOT NULL,
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
)
|
||||
WITH (oids = false);
|
||||
|
||||
|
||||
ALTER TABLE ONLY "public"."User" ADD CONSTRAINT "User_C_Id_fkey" FOREIGN KEY ("C_Id") REFERENCES "Company"("C_ID") ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;
|
||||
ALTER TABLE ONLY "public"."User" ADD CONSTRAINT "User_REG_CODE_fkey" FOREIGN KEY ("REG_CODE") REFERENCES "Code"("CODE_ID") ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;
|
||||
|
||||
-- 2025-05-20 22:16:50 UTC
|
||||
@@ -10,18 +10,21 @@ COPY SerpentRace_Frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
################ BACKEND BUILD #################
|
||||
FROM base AS backend-build
|
||||
WORKDIR /usr/local/app/backend
|
||||
COPY SerpentRace_Backend/package.json SerpentRace_Backend/package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY SerpentRace_Backend/ ./
|
||||
# Copy frontend build output to backend static directory
|
||||
COPY --from=client-build /usr/local/app/frontend/dist ./src/static
|
||||
# FROM base AS backend-build
|
||||
# WORKDIR /usr/local/app/backend
|
||||
# COPY SerpentRace_Backend/package.json SerpentRace_Backend/package-lock.json ./
|
||||
# RUN npm ci
|
||||
# COPY SerpentRace_Backend/ ./
|
||||
# # Copy frontend build output to backend static directory
|
||||
# COPY --from=client-build /usr/local/app/frontend/dist ./src/static
|
||||
|
||||
# generate js files from ts files
|
||||
RUN npm run build
|
||||
|
||||
################ PRODUCTION IMAGE #################
|
||||
FROM backend-build AS prod
|
||||
WORKDIR /usr/local/app/backend
|
||||
ENV NODE_ENV=production
|
||||
RUN npm ci --only=production
|
||||
ENV NODE_ENV=development
|
||||
RUN npm ci
|
||||
EXPOSE 3000
|
||||
CMD ["node", "src/index.mjs"]
|
||||
CMD ["node", "dist/index.js"]
|
||||
@@ -7,32 +7,34 @@ services:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: SerpentRace_Docker/Dockerfile
|
||||
target: prod
|
||||
environment:
|
||||
POSTGRES_HOST: db
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB_1}
|
||||
DB_1: ${POSTGRES_DB_2}
|
||||
DB_2: ${POSTGRES_DB_3}
|
||||
DB_3: ${POSTGRES_DB_4}
|
||||
command: ["npx", "nodemon", "src/index.mjs"]
|
||||
develop:
|
||||
watch:
|
||||
- path: ../SerpentRace_Backend/src
|
||||
action: sync
|
||||
target: /usr/local/app/backend/src
|
||||
- path: ../SerpentRace_Backend/package.json
|
||||
action: rebuild
|
||||
labels:
|
||||
traefik.http.routers.backend.rule: Host(`localhost`) && PathPrefix(`/api`)
|
||||
traefik.http.services.backend.loadbalancer.server.port: 3000
|
||||
depends_on:
|
||||
- db
|
||||
# backend:
|
||||
# build:
|
||||
# context: ..
|
||||
# dockerfile: SerpentRace_Docker/Dockerfile
|
||||
# target: prod
|
||||
# environment:
|
||||
# DATABASE_URL_1: "mysql://${DB_1}:${DB_PSWD_1}@db:3306/${DB_1}"
|
||||
# DATABASE_URL_2: "mysql://${DB_2}:${DB_PSWD_2}@db:3306/${DB_2}"
|
||||
# DATABASE_URL_3: "mysql://${DB_3}:${DB_PSWD_3}@db:3306/${DB_3}"
|
||||
# DATABASE_URL_4: "mysql://${DB_4}:${DB_PSWD_4}@db:3306/${DB_4}"
|
||||
# command: ["npx", "nodemon", "src/index.ts", "--watch", "src", "--ext", "ts"]
|
||||
# develop:
|
||||
# watch:
|
||||
# - path: ../SerpentRace_Backend/src
|
||||
# action: sync
|
||||
# target: /usr/local/app/backend/src
|
||||
# - path: ../SerpentRace_Backend/prisma
|
||||
# action: sync
|
||||
# target: /usr/local/app/backend/prisma
|
||||
# actions:
|
||||
# - migrate:all
|
||||
# - path: ../SerpentRace_Backend/package.json
|
||||
# action: rebuild
|
||||
# labels:
|
||||
# traefik.http.routers.backend.rule: Host(`api.localhost`)
|
||||
# traefik.http.services.backend.loadbalancer.server.port: 3000
|
||||
# depends_on:
|
||||
# - db
|
||||
|
||||
frontend:
|
||||
build:
|
||||
@@ -53,25 +55,33 @@ services:
|
||||
traefik.http.services.frontend.loadbalancer.server.port: 5173
|
||||
|
||||
db:
|
||||
image: postgres
|
||||
image: mariadb:latest
|
||||
restart: always
|
||||
shm_size: 128mb
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB_1}
|
||||
DB_1: ${POSTGRES_DB_2}
|
||||
DB_2: ${POSTGRES_DB_3}
|
||||
DB_3: ${POSTGRES_DB_4}
|
||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
|
||||
MYSQL_DATABASE: ${DB_1}
|
||||
MYSQL_USER: ${Default_User}
|
||||
MYSQL_PASSWORD: ${Default_Password}
|
||||
DB_1: ${DB_1}
|
||||
DB_2: ${DB_2}
|
||||
DB_3: ${DB_3}
|
||||
DB_4: ${DB_4}
|
||||
PSWD_1: ${DB_PSWD_1}
|
||||
PSWD_2: ${DB_PSWD_2}
|
||||
PSWD_3: ${DB_PSWD_3}
|
||||
PSWD_4: ${DB_PSWD_4}
|
||||
volumes:
|
||||
- ./init-multi-db.sh:/docker-entrypoint-initdb.d/init-multi-db.sh:ro
|
||||
- ./Default.sql:/docker-entrypoint-initdb.d/Default.sql:ro
|
||||
- ./init-db-users.sh:/docker-entrypoint-initdb.d/init-db-users.sh:ro
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
ports:
|
||||
- "8080:8080"
|
||||
labels:
|
||||
traefik.http.routers.adminer.rule: Host(`db.localhost`)
|
||||
traefik.http.services.adminer.loadbalancer.server.port: 8080
|
||||
traefik.http.services.adminer.loadbalancer.server.port: 8080
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" <<EOSQL
|
||||
CREATE USER IF NOT EXISTS '$DB_1'@'%' IDENTIFIED BY '$PSWD_1';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_1\`.* TO '$DB_1'@'%';
|
||||
|
||||
CREATE USER IF NOT EXISTS '$DB_2'@'%' IDENTIFIED BY '$PSWD_2';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_2\`.* TO '$DB_2'@'%';
|
||||
|
||||
CREATE USER IF NOT EXISTS '$DB_3'@'%' IDENTIFIED BY '$PSWD_3';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_3\`.* TO '$DB_3'@'%';
|
||||
|
||||
CREATE USER IF NOT EXISTS '$DB_4'@'%' IDENTIFIED BY '$PSWD_4';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_4\`.* TO '$DB_4'@'%';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
EOSQL
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE DATABASE "${DB_1}";
|
||||
CREATE DATABASE "${DB_2}";
|
||||
CREATE DATABASE "${DB_3}";
|
||||
mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" <<EOSQL
|
||||
CREATE DATABASE IF NOT EXISTS \`${DB_2}\`;
|
||||
CREATE DATABASE IF NOT EXISTS \`${DB_3}\`;
|
||||
CREATE DATABASE IF NOT EXISTS \`${DB_4}\`;
|
||||
EOSQL
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "SzeSnake",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
Reference in New Issue
Block a user