Moved to a "cleaner" directory structure

ref #11
This commit is contained in:
2020-12-03 20:38:47 +01:00
parent 3a04bb54bd
commit e8727ca922
27 changed files with 145 additions and 145 deletions

View File

@@ -0,0 +1,87 @@
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsPostalCode,
IsString,
} from "class-validator";
import { Participant } from "./Participant";
import { RunnerOrganisation } from "./RunnerOrganisation";
/**
* Defines a address (to be used for contact information).
*/
@Entity()
export class Address {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The address's description.
*/
@Column({nullable: true})
@IsString()
@IsOptional()
description?: string;
/**
* The address's first line.
* Containing the street and house number.
*/
@Column()
@IsString()
@IsNotEmpty()
address1: string;
/**
* The address's second line.
* Containing optional information.
*/
@Column({nullable: true})
@IsString()
@IsOptional()
address2?: string;
/**
* The address's postal code.
*/
@Column()
@IsString()
@IsNotEmpty()
@IsPostalCode("DE")
postalcode: string;
/**
* The address's city.
*/
@Column()
@IsString()
@IsNotEmpty()
city: string;
/**
* The address's country.
*/
@Column()
@IsString()
@IsNotEmpty()
country: string;
/**
* Used to link the address to participants.
*/
@OneToMany(() => Participant, participant => participant.address, { nullable: true })
participants: Participant[];
/**
* Used to link the address to runner groups.
*/
@OneToMany(() => RunnerOrganisation, group => group.address, { nullable: true })
groups: RunnerOrganisation[];
}

View File

@@ -0,0 +1,41 @@
import { Entity, Column, ManyToOne, ChildEntity } from "typeorm";
import { IsInt, IsNotEmpty, IsPositive, } from "class-validator";
import { Donation } from "./Donation";
import { Runner } from "./Runner";
/**
* Defines a distance based donation.
* Here people donate a certain amout per kilometer
*/
@ChildEntity()
export class DistanceDonation extends Donation {
/**
* The runner associated.
*/
@IsNotEmpty()
@ManyToOne(() => Runner, runner => runner.distanceDonations, { nullable: true })
runner: Runner;
/**
* The amount the donor set to be donated per kilometer that the runner ran.
*/
@Column()
@IsInt()
@IsPositive()
amountPerDistance: number;
/**
* The donation's amount in cents (or whatever your currency's smallest unit is.).
* The exact implementation may differ for each type of donation.
*/
@IsInt()
public get amount(): number {
let calculatedAmount = -1;
try {
calculatedAmount = this.amountPerDistance * this.runner.distance;
} catch (error) {
throw error;
}
return calculatedAmount;
}
}

View File

@@ -0,0 +1,36 @@
import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
} from "class-validator";
import { Participant } from "./Participant";
/**
* Defines the donation interface.
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class Donation {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The donations's donor.
*/
@IsNotEmpty()
@ManyToOne(() => Participant, donor => donor.donations, { nullable: true })
donor: Participant;
/**
* The donation's amount in cents (or whatever your currency's smallest unit is.).
* The exact implementation may differ for each type of donation.
*/
abstract amount: number;
}

View File

@@ -0,0 +1,17 @@
import { Entity, Column, ChildEntity } from "typeorm";
import { IsBoolean } from "class-validator";
import { Participant } from "./Participant";
/**
* Defines a donor.
*/
@ChildEntity()
export class Donor extends Participant {
/**
* Does this donor need a receipt?.
* Default: false
*/
@Column()
@IsBoolean()
receiptNeeded: boolean = false;
}

View File

@@ -0,0 +1,18 @@
import { Entity, Column, ChildEntity } from "typeorm";
import { IsInt, IsPositive, } from "class-validator";
import { Donation } from "./Donation";
/**
* Defines a fixed donation.
*/
@ChildEntity()
export class FixedDonation extends Donation {
/**
* The donation's amount in cents (or whatever your currency's smallest unit is.).
*/
@Column()
@IsInt()
@IsPositive()
amount: number;
}

View File

@@ -0,0 +1,90 @@
import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm";
import {
IsEmail,
IsInt,
IsNotEmpty,
IsOptional,
IsPhoneNumber,
IsPositive,
IsString,
} from "class-validator";
import { Address } from "./Address";
import { Donation } from "./Donation";
import { RunnerGroup } from "./RunnerGroup";
/**
* Defines a group's contact.
*/
@Entity()
export class GroupContact {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The contact's first name.
*/
@Column()
@IsNotEmpty()
@IsString()
firstname: string;
/**
* The contact's middle name.
* Optional
*/
@Column({nullable: true})
@IsOptional()
@IsString()
middlename?: string;
/**
* The contact's last name.
*/
@Column()
@IsNotEmpty()
@IsString()
lastname: string;
/**
* The contact's address.
* Optional
*/
@IsOptional()
@ManyToOne(() => Address, address => address.participants, { nullable: true })
address?: Address;
/**
* The contact's phone number.
* Optional
*/
@Column({nullable: true})
@IsOptional()
@IsPhoneNumber("DE")
phone?: string;
/**
* The contact's email address.
* Optional
*/
@Column({nullable: true})
@IsOptional()
@IsEmail()
email?: string;
/**
* Used to link the contact as the donor of a donation.
*/
@OneToMany(() => Donation, donation => donation.donor, { nullable: true })
donations: Donation[];
/**
* Used to link runners to donations.
*/
@OneToMany(() => RunnerGroup, group => group.contact, { nullable: true })
groups: RunnerGroup[];
}

View File

@@ -0,0 +1,83 @@
import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm";
import {
IsEmail,
IsInt,
IsNotEmpty,
IsOptional,
IsPhoneNumber,
IsPositive,
IsString,
} from "class-validator";
import { Address } from "./Address";
import { Donation } from "./Donation";
/**
* Defines the participant interface.
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class Participant {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The participant's first name.
*/
@Column()
@IsNotEmpty()
@IsString()
firstname: string;
/**
* The participant's middle name.
* Optional
*/
@Column({ nullable: true })
@IsOptional()
@IsString()
middlename?: string;
/**
* The participant's last name.
*/
@Column()
@IsNotEmpty()
@IsString()
lastname: string;
/**
* The participant's address.
* Optional
*/
@ManyToOne(() => Address, address => address.participants, { nullable: true })
address?: Address;
/**
* The participant's phone number.
* Optional
*/
@Column({ nullable: true })
@IsOptional()
@IsPhoneNumber("DE")
phone?: string;
/**
* The participant's email address.
* Optional
*/
@Column({ nullable: true })
@IsOptional()
@IsEmail()
email?: string;
/**
* Used to link the participant as the donor of a donation.
*/
@OneToMany(() => Donation, donation => donation.donor, { nullable: true })
donations: Donation[];
}

View File

@@ -0,0 +1,50 @@
import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { User } from './User';
import { UserGroup } from './UserGroup';
/**
* Defines the Permission interface.
*/
@Entity()
export abstract class Permission {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* users
*/
@OneToMany(() => User, user => user.permissions, { nullable: true })
users: User[]
/**
* groups
*/
@OneToMany(() => UserGroup, group => group.permissions, { nullable: true })
groups: UserGroup[]
/**
* The target
*/
@Column()
@IsNotEmpty()
@IsString()
target: string;
/**
* The action type
*/
@Column()
@IsNotEmpty()
@IsString()
action: string;
}

View File

@@ -0,0 +1,44 @@
import { Entity, Column, OneToMany, ManyToOne, ChildEntity } from "typeorm";
import { IsInt, IsNotEmpty, } from "class-validator";
import { Participant } from "./Participant";
import { RunnerGroup } from "./RunnerGroup";
import { DistanceDonation } from "./DistanceDonation";
import { RunnerCard } from "./RunnerCard";
import { Scan } from "./Scan";
/**
* Defines a runner.
*/
@ChildEntity()
export class Runner extends Participant {
/**
* The runner's associated group.
*/
@IsNotEmpty()
@ManyToOne(() => RunnerGroup, group => group.runners, { nullable: true })
group: RunnerGroup;
/**
* Used to link runners to donations.
*/
@OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner, { nullable: true })
distanceDonations: DistanceDonation[];
/**
* Used to link runners to cards.
*/
@OneToMany(() => RunnerCard, card => card.runner, { nullable: true })
cards: RunnerCard[];
/**
* Used to link runners to a scans
*/
@OneToMany(() => Scan, scan => scan.runner, { nullable: true })
scans: Scan[];
@IsInt()
public get distance(): number {
return this.scans.filter(scan => scan.valid === true).reduce((sum, current) => sum + current.distance, 0);
}
}

View File

@@ -0,0 +1,57 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm";
import {
IsBoolean,
IsEAN,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { Runner } from "./Runner";
import { TrackScan } from "./TrackScan";
/**
* Defines a card that can be scanned via a scanner station.
*/
@Entity()
export class RunnerCard {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The runner that is currently associated with this card.
*/
@IsOptional()
@ManyToOne(() => Runner, runner => runner.cards, { nullable: true })
runner: Runner;
/**
* The card's code.
* This has to be able to being converted to something barcode compatible.
* could theoretically be autogenerated
*/
@Column()
@IsEAN()
@IsString()
@IsNotEmpty()
code: string;
/**
* Is the card enabled (for fraud reasons)?
* Default: true
*/
@Column()
@IsBoolean()
enabled: boolean = true;
/**
* Used to link cards to a track scans.
*/
@OneToMany(() => TrackScan, scan => scan.track, { nullable: true })
scans: TrackScan[];
}

View File

@@ -0,0 +1,47 @@
import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { GroupContact } from "./GroupContact";
import { Runner } from "./Runner";
import { RunnerTeam } from "./RunnerTeam";
/**
* Defines the runnerGroup interface.
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class RunnerGroup {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The group's first name.
*/
@Column()
@IsNotEmpty()
@IsString()
name: string;
/**
* The group's contact.
* Optional
*/
@IsOptional()
@ManyToOne(() => GroupContact, contact => contact.groups, { nullable: true })
contact?: GroupContact;
/**
* Used to link runners to a runner group.
*/
@OneToMany(() => Runner, runner => runner.group, { nullable: true })
runners: Runner[];
}

View File

@@ -0,0 +1,26 @@
import { Entity, Column, ManyToOne, OneToMany, ChildEntity } from "typeorm";
import { IsOptional, } from "class-validator";
import { RunnerGroup } from "./RunnerGroup";
import { Address } from "./Address";
import { RunnerTeam } from "./RunnerTeam";
/**
* Defines a runner organisation (business or school for example).
*/
@ChildEntity()
export class RunnerOrganisation extends RunnerGroup {
/**
* The organisations's address.
* Optional
*/
@IsOptional()
@ManyToOne(() => Address, address => address.groups, { nullable: true })
address?: Address;
/**
* Used to link teams to runner groups.
*/
@OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true })
teams: RunnerTeam[];
}

View File

@@ -0,0 +1,19 @@
import { Entity, Column, ManyToOne, ChildEntity } from "typeorm";
import { IsNotEmpty } from "class-validator";
import { RunnerGroup } from "./RunnerGroup";
import { RunnerOrganisation } from "./RunnerOrganisation";
/**
* Defines a runner team (class or deparment for example).
*/
@ChildEntity()
export class RunnerTeam extends RunnerGroup {
/**
* The team's parent group.
* Optional
*/
@IsNotEmpty()
@ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: true })
parentGroup?: RunnerOrganisation;
}

View File

@@ -0,0 +1,54 @@
import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm";
import {
IsBoolean,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
} from "class-validator";
import { Runner } from "./Runner";
/**
* Defines the scan interface.
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class Scan {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The associated runner.
*/
@IsNotEmpty()
@ManyToOne(() => Runner, runner => runner.scans, { nullable: true })
runner: Runner;
/**
* The scan's distance in meters.
*/
@IsInt()
@IsPositive()
abstract distance: number;
/**
* Is the scan valid (for fraud reasons).
* Default: true
*/
@Column()
@IsBoolean()
valid: boolean = true;
/**
* seconds since last scan
*/
@IsInt()
@IsOptional()
secondsSinceLastScan: number;
}

View File

@@ -0,0 +1,61 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm";
import {
IsBoolean,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { Track } from "./Track";
import { TrackScan } from "./TrackScan";
/**
* ScannerStations have the ability to create scans for specific tracks.
*/
@Entity()
export class ScanStation {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* The station's description.
*/
@Column({nullable: true})
@IsOptional()
@IsString()
description?: string;
/**
* The track this station is associated with.
*/
@IsNotEmpty()
@ManyToOne(() => Track, track => track.stations, { nullable: true })
track: Track;
/**
* The station's api key.
*/
@Column()
@IsNotEmpty()
@IsString()
key: string;
/**
* Is the station enabled (for fraud reasons)?
* Default: true
*/
@Column()
@IsBoolean()
enabled: boolean = true;
/**
* Used to link track scans to a scan station.
*/
@OneToMany(() => TrackScan, scan => scan.track, { nullable: true })
scans: TrackScan[];
}

View File

@@ -0,0 +1,52 @@
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
IsString,
} from "class-validator";
import { ScanStation } from "./ScanStation";
import { TrackScan } from "./TrackScan";
/**
* Defines a track of given length.
*/
@Entity()
export class Track {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;;
/**
* The track's name.
*/
@Column()
@IsString()
@IsNotEmpty()
name: string;
/**
* The track's length/distance in meters.
*/
@Column()
@IsInt()
@IsPositive()
distance: number;
/**
* Used to link scan stations to track.
*/
@OneToMany(() => ScanStation, station => station.track, { nullable: true })
stations: ScanStation[];
/**
* Used to link track scans to a track.
*/
@OneToMany(() => TrackScan, scan => scan.track, { nullable: true })
scans: TrackScan[];
}

View File

@@ -0,0 +1,58 @@
import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, ChildEntity } from "typeorm";
import {
IsBoolean,
IsDateString,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
} from "class-validator";
import { Scan } from "./Scan";
import { Runner } from "./Runner";
import { Track } from "./Track";
import { RunnerCard } from "./RunnerCard";
import { ScanStation } from "./ScanStation";
/**
* Defines the scan interface.
*/
@ChildEntity()
export class TrackScan extends Scan {
/**
* The associated track.
*/
@IsNotEmpty()
@ManyToOne(() => Track, track => track.scans, { nullable: true })
track: Track;
/**
* The associated card.
*/
@IsNotEmpty()
@ManyToOne(() => RunnerCard, card => card.scans, { nullable: true })
card: RunnerCard;
/**
* The scanning station.
*/
@IsNotEmpty()
@ManyToOne(() => ScanStation, station => station.scans, { nullable: true })
station: ScanStation;
/**
* The scan's distance in meters.
*/
@IsInt()
@IsPositive()
public get distance(): number {
return this.track.distance;
}
/**
* The scan's creation timestamp.
*/
@Column()
@IsDateString()
@IsNotEmpty()
timestamp: string;
}

130
src/models/entities/User.ts Normal file
View File

@@ -0,0 +1,130 @@
import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm";
import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator";
import { UserGroup } from './UserGroup';
import { Permission } from './Permission';
import { UserAction } from './UserAction';
/**
* Defines a admin user.
*/
@Entity()
export class User {
/**
* autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* autogenerated uuid
*/
@IsOptional()
@IsInt()
@Generated("uuid")
uuid: string;
/**
* user email
*/
@IsEmail()
email: string;
/**
* user phone
*/
@IsPhoneNumber("ZZ")
@IsOptional()
phone: string;
/**
* username
*/
@IsString()
username: string;
/**
* firstname
*/
@IsString()
@IsNotEmpty()
firstname: string;
/**
* middlename
*/
@IsString()
@IsOptional()
middlename: string;
/**
* lastname
*/
@IsString()
@IsNotEmpty()
lastname: string;
/**
* password
*/
@IsString()
@IsNotEmpty()
password: string;
/**
* permissions
*/
@ManyToOne(() => Permission, permission => permission.users, { nullable: true })
permissions: Permission[];
/**
* groups
*/
@ManyToMany(() => UserGroup)
@JoinTable()
groups: UserGroup[];
/**
* is user enabled?
*/
@IsBoolean()
enabled: boolean;
/**
* jwt refresh count
*/
@IsInt()
@Column({ default: 1 })
refreshTokenCount: number;
/**
* profilepic
*/
@IsString()
profilepic: string;
/**
* actions
*/
@OneToMany(() => UserAction, action => action.user)
actions: UserAction
/**
* calculate all permissions
*/
public get calc_permissions(): Permission[] {
let final_permissions = []
this.groups.forEach((permission) => {
if (!final_permissions.includes(permission)) {
final_permissions.push(permission)
}
})
this.permissions.forEach((permission) => {
if (!final_permissions.includes(permission)) {
final_permissions.push(permission)
}
})
return final_permissions
}
}

View File

@@ -0,0 +1,52 @@
import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { User } from './User';
/**
* Defines the UserAction interface.
*/
@Entity()
export class UserAction {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* user
*/
@ManyToOne(() => User, user => user.actions)
user: User
/**
* The actions's target (e.g. Track#2)
*/
@Column()
@IsNotEmpty()
@IsString()
target: string;
/**
* The actions's action (e.g. UPDATE)
*/
@Column()
@IsNotEmpty()
@IsString()
action: string;
/**
* The description of change (before-> after; e.g. distance:15->17)
*/
@Column({ nullable: true })
@IsOptional()
@IsString()
changed: string;
}

View File

@@ -0,0 +1,44 @@
import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm";
import {
IsInt,
IsNotEmpty,
IsOptional,
IsString,
} from "class-validator";
import { Permission } from "./Permission";
/**
* Defines the UserGroup interface.
*/
@Entity()
export abstract class UserGroup {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsOptional()
@IsInt()
id: number;
/**
* permissions
*/
@ManyToOne(() => Permission, permission => permission.groups, { nullable: true })
permissions: Permission[];
/**
* The group's name
*/
@Column()
@IsNotEmpty()
@IsString()
name: string;
/**
* The group's description
*/
@Column({nullable: true})
@IsOptional()
@IsString()
description?: string;
}