diff --git a/client/angular-proxy.json b/client/angular-proxy.json new file mode 100644 index 0000000..009373c --- /dev/null +++ b/client/angular-proxy.json @@ -0,0 +1,6 @@ +{ + "/Api": { + "target": "http://localhost:4201/", + "secure": false + } +} diff --git a/client/angular.json b/client/angular.json index a521dc8..92c90da 100644 --- a/client/angular.json +++ b/client/angular.json @@ -23,13 +23,8 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "aot": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], "scripts": [] }, "configurations": { @@ -66,7 +61,8 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "rdt-client:build" + "browserTarget": "rdt-client:build", + "proxyConfig": "angular-proxy.json" }, "configurations": { "production": { @@ -87,13 +83,8 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], "scripts": [] } }, @@ -105,9 +96,7 @@ "tsconfig.spec.json", "e2e/tsconfig.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } }, "e2e": { @@ -123,6 +112,7 @@ } } } - }}, + } + }, "defaultProject": "rdt-client" } diff --git a/client/package-lock.json b/client/package-lock.json index ae1d2c7..8b3e947 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1371,6 +1371,11 @@ "to-fast-properties": "^2.0.0" } }, + "@fortawesome/fontawesome-free": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz", + "integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==" + }, "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", @@ -2659,6 +2664,11 @@ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", "dev": true }, + "bulma": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.1.tgz", + "integrity": "sha512-Afi2zv4DKmNSYfmx55V+Mtnt8+WfR8Rs65kWArmzEuWP7vNr7dSAEDI+ORZlgOR1gueNZwpKaPdUi4ZiTNwgPA==" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -4807,6 +4817,11 @@ "minimatch": "^3.0.3" } }, + "filesize": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", + "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -7377,6 +7392,14 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "ngx-filesize": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/ngx-filesize/-/ngx-filesize-2.0.13.tgz", + "integrity": "sha512-rdOJMmJTou8tjiMLqxybDD1SWrQETbioXE9InlxABuxGJO0eEWzLfXXxMvNj2o92Wo6YqbkkQwi2KEOk7XvFZw==", + "requires": { + "filesize": ">= 4.0.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/client/package.json b/client/package.json index aefddd8..d4695e8 100644 --- a/client/package.json +++ b/client/package.json @@ -19,6 +19,9 @@ "@angular/platform-browser": "~9.1.0", "@angular/platform-browser-dynamic": "~9.1.0", "@angular/router": "~9.1.0", + "@fortawesome/fontawesome-free": "^5.13.0", + "bulma": "^0.8.1", + "ngx-filesize": "^2.0.13", "rxjs": "~6.5.4", "tslib": "^1.10.0", "zone.js": "~0.10.2" diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index defa9f2..6dd65c9 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -1,10 +1,23 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; +import { MainLayoutComponent } from './main-layout/main-layout.component'; +import { LoginComponent } from './login/login.component'; +import { AuthGuard } from './auth.guard'; -const routes: Routes = []; +const routes: Routes = [ + { + path: 'login', + component: LoginComponent, + }, + { + path: '', + component: MainLayoutComponent, + canActivate: [AuthGuard], + }, +]; @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] + exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 07b5f6b..3f25e4c 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + template: '', + styles: [] }) export class AppComponent {} diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 70dd2dd..ceaad1d 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -1,13 +1,44 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; +import { NgxFilesizeModule, FileSizePipe } from 'ngx-filesize'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { MainLayoutComponent } from './main-layout/main-layout.component'; +import { NavbarComponent } from './navbar/navbar.component'; +import { AddNewTorrentComponent } from './navbar/add-new-torrent/add-new-torrent.component'; +import { TorrentTableComponent } from './torrent-table/torrent-table.component'; +import { TorrentRowComponent } from './torrent-row/torrent-row.component'; +import { TorrentFileComponent } from './torrent-file/torrent-file.component'; +import { SettingsComponent } from './navbar/settings/settings.component'; +import { TorrentStatusPipe } from './torrent-status.pipe'; +import { FileStatusPipe } from './file-status.pipe'; +import { LoginComponent } from './login/login.component'; @NgModule({ - declarations: [AppComponent], - imports: [BrowserModule, AppRoutingModule], - providers: [], - bootstrap: [AppComponent] + declarations: [ + AppComponent, + MainLayoutComponent, + NavbarComponent, + AddNewTorrentComponent, + TorrentTableComponent, + TorrentRowComponent, + TorrentFileComponent, + SettingsComponent, + TorrentStatusPipe, + FileStatusPipe, + LoginComponent, + ], + imports: [ + BrowserModule, + AppRoutingModule, + FormsModule, + HttpClientModule, + NgxFilesizeModule, + ], + providers: [FileSizePipe], + bootstrap: [AppComponent], }) export class AppModule {} diff --git a/client/src/app/auth.guard.ts b/client/src/app/auth.guard.ts new file mode 100644 index 0000000..981d41c --- /dev/null +++ b/client/src/app/auth.guard.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { + CanActivate, + ActivatedRouteSnapshot, + RouterStateSnapshot, + Router, +} from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { UsersService } from './users.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthGuard implements CanActivate { + constructor(private router: Router, private usersService: UsersService) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | boolean { + return true; + + //this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); + //return false; + } +} diff --git a/client/src/app/file-status.pipe.ts b/client/src/app/file-status.pipe.ts new file mode 100644 index 0000000..f81a746 --- /dev/null +++ b/client/src/app/file-status.pipe.ts @@ -0,0 +1,25 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { TorrentFile } from './models/torrent.model'; +import { DownloadStatus } from './models/download.model'; + +@Pipe({ + name: 'fileStatus', +}) +export class FileStatusPipe implements PipeTransform { + transform(value: TorrentFile): string { + if ( + !value.download || + value.download.status === DownloadStatus.PendingDownload + ) { + return 'Pending'; + } + + if (value.download.status === DownloadStatus.Downloading) { + return `${value.download.progress}%`; + } + + if (value.download.status === DownloadStatus.Finished) { + return `Finished`; + } + } +} diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html new file mode 100644 index 0000000..b903e79 --- /dev/null +++ b/client/src/app/login/login.component.html @@ -0,0 +1,36 @@ +
+
+
+
+
+ +
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
+
+ +
+
+
+
+
+
+
diff --git a/client/src/app/app.component.html b/client/src/app/login/login.component.scss similarity index 100% rename from client/src/app/app.component.html rename to client/src/app/login/login.component.scss diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts new file mode 100644 index 0000000..c74528f --- /dev/null +++ b/client/src/app/login/login.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/client/src/app/main-layout/main-layout.component.html b/client/src/app/main-layout/main-layout.component.html new file mode 100644 index 0000000..1ad0a23 --- /dev/null +++ b/client/src/app/main-layout/main-layout.component.html @@ -0,0 +1,6 @@ + +
+
+ +
+
diff --git a/client/src/app/main-layout/main-layout.component.scss b/client/src/app/main-layout/main-layout.component.scss new file mode 100644 index 0000000..1ccab01 --- /dev/null +++ b/client/src/app/main-layout/main-layout.component.scss @@ -0,0 +1,3 @@ +.main-list { + padding-top: 6em; +} diff --git a/client/src/app/main-layout/main-layout.component.ts b/client/src/app/main-layout/main-layout.component.ts new file mode 100644 index 0000000..626457a --- /dev/null +++ b/client/src/app/main-layout/main-layout.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-main-layout', + templateUrl: './main-layout.component.html', + styleUrls: ['./main-layout.component.scss'] +}) +export class MainLayoutComponent implements OnInit { + constructor() {} + + ngOnInit(): void {} +} diff --git a/client/src/app/models/download.model.ts b/client/src/app/models/download.model.ts new file mode 100644 index 0000000..663b456 --- /dev/null +++ b/client/src/app/models/download.model.ts @@ -0,0 +1,19 @@ +export class Download { + public downloadId: string; + + public torrentId: string; + + public link: string; + + public added: Date; + + public status: DownloadStatus; + + public progress: number; +} + +export enum DownloadStatus { + PendingDownload = 0, + Downloading, + Finished, +} diff --git a/client/src/app/models/profile.model.ts b/client/src/app/models/profile.model.ts new file mode 100644 index 0000000..cc3a993 --- /dev/null +++ b/client/src/app/models/profile.model.ts @@ -0,0 +1,4 @@ +export class Profile { + userName: string; + expiration: Date; +} diff --git a/client/src/app/models/setting.model.ts b/client/src/app/models/setting.model.ts new file mode 100644 index 0000000..7e8c8d9 --- /dev/null +++ b/client/src/app/models/setting.model.ts @@ -0,0 +1,4 @@ +export class Setting { + public settingId: string; + public value: string; +} diff --git a/client/src/app/models/torrent.model.ts b/client/src/app/models/torrent.model.ts new file mode 100644 index 0000000..1700ee9 --- /dev/null +++ b/client/src/app/models/torrent.model.ts @@ -0,0 +1,41 @@ +import { Download } from './download.model'; + +export class Torrent { + torrentId: string; + hash: string; + status: TorrentStatus; + rdId: string; + rdName: string; + rdSize: number; + rdHost: string; + rdSplit: number; + rdProgress: number; + rdStatus: string; + rdAdded: Date; + rdEnded: Date; + rdSpeed: number; + rdSeeders: number; + rdFiles: string; + + files: TorrentFile[]; + downloads: Download[]; + downloadProgress: number; +} + +export class TorrentFile { + id: string; + path: string; + bytes: string; + selected: boolean; + + download: Download; +} + +export enum TorrentStatus { + RealDebrid = 0, + WaitingForDownload, + Downloading, + Finished, + + Error = 99, +} diff --git a/client/src/app/navbar/add-new-torrent/add-new-torrent.component.html b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.html new file mode 100644 index 0000000..ee22118 --- /dev/null +++ b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.html @@ -0,0 +1,66 @@ + diff --git a/client/src/app/navbar/add-new-torrent/add-new-torrent.component.scss b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.scss new file mode 100644 index 0000000..3cef3d3 --- /dev/null +++ b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.scss @@ -0,0 +1,4 @@ +.file-name { + max-width: 24em; + width: 24em; +} diff --git a/client/src/app/navbar/add-new-torrent/add-new-torrent.component.ts b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.ts new file mode 100644 index 0000000..72397c0 --- /dev/null +++ b/client/src/app/navbar/add-new-torrent/add-new-torrent.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { TorrentService } from 'src/app/torrent.service'; + +@Component({ + selector: 'app-add-new-torrent', + templateUrl: './add-new-torrent.component.html', + styleUrls: ['./add-new-torrent.component.scss'] +}) +export class AddNewTorrentComponent implements OnInit { + @Input() + public get open(): boolean { + return this.isActive; + } + + public set open(val: boolean) { + this.reset(); + this.isActive = val; + } + + @Output() + public openChange = new EventEmitter(); + + public isActive = false; + + public fileName: string; + public magnetLink: string; + public saving = false; + public error: string; + + private selectedFile: File; + + constructor(private torrentService: TorrentService) {} + + ngOnInit(): void {} + + public reset(): void { + this.fileName = ''; + this.magnetLink = ''; + this.saving = false; + this.selectedFile = null; + this.error = null; + } + + public pickFile(evt: Event): void { + const files = (evt.target as HTMLInputElement).files; + + if (files.length === 0) { + return; + } + + const file = files[0]; + + this.fileName = file.name; + + this.selectedFile = file; + } + + public ok(): void { + this.saving = true; + this.error = null; + + if (this.magnetLink) { + this.torrentService.uploadMagnet(this.magnetLink).subscribe( + () => { + this.cancel(); + }, + err => { + this.error = err.error; + this.saving = false; + } + ); + } else if (this.selectedFile) { + this.torrentService.uploadFile(this.selectedFile).subscribe( + () => { + this.cancel(); + }, + err => { + this.error = err.error; + this.saving = false; + } + ); + } else { + this.cancel(); + } + } + + public cancel(): void { + this.isActive = false; + this.openChange.emit(this.open); + } +} diff --git a/client/src/app/navbar/navbar.component.html b/client/src/app/navbar/navbar.component.html new file mode 100644 index 0000000..27d3abe --- /dev/null +++ b/client/src/app/navbar/navbar.component.html @@ -0,0 +1,77 @@ + + + + diff --git a/client/src/app/app.component.scss b/client/src/app/navbar/navbar.component.scss similarity index 100% rename from client/src/app/app.component.scss rename to client/src/app/navbar/navbar.component.scss diff --git a/client/src/app/navbar/navbar.component.ts b/client/src/app/navbar/navbar.component.ts new file mode 100644 index 0000000..1f3b3f1 --- /dev/null +++ b/client/src/app/navbar/navbar.component.ts @@ -0,0 +1,26 @@ +import { Component, OnInit } from '@angular/core'; +import { TorrentService } from '../torrent.service'; +import { SettingsService } from '../settings.service'; +import { Profile } from '../models/profile.model'; + +@Component({ + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'], +}) +export class NavbarComponent implements OnInit { + public showMobileMenu = false; + + public showNewTorrent = false; + public showSettings = false; + + public profile: Profile; + + constructor(private settingsService: SettingsService) {} + + ngOnInit(): void { + this.settingsService.getProfile().subscribe((result) => { + this.profile = result; + }); + } +} diff --git a/client/src/app/navbar/settings/settings.component.html b/client/src/app/navbar/settings/settings.component.html new file mode 100644 index 0000000..43c09d7 --- /dev/null +++ b/client/src/app/navbar/settings/settings.component.html @@ -0,0 +1,79 @@ + diff --git a/client/src/assets/.gitkeep b/client/src/app/navbar/settings/settings.component.scss similarity index 100% rename from client/src/assets/.gitkeep rename to client/src/app/navbar/settings/settings.component.scss diff --git a/client/src/app/navbar/settings/settings.component.ts b/client/src/app/navbar/settings/settings.component.ts new file mode 100644 index 0000000..99293bd --- /dev/null +++ b/client/src/app/navbar/settings/settings.component.ts @@ -0,0 +1,103 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { SettingsService } from 'src/app/settings.service'; +import { Setting } from 'src/app/models/setting.model'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'], +}) +export class SettingsComponent implements OnInit { + @Input() + public get open(): boolean { + return this.isActive; + } + + public set open(val: boolean) { + this.reset(); + this.isActive = val; + } + + @Output() + public openChange = new EventEmitter(); + + public isActive = false; + + public saving = false; + public error: string; + + public settingRealDebridApiKey: string; + public settingDownloadFolder: string; + public settingDownloadLimit: number; + + constructor(private settingsService: SettingsService) {} + + ngOnInit(): void {} + + public reset(): void { + this.saving = false; + this.error = null; + + this.settingsService.get().subscribe( + (results) => { + this.settingRealDebridApiKey = this.getSetting( + results, + 'RealDebridApiKey' + ); + this.settingDownloadFolder = this.getSetting(results, 'DownloadFolder'); + this.settingDownloadLimit = parseInt( + this.getSetting(results, 'DownloadLimit'), + 10 + ); + }, + (err) => { + this.error = err.error; + this.saving = true; + } + ); + } + + public ok(): void { + this.saving = true; + + const settings: Setting[] = [ + { + settingId: 'RealDebridApiKey', + value: this.settingRealDebridApiKey, + }, + { + settingId: 'DownloadFolder', + value: this.settingDownloadFolder, + }, + { + settingId: 'DownloadLimit', + value: (this.settingDownloadLimit ?? 10).toString(), + }, + ]; + + this.settingsService.update(settings).subscribe( + () => { + this.isActive = false; + this.openChange.emit(this.open); + }, + (err) => { + this.error = err; + } + ); + } + + public cancel(): void { + this.isActive = false; + this.openChange.emit(this.open); + } + + private getSetting(settings: Setting[], key: string): string { + const setting = settings.filter((m) => m.settingId === key); + + if (setting.length !== 1) { + throw new Error(`Unable to find setting with key ${key}`); + } + + return setting[0].value; + } +} diff --git a/client/src/app/settings.service.ts b/client/src/app/settings.service.ts new file mode 100644 index 0000000..db2a355 --- /dev/null +++ b/client/src/app/settings.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Setting } from './models/setting.model'; +import { Profile } from './models/profile.model'; + +@Injectable({ + providedIn: 'root', +}) +export class SettingsService { + constructor(private http: HttpClient) {} + + public get(): Observable { + return this.http.get(`/Api/Settings`); + } + + public update(settings: Setting[]): Observable { + return this.http.put(`/Api/Settings`, { settings }); + } + + public getProfile(): Observable { + return this.http.get(`/Api/Settings/Profile`); + } +} diff --git a/client/src/app/torrent-file/torrent-file.component.html b/client/src/app/torrent-file/torrent-file.component.html new file mode 100644 index 0000000..83d990f --- /dev/null +++ b/client/src/app/torrent-file/torrent-file.component.html @@ -0,0 +1,10 @@ + + + {{ file.path }} + +{{ file.bytes | filesize }} + + {{ file | fileStatus }} + + + diff --git a/client/src/app/torrent-file/torrent-file.component.scss b/client/src/app/torrent-file/torrent-file.component.scss new file mode 100644 index 0000000..03bcfa7 --- /dev/null +++ b/client/src/app/torrent-file/torrent-file.component.scss @@ -0,0 +1,3 @@ +td { + font-size: smaller; +} diff --git a/client/src/app/torrent-file/torrent-file.component.ts b/client/src/app/torrent-file/torrent-file.component.ts new file mode 100644 index 0000000..e390f17 --- /dev/null +++ b/client/src/app/torrent-file/torrent-file.component.ts @@ -0,0 +1,17 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Torrent, TorrentFile } from '../models/torrent.model'; +import { TorrentService } from '../torrent.service'; + +@Component({ + selector: '[app-torrent-file]', + templateUrl: './torrent-file.component.html', + styleUrls: ['./torrent-file.component.scss'], +}) +export class TorrentFileComponent implements OnInit { + @Input() + public file: TorrentFile; + + constructor(private torrentService: TorrentService) {} + + ngOnInit(): void {} +} diff --git a/client/src/app/torrent-row/torrent-row.component.html b/client/src/app/torrent-row/torrent-row.component.html new file mode 100644 index 0000000..25a8523 --- /dev/null +++ b/client/src/app/torrent-row/torrent-row.component.html @@ -0,0 +1,27 @@ +{{ torrent.rdName }} + + {{ torrent.rdSize | filesize }} + + + {{ torrent | status }} + + + + + + + + + + + diff --git a/client/src/app/torrent-row/torrent-row.component.scss b/client/src/app/torrent-row/torrent-row.component.scss new file mode 100644 index 0000000..e8bc505 --- /dev/null +++ b/client/src/app/torrent-row/torrent-row.component.scss @@ -0,0 +1,4 @@ +.delete-icon { + color: red; + cursor: pointer; +} diff --git a/client/src/app/torrent-row/torrent-row.component.ts b/client/src/app/torrent-row/torrent-row.component.ts new file mode 100644 index 0000000..26b4777 --- /dev/null +++ b/client/src/app/torrent-row/torrent-row.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Torrent, TorrentStatus } from 'src/app/models/torrent.model'; +import { TorrentService } from 'src/app/torrent.service'; + +@Component({ + selector: '[app-torrent-row]', + templateUrl: './torrent-row.component.html', + styleUrls: ['./torrent-row.component.scss'], +}) +export class TorrentRowComponent implements OnInit { + @Input() + public torrent: Torrent; + + public loading = false; + + constructor(private torrentService: TorrentService) {} + + ngOnInit(): void {} + + public download(event: Event): void { + event.stopPropagation(); + + this.loading = true; + this.torrentService.download(this.torrent.torrentId).subscribe(() => { + this.loading = false; + this.torrent.status = TorrentStatus.Downloading; + }); + } + + public delete(event: Event): void { + event.stopPropagation(); + + this.loading = true; + this.torrentService.delete(this.torrent.torrentId).subscribe(() => {}); + } +} diff --git a/client/src/app/torrent-status.pipe.ts b/client/src/app/torrent-status.pipe.ts new file mode 100644 index 0000000..ecee843 --- /dev/null +++ b/client/src/app/torrent-status.pipe.ts @@ -0,0 +1,28 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Torrent, TorrentStatus } from './models/torrent.model'; +import { FileSizePipe } from 'ngx-filesize'; + +@Pipe({ + name: 'status', +}) +export class TorrentStatusPipe implements PipeTransform { + constructor(private pipe: FileSizePipe) {} + + transform(torrent: Torrent): string { + switch (torrent.status) { + case TorrentStatus.RealDebrid: + const speed = this.pipe.transform(torrent.rdSpeed, 'filesize'); + return `Downloading from RD (${torrent.rdProgress}% - ${speed}/s)`; + case TorrentStatus.WaitingForDownload: + return `Waiting to download`; + case TorrentStatus.Downloading: + return `Downloading (${torrent.downloadProgress}%)`; + case TorrentStatus.Finished: + return `Finished`; + case TorrentStatus.Error: + return `Error`; + default: + return 'Unknown status'; + } + } +} diff --git a/client/src/app/torrent-table/torrent-table.component.html b/client/src/app/torrent-table/torrent-table.component.html new file mode 100644 index 0000000..211eb04 --- /dev/null +++ b/client/src/app/torrent-table/torrent-table.component.html @@ -0,0 +1,48 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSizeStatus
+ +
+
diff --git a/client/src/app/torrent-table/torrent-table.component.scss b/client/src/app/torrent-table/torrent-table.component.scss new file mode 100644 index 0000000..41c6f23 --- /dev/null +++ b/client/src/app/torrent-table/torrent-table.component.scss @@ -0,0 +1,5 @@ +table { + tr { + cursor: pointer; + } +} diff --git a/client/src/app/torrent-table/torrent-table.component.ts b/client/src/app/torrent-table/torrent-table.component.ts new file mode 100644 index 0000000..b5e0799 --- /dev/null +++ b/client/src/app/torrent-table/torrent-table.component.ts @@ -0,0 +1,61 @@ +import { + Component, + OnInit, + OnDestroy, + HostListener, + ElementRef, +} from '@angular/core'; +import { Torrent } from '../models/torrent.model'; +import { TorrentService } from '../torrent.service'; + +@Component({ + selector: 'app-torrent-table', + templateUrl: './torrent-table.component.html', + styleUrls: ['./torrent-table.component.scss'], +}) +export class TorrentTableComponent implements OnInit, OnDestroy { + public torrents: Torrent[] = []; + + public showFiles: { [key: string]: boolean } = {}; + + private timer: any; + + constructor(private torrentService: TorrentService) {} + + ngOnInit(): void { + this.timer = setInterval(() => { + this.torrentService.getList().subscribe((result) => { + this.torrents = result; + }); + }, 1000); + } + + ngOnDestroy(): void { + clearInterval(this.timer); + } + + public selectTorrent(torrent: Torrent): void { + this.showFiles[torrent.torrentId] = !this.showFiles[torrent.torrentId]; + + if (this.showFiles[torrent.torrentId]) { + this.torrentService.getDetails(torrent.torrentId).subscribe((result) => { + torrent.files = result.files; + torrent.downloads = result.downloads; + + torrent.files.forEach((file) => { + const downloads = torrent.downloads.filter( + (m) => m.link === file.path + ); + + if (downloads.length > 0) { + file.download = downloads[0]; + } + }); + }); + } + } + + public trackByMethod(index: number, el: Torrent): string { + return el.torrentId; + } +} diff --git a/client/src/app/torrent.service.ts b/client/src/app/torrent.service.ts new file mode 100644 index 0000000..5edfa32 --- /dev/null +++ b/client/src/app/torrent.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, Subject } from 'rxjs'; +import { Torrent } from './models/torrent.model'; + +@Injectable({ + providedIn: 'root', +}) +export class TorrentService { + constructor(private http: HttpClient) {} + + public getList(): Observable { + return this.http.get(`/Api/Torrents`); + } + + public getDetails(torrentId: string): Observable { + return this.http.get(`/Api/Torrents/${torrentId}`); + } + + public uploadMagnet(magnetLink: string): Observable { + return this.http.post(`/Api/Torrents/UploadMagnet`, { magnetLink }); + } + + public uploadFile(file: File): Observable { + const formData: FormData = new FormData(); + formData.append('file', file); + return this.http.post(`/Api/Torrents/UploadFile`, formData); + } + + public download(torrentId: string): Observable { + return this.http.get(`/Api/Torrents/Download/${torrentId}`); + } + + public delete(torrentId: string): Observable { + return this.http.delete(`/Api/Torrents/${torrentId}`); + } +} diff --git a/client/src/app/users.service.ts b/client/src/app/users.service.ts new file mode 100644 index 0000000..25f96f4 --- /dev/null +++ b/client/src/app/users.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class UsersService { + constructor() {} + + public getLoggedInUser() {} +} diff --git a/client/src/assets/logo.png b/client/src/assets/logo.png new file mode 100644 index 0000000..a74a86a Binary files /dev/null and b/client/src/assets/logo.png differ diff --git a/client/src/styles.scss b/client/src/styles.scss index 90d4ee0..8649996 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -1 +1,2 @@ -/* You can add global styles to this file, and also import other style files */ +@import "../node_modules/bulma/bulma.sass"; +@import "../node_modules/@fortawesome/fontawesome-free/css/all.css"; diff --git a/server/RdtClient.Data/Data/DataContext.cs b/server/RdtClient.Data/Data/DataContext.cs new file mode 100644 index 0000000..60b19b1 --- /dev/null +++ b/server/RdtClient.Data/Data/DataContext.cs @@ -0,0 +1,60 @@ +using System; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Data.Data +{ + public class DataContext : IdentityDbContext + { + public DataContext(DbContextOptions options) : base(options) + { + } + + public DataContext() + { + } + + public static String ConnectionString => $"Data Source={AppContext.BaseDirectory}database.db"; + + public DbSet Downloads { get; set; } + public DbSet Settings { get; set; } + public DbSet Torrents { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite(ConnectionString); + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity() + .HasData(new Setting + { + SettingId = "RealDebridApiKey", + Type = "String", + Value = "" + }); + + builder.Entity() + .HasData(new Setting + { + SettingId = "DownloadFolder", + Type = "String", + Value = @"C:\Downloads" + }); + + builder.Entity() + .HasData(new Setting + { + SettingId = "DownloadLimit", + Type = "Int32", + Value = "10" + }); + } + + public void Migrate() + { + Database.Migrate(); + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Data/Data/DownloadData.cs b/server/RdtClient.Data/Data/DownloadData.cs new file mode 100644 index 0000000..bb59e75 --- /dev/null +++ b/server/RdtClient.Data/Data/DownloadData.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Data.Data +{ + public interface IDownloadData + { + Task> Get(); + Task Add(Guid torrentId, String link); + Task UpdateStatus(Guid downloadId, DownloadStatus status); + Task DeleteForTorrent(Guid torrentId); + } + + public class DownloadData : IDownloadData + { + private readonly DataContext _dataContext; + + public DownloadData(DataContext dataContext) + { + _dataContext = dataContext; + } + + public async Task> Get() + { + return await _dataContext.Downloads.ToListAsync(); + } + + public async Task Add(Guid torrentId, String link) + { + var download = new Download + { + DownloadId = Guid.NewGuid(), + TorrentId = torrentId, + Link = link, + Added = DateTimeOffset.UtcNow, + Status = DownloadStatus.PendingDownload + }; + + _dataContext.Downloads.Add(download); + + await _dataContext.SaveChangesAsync(); + + return download; + } + + public async Task UpdateStatus(Guid downloadId, DownloadStatus status) + { + var download = await _dataContext.Downloads.FirstOrDefaultAsync(m => m.DownloadId == downloadId); + + download.Status = status; + + await _dataContext.SaveChangesAsync(); + } + + public async Task DeleteForTorrent(Guid torrentId) + { + var downloads = await _dataContext.Downloads + .Where(m => m.TorrentId == torrentId) + .ToListAsync(); + + _dataContext.Downloads.RemoveRange(downloads); + + await _dataContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Data/Data/SettingData.cs b/server/RdtClient.Data/Data/SettingData.cs new file mode 100644 index 0000000..a6fc13e --- /dev/null +++ b/server/RdtClient.Data/Data/SettingData.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Data.Data +{ + public interface ISettingData + { + Task> GetAll(); + Task Update(IList settings); + Task Get(String key); + } + + public class SettingData : ISettingData + { + private readonly DataContext _dataContext; + + public SettingData(DataContext dataContext) + { + _dataContext = dataContext; + } + + public async Task> GetAll() + { + return await _dataContext.Settings.ToListAsync(); + } + + public async Task Update(IList settings) + { + var dbSettings = await _dataContext.Settings.ToListAsync(); + + foreach (var dbSetting in dbSettings) + { + var setting = settings.FirstOrDefault(m => m.SettingId == dbSetting.SettingId); + + if (setting != null) + { + dbSetting.Value = setting.Value; + } + } + + await _dataContext.SaveChangesAsync(); + } + + public async Task Get(String key) + { + return await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == key); + } + } +} diff --git a/server/RdtClient.Data/Data/TorrentData.cs b/server/RdtClient.Data/Data/TorrentData.cs new file mode 100644 index 0000000..15732e9 --- /dev/null +++ b/server/RdtClient.Data/Data/TorrentData.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Data.Data +{ + public interface ITorrentData + { + Task> Get(); + Task Get(Guid id); + Task Add(String realDebridId, String hash); + Task UpdateRdData(Torrent torrent); + Task UpdateStatus(Guid torrentId, TorrentStatus status); + Task Delete(Guid id); + } + + public class TorrentData : ITorrentData + { + private readonly DataContext _dataContext; + + public TorrentData(DataContext dataContext) + { + _dataContext = dataContext; + } + + public async Task> Get() + { + return await _dataContext.Torrents.ToListAsync(); + } + + public async Task Get(Guid id) + { + var results = await _dataContext.Torrents + .Include(m => m.Downloads) + .FirstOrDefaultAsync(m => m.TorrentId == id); + + foreach (var file in results.Downloads) + { + file.Torrent = null; + } + + return results; + } + + public async Task Add(String realDebridId, String hash) + { + var torrent = new Torrent + { + TorrentId = Guid.NewGuid(), + RdId = realDebridId, + Hash = hash, + Status = TorrentStatus.RealDebrid + }; + + _dataContext.Torrents.Add(torrent); + + await _dataContext.SaveChangesAsync(); + + return torrent; + } + + public async Task UpdateRdData(Torrent torrent) + { + var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId); + + dbTorrent.RdName = torrent.RdName; + dbTorrent.RdSize = torrent.RdSize; + dbTorrent.RdHost = torrent.RdHost; + dbTorrent.RdSplit = torrent.RdSplit; + dbTorrent.RdProgress = torrent.RdProgress; + dbTorrent.RdStatus = torrent.RdStatus; + dbTorrent.RdAdded = torrent.RdAdded; + dbTorrent.RdEnded = torrent.RdEnded; + dbTorrent.RdSpeed = torrent.RdSpeed; + dbTorrent.RdSeeders = torrent.RdSeeders; + + await _dataContext.SaveChangesAsync(); + } + + public async Task UpdateStatus(Guid torrentId, TorrentStatus status) + { + var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId); + + dbTorrent.Status = status; + + await _dataContext.SaveChangesAsync(); + } + + public async Task Delete(Guid id) + { + var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == id); + + _dataContext.Torrents.Remove(dbTorrent); + + await _dataContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Data/DataContext/DataContext.cs b/server/RdtClient.Data/DataContext/DataContext.cs deleted file mode 100644 index 18ee4c7..0000000 --- a/server/RdtClient.Data/DataContext/DataContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; - -namespace RdtClient.Data.DataContext -{ - public class DataContext : IdentityDbContext - { - - } -} diff --git a/server/RdtClient.Data/DiConfig.cs b/server/RdtClient.Data/DiConfig.cs index f0b7288..72ceb17 100644 --- a/server/RdtClient.Data/DiConfig.cs +++ b/server/RdtClient.Data/DiConfig.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using RdtClient.Data.Data; namespace RdtClient.Data { @@ -6,7 +7,9 @@ namespace RdtClient.Data { public static void Config(IServiceCollection services) { - //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } } diff --git a/server/RdtClient.Data/Enums/DownloadStatus.cs b/server/RdtClient.Data/Enums/DownloadStatus.cs new file mode 100644 index 0000000..9e97a1d --- /dev/null +++ b/server/RdtClient.Data/Enums/DownloadStatus.cs @@ -0,0 +1,9 @@ +namespace RdtClient.Data.Enums +{ + public enum DownloadStatus + { + PendingDownload = 0, + Downloading, + Finished + } +} diff --git a/server/RdtClient.Data/Enums/TorrentStatus.cs b/server/RdtClient.Data/Enums/TorrentStatus.cs new file mode 100644 index 0000000..f89f7cf --- /dev/null +++ b/server/RdtClient.Data/Enums/TorrentStatus.cs @@ -0,0 +1,12 @@ +namespace RdtClient.Data.Enums +{ + public enum TorrentStatus + { + RealDebrid = 0, + WaitingForDownload, + Downloading, + Finished, + + Error = 99 + } +} diff --git a/server/RdtClient.Data/Migrations/20200403195110_Initial.Designer.cs b/server/RdtClient.Data/Migrations/20200403195110_Initial.Designer.cs new file mode 100644 index 0000000..2c0f547 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20200403195110_Initial.Designer.cs @@ -0,0 +1,395 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RdtClient.Data.Data; + +namespace RdtClient.Data.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20200403195110_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.Property("DownloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TorrentId") + .HasColumnType("TEXT"); + + b.HasKey("DownloadId"); + + b.HasIndex("TorrentId"); + + b.ToTable("Downloads"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b => + { + b.Property("SettingId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("SettingId"); + + b.ToTable("Settings"); + + b.HasData( + new + { + SettingId = "RealDebridApiKey", + Type = "String", + Value = "" + }, + new + { + SettingId = "DownloadFolder", + Type = "String", + Value = "C:\\Downloads" + }, + new + { + SettingId = "SpeedLimit", + Type = "Int32", + Value = "0" + }, + new + { + SettingId = "SegmentsLimit", + Type = "Int32", + Value = "10" + }); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b => + { + b.Property("TorrentId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Hash") + .HasColumnType("TEXT"); + + b.Property("RdAdded") + .HasColumnType("TEXT"); + + b.Property("RdEnded") + .HasColumnType("TEXT"); + + b.Property("RdFiles") + .HasColumnType("TEXT"); + + b.Property("RdHost") + .HasColumnType("TEXT"); + + b.Property("RdId") + .HasColumnType("TEXT"); + + b.Property("RdName") + .HasColumnType("TEXT"); + + b.Property("RdProgress") + .HasColumnType("INTEGER"); + + b.Property("RdSeeders") + .HasColumnType("INTEGER"); + + b.Property("RdSize") + .HasColumnType("INTEGER"); + + b.Property("RdSpeed") + .HasColumnType("INTEGER"); + + b.Property("RdSplit") + .HasColumnType("INTEGER"); + + b.Property("RdStatus") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("TorrentId"); + + b.ToTable("Torrents"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent") + .WithMany("Downloads") + .HasForeignKey("TorrentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/server/RdtClient.Data/Migrations/20200403195110_Initial.cs b/server/RdtClient.Data/Migrations/20200403195110_Initial.cs new file mode 100644 index 0000000..c31bd57 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20200403195110_Initial.cs @@ -0,0 +1,305 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RdtClient.Data.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 256, nullable: true), + NormalizedName = table.Column(maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(nullable: false), + UserName = table.Column(maxLength: 256, nullable: true), + NormalizedUserName = table.Column(maxLength: 256, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false), + PasswordHash = table.Column(nullable: true), + SecurityStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + TwoFactorEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Settings", + columns: table => new + { + SettingId = table.Column(nullable: false), + Value = table.Column(nullable: true), + Type = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Settings", x => x.SettingId); + }); + + migrationBuilder.CreateTable( + name: "Torrents", + columns: table => new + { + TorrentId = table.Column(nullable: false), + Hash = table.Column(nullable: true), + Status = table.Column(nullable: false), + RdId = table.Column(nullable: true), + RdName = table.Column(nullable: true), + RdSize = table.Column(nullable: false), + RdHost = table.Column(nullable: true), + RdSplit = table.Column(nullable: false), + RdProgress = table.Column(nullable: false), + RdStatus = table.Column(nullable: true), + RdAdded = table.Column(nullable: false), + RdEnded = table.Column(nullable: true), + RdSpeed = table.Column(nullable: true), + RdSeeders = table.Column(nullable: true), + RdFiles = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Torrents", x => x.TorrentId); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(nullable: false), + ProviderKey = table.Column(nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(nullable: false), + Name = table.Column(nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Downloads", + columns: table => new + { + DownloadId = table.Column(nullable: false), + TorrentId = table.Column(nullable: false), + Link = table.Column(nullable: true), + Added = table.Column(nullable: false), + Status = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Downloads", x => x.DownloadId); + table.ForeignKey( + name: "FK_Downloads_Torrents_TorrentId", + column: x => x.TorrentId, + principalTable: "Torrents", + principalColumn: "TorrentId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Settings", + columns: new[] { "SettingId", "Type", "Value" }, + values: new object[] { "RealDebridApiKey", "String", "" }); + + migrationBuilder.InsertData( + table: "Settings", + columns: new[] { "SettingId", "Type", "Value" }, + values: new object[] { "DownloadFolder", "String", "C:\\Downloads" }); + + migrationBuilder.InsertData( + table: "Settings", + columns: new[] { "SettingId", "Type", "Value" }, + values: new object[] { "DownloadLimit", "Int32", "1" }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Downloads_TorrentId", + table: "Downloads", + column: "TorrentId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "Downloads"); + + migrationBuilder.DropTable( + name: "Settings"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "Torrents"); + } + } +} diff --git a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs new file mode 100644 index 0000000..91277f7 --- /dev/null +++ b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs @@ -0,0 +1,387 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RdtClient.Data.Data; + +namespace RdtClient.Data.Migrations +{ + [DbContext(typeof(DataContext))] + partial class DataContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.Property("DownloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TorrentId") + .HasColumnType("TEXT"); + + b.HasKey("DownloadId"); + + b.HasIndex("TorrentId"); + + b.ToTable("Downloads"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b => + { + b.Property("SettingId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("SettingId"); + + b.ToTable("Settings"); + + b.HasData( + new + { + SettingId = "RealDebridApiKey", + Type = "String", + Value = "" + }, + new + { + SettingId = "DownloadFolder", + Type = "String", + Value = "C:\\Downloads" + }, + new + { + SettingId = "DownloadLimit", + Type = "Int32", + Value = "10" + }); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b => + { + b.Property("TorrentId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Hash") + .HasColumnType("TEXT"); + + b.Property("RdAdded") + .HasColumnType("TEXT"); + + b.Property("RdEnded") + .HasColumnType("TEXT"); + + b.Property("RdFiles") + .HasColumnType("TEXT"); + + b.Property("RdHost") + .HasColumnType("TEXT"); + + b.Property("RdId") + .HasColumnType("TEXT"); + + b.Property("RdName") + .HasColumnType("TEXT"); + + b.Property("RdProgress") + .HasColumnType("INTEGER"); + + b.Property("RdSeeders") + .HasColumnType("INTEGER"); + + b.Property("RdSize") + .HasColumnType("INTEGER"); + + b.Property("RdSpeed") + .HasColumnType("INTEGER"); + + b.Property("RdSplit") + .HasColumnType("INTEGER"); + + b.Property("RdStatus") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("TorrentId"); + + b.ToTable("Torrents"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent") + .WithMany("Downloads") + .HasForeignKey("TorrentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/server/RdtClient.Data/Models/Data/Download.cs b/server/RdtClient.Data/Models/Data/Download.cs new file mode 100644 index 0000000..c94f803 --- /dev/null +++ b/server/RdtClient.Data/Models/Data/Download.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using RdtClient.Data.Enums; + +namespace RdtClient.Data.Models.Data +{ + public class Download + { + [Key] + public Guid DownloadId { get; set; } + + public Guid TorrentId { get; set; } + + public String Link { get; set; } + + public DateTimeOffset Added { get; set; } + + public DownloadStatus Status { get; set; } + + [ForeignKey("TorrentId")] + public Torrent Torrent { get; set; } + + [NotMapped] + public Int32 Progress { get; set; } + } +} diff --git a/server/RdtClient.Data/Models/Data/Setting.cs b/server/RdtClient.Data/Models/Data/Setting.cs new file mode 100644 index 0000000..1a50983 --- /dev/null +++ b/server/RdtClient.Data/Models/Data/Setting.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace RdtClient.Data.Models.Data +{ + public class Setting + { + [Key] + public String SettingId { get; set; } + + public String Value { get; set; } + + public String Type { get; set; } + } +} diff --git a/server/RdtClient.Data/Models/Data/Torrent.cs b/server/RdtClient.Data/Models/Data/Torrent.cs new file mode 100644 index 0000000..a76bead --- /dev/null +++ b/server/RdtClient.Data/Models/Data/Torrent.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; +using RDNET.Models; +using RdtClient.Data.Enums; + +namespace RdtClient.Data.Models.Data +{ + public class Torrent + { + [Key] + public Guid TorrentId { get; set; } + + public String Hash { get; set; } + + public TorrentStatus Status { get; set; } + + [InverseProperty("Torrent")] + public IList Downloads { get; set; } + + public String RdId { get; set; } + public String RdName { get; set; } + public Int64 RdSize { get; set; } + public String RdHost { get; set; } + public Int64 RdSplit { get; set; } + public Int64 RdProgress { get; set; } + public String RdStatus { get; set; } + public DateTimeOffset RdAdded { get; set; } + public DateTimeOffset? RdEnded { get; set; } + public Int64? RdSpeed { get; set; } + public Int64? RdSeeders { get; set; } + public String RdFiles { get; set; } + + [NotMapped] + public Int32 DownloadProgress { get; set; } + + [NotMapped] + public IList Files + { + get + { + if (String.IsNullOrWhiteSpace(RdFiles)) + { + return new List(); + } + + try + { + return JsonConvert.DeserializeObject>(RdFiles); + } + catch + { + return new List(); + } + } + } + } +} diff --git a/server/RdtClient.Data/Models/Internal/AppSettings.cs b/server/RdtClient.Data/Models/Internal/AppSettings.cs index 8eb0313..6a603bc 100644 --- a/server/RdtClient.Data/Models/Internal/AppSettings.cs +++ b/server/RdtClient.Data/Models/Internal/AppSettings.cs @@ -1,14 +1,6 @@ -using System; - -namespace RdtClient.Data.Models.Internal +namespace RdtClient.Data.Models.Internal { public class AppSettings { - public AppSettingsConnectionStrings ConnectionStrings { get; set; } - } - - public class AppSettingsConnectionStrings - { - public String Client { get; set; } } } diff --git a/server/RdtClient.Data/RdtClient.Data.csproj b/server/RdtClient.Data/RdtClient.Data.csproj index ea3151a..883337a 100644 --- a/server/RdtClient.Data/RdtClient.Data.csproj +++ b/server/RdtClient.Data/RdtClient.Data.csproj @@ -4,14 +4,47 @@ netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + ..\libs\RDNET.dll + diff --git a/server/RdtClient.Service/DiConfig.cs b/server/RdtClient.Service/DiConfig.cs index 688f566..11e088b 100644 --- a/server/RdtClient.Service/DiConfig.cs +++ b/server/RdtClient.Service/DiConfig.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using RdtClient.Service.Services; namespace RdtClient.Service { @@ -6,7 +7,10 @@ namespace RdtClient.Service { public static void Config(IServiceCollection services) { - //services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } -} +} \ No newline at end of file diff --git a/server/RdtClient.Service/Models/Profile.cs b/server/RdtClient.Service/Models/Profile.cs new file mode 100644 index 0000000..c2eaa11 --- /dev/null +++ b/server/RdtClient.Service/Models/Profile.cs @@ -0,0 +1,10 @@ +using System; + +namespace RdtClient.Service.Models +{ + public class Profile + { + public String UserName { get; set; } + public DateTimeOffset Expiration { get; set; } + } +} diff --git a/server/RdtClient.Service/Properties/launchSettings.json b/server/RdtClient.Service/Properties/launchSettings.json new file mode 100644 index 0000000..7eb414c --- /dev/null +++ b/server/RdtClient.Service/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53591/", + "sslPort": 44310 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "RdtClient.Service": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Service/RdtClient.Service.csproj b/server/RdtClient.Service/RdtClient.Service.csproj index 3d39cc9..ed62c6d 100644 --- a/server/RdtClient.Service/RdtClient.Service.csproj +++ b/server/RdtClient.Service/RdtClient.Service.csproj @@ -1,11 +1,25 @@ - + netcoreapp3.1 + library + + + + + + + + + + ..\libs\RDNET.dll + + + diff --git a/server/RdtClient.Service/RdtClient.Service.csproj.user b/server/RdtClient.Service/RdtClient.Service.csproj.user new file mode 100644 index 0000000..cff74a9 --- /dev/null +++ b/server/RdtClient.Service/RdtClient.Service.csproj.user @@ -0,0 +1,6 @@ + + + + IIS Express + + \ No newline at end of file diff --git a/server/RdtClient.Service/Services/DownloadManager.cs b/server/RdtClient.Service/Services/DownloadManager.cs new file mode 100644 index 0000000..cdab342 --- /dev/null +++ b/server/RdtClient.Service/Services/DownloadManager.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using RdtClient.Data.Data; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.Data; +using SharpCompress.Common; +using SharpCompress.Readers; + +namespace RdtClient.Service.Services +{ + public class DownloadManager + { + public static readonly ConcurrentDictionary ActiveDownloads = new ConcurrentDictionary(); + + private static readonly Object Lock = new Object(); + + static DownloadManager() + { + ServicePointManager.Expect100Continue = false; + ServicePointManager.DefaultConnectionLimit = 100; + ServicePointManager.MaxServicePointIdleTime = 1000; + } + + public static async Task Download(Download download, String destinationFolderPath) + { + await UpdateStatus(download.DownloadId, DownloadStatus.Downloading, TorrentStatus.Downloading); + + if (!ActiveDownloads.TryAdd(download.DownloadId, download)) + { + return; + } + + var fileUrl = download.Link; + + var uri = new Uri(fileUrl); + var filePath = Path.Combine(destinationFolderPath, uri.Segments.Last()); + + var webRequest = WebRequest.Create(fileUrl); + webRequest.Method = "HEAD"; + Int64 responseLength; + using (var webResponse = await webRequest.GetResponseAsync()) + { + responseLength = Int64.Parse(webResponse.Headers.Get("Content-Length")); + } + + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + + var request = WebRequest.Create(fileUrl); + using (var response = await request.GetResponseAsync()) + { + await using var stream = response.GetResponseStream(); + await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write); + + var buffer = new Byte[4096]; + + while (fileStream.Length < response.ContentLength) + { + var read = stream.Read(buffer, 0, buffer.Length); + + if (read > 0) + { + fileStream.Write(buffer, 0, read); + + download.Progress = (Int32) (fileStream.Length * 100 / responseLength); + ActiveDownloads.TryAdd(download.DownloadId, download); + } + else + { + break; + } + } + } + + try + { + await using Stream stream = File.OpenRead(filePath); + + var reader = ReaderFactory.Open(stream); + while (reader.MoveToNextEntry()) + { + if (!reader.Entry.IsDirectory) + { + Console.WriteLine(reader.Entry.Key); + reader.WriteEntryToDirectory(destinationFolderPath, + new ExtractionOptions + { + ExtractFullPath = true, + Overwrite = true + }); + } + } + + File.Delete(filePath); + } + catch + { + // ignored + } + + await UpdateStatus(download.DownloadId, DownloadStatus.Finished, TorrentStatus.Finished); + + ActiveDownloads.TryRemove(download.DownloadId, out _); + } + + private static async Task UpdateStatus(Guid downloadId, DownloadStatus downloadStatus, TorrentStatus torrentStatus) + { + await using var context = new DataContext(); + + var download = await context.Downloads.FirstOrDefaultAsync(m => m.DownloadId == downloadId); + + download.Status = downloadStatus; + + await context.SaveChangesAsync(); + + var torrent = await context.Torrents.FirstOrDefaultAsync(m => m.TorrentId == download.TorrentId); + + if (torrentStatus == TorrentStatus.Finished) + { + var allDownloads = await context.Downloads.Where(m => m.TorrentId == download.TorrentId) + .ToListAsync(); + + if (allDownloads.All(m => m.Status == DownloadStatus.Finished)) + { + torrent.Status = TorrentStatus.Finished; + } + else + { + torrent.Status = TorrentStatus.Downloading; + } + } + else + { + torrent.Status = torrentStatus; + } + + await context.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Service/Services/Downloads.cs b/server/RdtClient.Service/Services/Downloads.cs new file mode 100644 index 0000000..b407012 --- /dev/null +++ b/server/RdtClient.Service/Services/Downloads.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using RdtClient.Data.Data; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Service.Services +{ + public interface IDownloads + { + Task> Get(); + Task Add(Guid torrentId, String link); + Task UpdateStatus(Guid downloadId, DownloadStatus status); + Task DeleteForTorrent(Guid torrentId); + } + + public class Downloads : IDownloads + { + private readonly IDownloadData _downloadData; + private readonly ISettings _settings; + + public Downloads(IDownloadData downloadData, ISettings settings) + { + _downloadData = downloadData; + _settings = settings; + } + + public async Task> Get() + { + return await _downloadData.Get(); + } + + public async Task Add(Guid torrentId, String link) + { + return await _downloadData.Add(torrentId, link); + } + + public async Task UpdateStatus(Guid downloadId, DownloadStatus status) + { + await _downloadData.UpdateStatus(downloadId, status); + } + + public async Task DeleteForTorrent(Guid torrentId) + { + await _downloadData.DeleteForTorrent(torrentId); + } + } +} diff --git a/server/RdtClient.Service/Services/Scheduler.cs b/server/RdtClient.Service/Services/Scheduler.cs new file mode 100644 index 0000000..98e6767 --- /dev/null +++ b/server/RdtClient.Service/Services/Scheduler.cs @@ -0,0 +1,62 @@ +using System.Linq; +using System.Threading.Tasks; +using Hangfire; +using RdtClient.Data.Enums; + +namespace RdtClient.Service.Services +{ + public interface IScheduler + { + void Start(); + Task Process(); + } + + public class Scheduler : IScheduler + { + private readonly IDownloads _downloads; + private readonly ISettings _settings; + private readonly ITorrents _torrents; + + public Scheduler(ITorrents torrents, IDownloads downloads, ISettings settings) + { + _torrents = torrents; + _downloads = downloads; + _settings = settings; + } + + public void Start() + { + RecurringJob.AddOrUpdate(() => Process(), "* * * * *"); + BackgroundJob.Enqueue(() => Process()); + } + + [DisableConcurrentExecution(5)] + public async Task Process() + { + await _torrents.Update(); + + var downloads = await _downloads.Get(); + + downloads = downloads.Where(m => m.Status != DownloadStatus.Finished) + .OrderByDescending(m => m.Status) + .ThenByDescending(m => m.Added) + .ToList(); + + var maxDownloads = await _settings.GetNumber("DownloadLimit"); + var destinationFolderPath = await _settings.GetString("DownloadFolder"); + + foreach (var download in downloads) + { + if (DownloadManager.ActiveDownloads.Count >= maxDownloads) + { + return; + } + + download.Torrent = null; + BackgroundJob.Enqueue(() => DownloadManager.Download(download, destinationFolderPath)); + + await Task.Delay(1000); + } + } + } +} \ No newline at end of file diff --git a/server/RdtClient.Service/Services/Settings.cs b/server/RdtClient.Service/Services/Settings.cs new file mode 100644 index 0000000..282cab9 --- /dev/null +++ b/server/RdtClient.Service/Services/Settings.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using RdtClient.Data.Data; +using RdtClient.Data.Models.Data; + +namespace RdtClient.Service.Services +{ + public interface ISettings + { + Task> GetAll(); + Task Update(IList settings); + Task GetString(String key); + Task GetNumber(String key); + } + + public class Settings : ISettings + { + private readonly ISettingData _settingData; + + public Settings(ISettingData settingData) + { + _settingData = settingData; + } + + public async Task> GetAll() + { + return await _settingData.GetAll(); + } + + public async Task Update(IList settings) + { + await _settingData.Update(settings); + } + + public async Task GetString(String key) + { + var setting = await _settingData.Get(key); + + if (setting == null) + { + throw new Exception($"Setting with key {key} not found"); + } + + return setting.Value; + } + + public async Task GetNumber(String key) + { + var setting = await _settingData.Get(key); + + if (setting == null) + { + throw new Exception($"Setting with key {key} not found"); + } + + return Int32.Parse(setting.Value); + } + } +} diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs new file mode 100644 index 0000000..02c8fe3 --- /dev/null +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using RDNET; +using RdtClient.Data.Data; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.Data; +using RdtClient.Service.Models; + +namespace RdtClient.Service.Services +{ + public interface ITorrents + { + Task> Get(); + Task Get(Guid id); + Task> Update(); + Task UploadMagnet(String magnetLink); + Task UploadFile(Byte[] bytes); + Task Delete(Guid id); + Task Download(Guid id); + void Reset(); + Task GetProfile(); + } + + public class Torrents : ITorrents + { + private readonly ITorrentData _torrentData; + private readonly ISettings _settings; + private readonly IDownloads _downloads; + + private static RdNetClient _rdtClient; + + private static DateTime _rdtLastUpdate = DateTime.UtcNow; + + private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1,1); + + private RdNetClient RdNetClient + { + get + { + if (_rdtClient == null) + { + var apiKey = _settings.GetString("RealDebridApiKey").Result; + + if (String.IsNullOrWhiteSpace(apiKey)) + { + throw new Exception("RealDebrid API Key not set in the settings"); + } + + _rdtClient = new RdNetClient("X245A4XAIBGVM", null, null, null, null, apiKey); + } + + return _rdtClient; + } + } + + public Torrents(ITorrentData torrentData, ISettings settings, IDownloads downloads) + { + _torrentData = torrentData; + _settings = settings; + _downloads = downloads; + } + + public async Task> Get() + { + var torrents = await _torrentData.Get(); + + if (DateTime.UtcNow > _rdtLastUpdate) + { + _rdtLastUpdate = DateTime.UtcNow.AddSeconds(10); + + torrents = await Update(); + } + + foreach (var torrent in torrents) + { + var downloads = DownloadManager.ActiveDownloads.Where(m => m.Value.TorrentId == torrent.TorrentId).ToList(); + + if (torrent.Files.Count > 0) + { + torrent.DownloadProgress = downloads.Sum(m => m.Value.Progress) / torrent.Files.Count; + } + } + + return torrents; + } + + public async Task Get(Guid id) + { + var torrent = await _torrentData.Get(id); + + if (torrent != null) + { + var rdTorrent = await RdNetClient.TorrentInfoAsync(torrent.RdId); + + await Update(torrent, rdTorrent); + } + + return torrent; + } + + public async Task> Update() + { + var torrents = await _torrentData.Get(); + + var w = await SemaphoreSlim.WaitAsync(10); + if (!w) + { + return torrents; + } + + try + { + var rdTorrents = await RdNetClient.TorrentsAsync(0, 100); + + foreach (var rdTorrent in rdTorrents) + { + var torrent = torrents.FirstOrDefault(m => m.RdId == rdTorrent.Id); + + if (torrent == null) + { + var newTorrent = await _torrentData.Add(rdTorrent.Id, rdTorrent.Hash); + await Get(newTorrent.TorrentId); + } + else + { + await Update(torrent, rdTorrent); + } + } + + foreach (var torrent in torrents) + { + var rdTorrent = rdTorrents.FirstOrDefault(m => m.Id == torrent.RdId); + + if (rdTorrent == null) + { + await _torrentData.Delete(torrent.TorrentId); + } + } + + return torrents; + } + finally + { + SemaphoreSlim.Release(); + } + } + + public async Task UploadMagnet(String magnetLink) + { + var magnet = MonoTorrent.MagnetLink.Parse(magnetLink); + + var rdTorrent = await RdNetClient.TorrentAddMagnet(magnetLink); + + await Add(rdTorrent.Id, magnet.InfoHash.ToHex()); + } + + public async Task UploadFile(Byte[] bytes) + { + var torrent = MonoTorrent.Torrent.Load(bytes); + + var rdTorrent = await RdNetClient.TorrentAddFile(bytes); + + await Add(rdTorrent.Id, torrent.InfoHash.ToHex()); + } + + public async Task Delete(Guid id) + { + var torrent = await Get(id); + + if (torrent != null) + { + await _torrentData.Delete(id); + await RdNetClient.TorrentDelete(torrent.RdId); + } + } + + public async Task Download(Guid id) + { + var torrent = await _torrentData.Get(id); + + await _downloads.DeleteForTorrent(id); + + var rdTorrent = await RdNetClient.TorrentInfoAsync(torrent.RdId); + + foreach (var file in rdTorrent.Files) + { + var unrestrictedLink = await RdNetClient.UnrestrictLinkAsync(file.Path); + + await _downloads.Add(torrent.TorrentId, unrestrictedLink.Download); + } + } + + public void Reset() + { + _rdtClient = null; + _rdtLastUpdate = DateTime.UtcNow; + } + + public async Task GetProfile() + { + var user = await _rdtClient.UserAsync(); + + var profile = new Profile + { + UserName = user.Username, + Expiration = user.Expiration + }; + + return profile; + } + + private async Task Add(String rdTorrentId, String infoHash) + { + var newTorrent = await _torrentData.Add(rdTorrentId, infoHash); + + var rdTorrent = await RdNetClient.TorrentInfoAsync(rdTorrentId); + + if (rdTorrent.Files != null && rdTorrent.Files.Count > 0) + { + if (!rdTorrent.Files.Any(m => m.Selected)) + { + var fileIds = rdTorrent.Files.Select(m => m.Id.ToString()).ToArray(); + + await RdNetClient.TorrentSelectFiles(rdTorrentId, fileIds); + } + } + + await Update(newTorrent, rdTorrent); + } + + private async Task Update(Torrent torrent, RDNET.Models.Torrent rdTorrent) + { + if (!String.IsNullOrWhiteSpace(rdTorrent.Filename)) + { + torrent.RdName = rdTorrent.Filename; + } + else if (!String.IsNullOrWhiteSpace(rdTorrent.OriginalFilename)) + { + torrent.RdName = rdTorrent.OriginalFilename; + } + + if (rdTorrent.Bytes > 0) + { + torrent.RdSize = rdTorrent.Bytes; + } + else if (rdTorrent.OriginalBytes > 0) + { + torrent.RdSize = rdTorrent.OriginalBytes; + } + + if (rdTorrent.Files != null) + { + torrent.RdFiles = JsonConvert.SerializeObject(rdTorrent.Files); + } + + torrent.RdHost = rdTorrent.Host; + torrent.RdSplit = rdTorrent.Split; + torrent.RdProgress = rdTorrent.Progress; + torrent.RdStatus = rdTorrent.Status; + torrent.RdAdded = rdTorrent.Added; + torrent.RdEnded = rdTorrent.Ended; + torrent.RdSpeed = rdTorrent.Speed; + torrent.RdSeeders = rdTorrent.Seeders; + + await _torrentData.UpdateRdData(torrent); + + if (torrent.Status == TorrentStatus.RealDebrid && torrent.RdProgress == 100) + { + await _torrentData.UpdateStatus(torrent.TorrentId, TorrentStatus.WaitingForDownload); + } + } + } +} diff --git a/server/RdtClient.Web/Controllers/SettingsController.cs b/server/RdtClient.Web/Controllers/SettingsController.cs new file mode 100644 index 0000000..7a350e5 --- /dev/null +++ b/server/RdtClient.Web/Controllers/SettingsController.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using RdtClient.Data.Models.Data; +using RdtClient.Service.Models; +using RdtClient.Service.Services; + +namespace RdtClient.Web.Controllers +{ + [Route("Api/Settings")] + public class SettingsController : Controller + { + private readonly ISettings _settings; + private readonly ITorrents _torrents; + + public SettingsController(ISettings settings, ITorrents torrents) + { + _settings = settings; + _torrents = torrents; + } + + [HttpGet] + [Route("")] + public async Task>> Get() + { + try + { + var result = await _settings.GetAll(); + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(ex); + } + } + + [HttpPut] + [Route("")] + public async Task Update([FromBody] SettingsControllerUpdateRequest request) + { + try + { + await _settings.Update(request.Settings); + _torrents.Reset(); + + return Ok(); + } + catch (Exception ex) + { + return BadRequest(ex); + } + } + + [HttpGet] + [Route("Profile")] + public async Task> Profile() + { + try + { + var profile = await _torrents.GetProfile(); + return Ok(profile); + } + catch (Exception ex) + { + return BadRequest(ex); + } + } + } + + public class SettingsControllerUpdateRequest + { + public IList Settings { get; set; } + } +} \ No newline at end of file diff --git a/server/RdtClient.Web/Controllers/TorrentsController.cs b/server/RdtClient.Web/Controllers/TorrentsController.cs new file mode 100644 index 0000000..17e828a --- /dev/null +++ b/server/RdtClient.Web/Controllers/TorrentsController.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using RdtClient.Data.Models.Data; +using RdtClient.Service.Services; + +namespace RdtClient.Web.Controllers +{ + [Route("Api/Torrents")] + public class TorrentsController : Controller + { + private readonly ITorrents _torrents; + private readonly IScheduler _scheduler; + + public TorrentsController(ITorrents torrents, IScheduler scheduler) + { + _torrents = torrents; + _scheduler = scheduler; + } + + [HttpGet] + [Route("")] + public async Task>> Get() + { + try + { + var result = await _torrents.Get(); + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(ex); + } + } + + [HttpGet] + [Route("{id}")] + public async Task> Get(Guid id) + { + try + { + var result = await _torrents.Get(id); + + if (result == null) + { + throw new Exception("Torrent not found"); + } + + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(ex); + } + } + + [HttpPost] + [Route("UploadFile")] + public async Task UploadFile([FromForm] IFormFile file) + { + try + { + if (file == null || file.Length <= 0) + { + throw new Exception("Invalid torrent file"); + } + + var fileStream = file.OpenReadStream(); + + await using var memoryStream = new MemoryStream(); + + fileStream.CopyTo(memoryStream); + + var bytes = memoryStream.ToArray(); + + await _torrents.UploadFile(bytes); + + return Ok(); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + + [HttpPost] + [Route("UploadMagnet")] + public async Task UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request) + { + try + { + await _torrents.UploadMagnet(request.MagnetLink); + + return Ok(); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + + [HttpDelete] + [Route("{id}")] + public async Task Delete(Guid id) + { + try + { + await _torrents.Delete(id); + + return Ok(); + } + catch(Exception ex) + { + return BadRequest(ex.Message); + } + } + + [HttpGet] + [Route("Download/{id}")] + public async Task Download(Guid id) + { + try + { + await _torrents.Download(id); + await _scheduler.Process(); + + return Ok(); + } + catch(Exception ex) + { + return BadRequest(ex.Message); + } + } + } + + public class TorrentControllerUploadMagnetRequest + { + public String MagnetLink { get; set; } + } +} diff --git a/server/RdtClient.Web/Program.cs b/server/RdtClient.Web/Program.cs index 208bb44..7023f9f 100644 --- a/server/RdtClient.Web/Program.cs +++ b/server/RdtClient.Web/Program.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace RdtClient.Web { @@ -13,8 +14,15 @@ namespace RdtClient.Web .Run(); } - private static IHostBuilder CreateHostBuilder(String[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + private static IHostBuilder CreateHostBuilder(String[] args) + { + return Host + .CreateDefaultBuilder(args) + .ConfigureLogging(logging => + { + logging.AddFile("app.log"); + }) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } } } \ No newline at end of file diff --git a/server/RdtClient.Web/Properties/launchSettings.json b/server/RdtClient.Web/Properties/launchSettings.json index 93fe097..8c47cc0 100644 --- a/server/RdtClient.Web/Properties/launchSettings.json +++ b/server/RdtClient.Web/Properties/launchSettings.json @@ -1,18 +1,16 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:52923", - "sslPort": 44356 + "applicationUrl": "http://localhost:4201", + "sslPort": 0 } }, + "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "weatherforecast", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -21,10 +19,10 @@ "commandName": "Project", "launchBrowser": true, "launchUrl": "weatherforecast", - "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" } } -} +} \ No newline at end of file diff --git a/server/RdtClient.Web/RdtClient.Web.csproj b/server/RdtClient.Web/RdtClient.Web.csproj index cc58959..b32f011 100644 --- a/server/RdtClient.Web/RdtClient.Web.csproj +++ b/server/RdtClient.Web/RdtClient.Web.csproj @@ -5,11 +5,13 @@ - + + + @@ -19,6 +21,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/server/RdtClient.Web/Startup.cs b/server/RdtClient.Web/Startup.cs index 7b82547..6911319 100644 --- a/server/RdtClient.Web/Startup.cs +++ b/server/RdtClient.Web/Startup.cs @@ -1,3 +1,5 @@ +using Hangfire; +using Hangfire.MemoryStorage; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -6,8 +8,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using RdtClient.Data.DataContext; +using RdtClient.Data.Data; using RdtClient.Data.Models.Internal; +using RdtClient.Service.Services; namespace RdtClient.Web { @@ -27,7 +30,7 @@ namespace RdtClient.Web services.AddSingleton(appSettings); - services.AddDbContext(options => options.UseSqlite(appSettings.ConnectionStrings.Client)); + services.AddDbContext(options => options.UseSqlite(DataContext.ConnectionString)); services.AddControllers(); @@ -51,6 +54,8 @@ namespace RdtClient.Web services.AddHttpsRedirection(options => { options.HttpsPort = 443; }); + services.AddHttpClient(); + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.SlidingExpiration = true; }); @@ -68,15 +73,22 @@ namespace RdtClient.Web .AddEntityFrameworkStores() .AddDefaultTokenProviders(); + services.AddHangfire(configuration => configuration + .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) + .UseSimpleAssemblyNameTypeSerializer() + .UseRecommendedSerializerSettings() + .UseMemoryStorage()); + + services.AddHangfireServer(); + Data.DiConfig.Config(services); Service.DiConfig.Config(services); } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext, IScheduler scheduler) { if (env.IsDevelopment()) { - app.UseDeveloperExceptionPage(); app.UseCors("Dev"); } else @@ -92,6 +104,8 @@ namespace RdtClient.Web app.UseAuthorization(); + app.UseHangfireServer(); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); @@ -101,6 +115,10 @@ namespace RdtClient.Web { spa.Options.SourcePath = "wwwroot"; }); + + dataContext.Migrate(); + + scheduler.Start(); } } } \ No newline at end of file diff --git a/server/RdtClient.Web/app.log b/server/RdtClient.Web/app.log new file mode 100644 index 0000000..59e484e --- /dev/null +++ b/server/RdtClient.Web/app.log @@ -0,0 +1,512 @@ +2020-04-03T15:59:20.1355273-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:20.1385649-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:20.2294123-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:20.2294368-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:20.2510079-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:3146ca38 successfully announced in 101.8542 ms +2020-04-03T15:59:20.2510111-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:f605d0e0 successfully announced in 20.7292 ms +2020-04-03T15:59:20.2541883-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:3146ca38 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:20.2541883-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:f605d0e0 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:20.2588662-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:f605d0e0 all the dispatchers started +2020-04-03T15:59:20.2589265-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:17836:3146ca38 all the dispatchers started +2020-04-03T15:59:20.9901692-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T15:59:20.9902829-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T15:59:20.9903021-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-03T15:59:22.6292638-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:22.6322230-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:22.7242705-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:22.7243034-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:22.7452766-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc successfully announced in 19.9586 ms +2020-04-03T15:59:22.7452765-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 successfully announced in 101.7602 ms +2020-04-03T15:59:22.7481327-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:22.7481351-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:22.7533167-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 all the dispatchers started +2020-04-03T15:59:22.7535232-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc all the dispatchers started +2020-04-03T15:59:23.4204963-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T15:59:23.4206249-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T15:59:23.4206510-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-03T15:59:30.7772267-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc caught stopping signal... +2020-04-03T15:59:30.7779767-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application is shutting down... +2020-04-03T15:59:30.7783343-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc caught stopped signal... +2020-04-03T15:59:30.7794920-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 caught stopping signal... +2020-04-03T15:59:30.7796436-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 caught stopped signal... +2020-04-03T15:59:30.7812297-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 All dispatchers stopped +2020-04-03T15:59:30.7812297-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc All dispatchers stopped +2020-04-03T15:59:30.7828238-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 successfully reported itself as stopped in 0.2783 ms +2020-04-03T15:59:30.7828222-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc successfully reported itself as stopped in 0.2778 ms +2020-04-03T15:59:30.7828386-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:15034db2 has been stopped in total 3.303 ms +2020-04-03T15:59:30.7828431-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25392:c48269cc has been stopped in total 5.4344 ms +2020-04-03T15:59:49.2420348-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:49.2449966-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:49.7330627-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T15:59:49.7342224-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T15:59:49.7767552-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:238a3170 successfully announced in 518.619 ms +2020-04-03T15:59:49.7778417-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:b384ecba successfully announced in 37.4892 ms +2020-04-03T15:59:49.7897107-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:238a3170 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:49.7907947-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:b384ecba is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T15:59:49.9335939-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:238a3170 all the dispatchers started +2020-04-03T15:59:49.9493934-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:7656:b384ecba all the dispatchers started +2020-04-03T15:59:50.8034032-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T15:59:50.8108739-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T15:59:50.8127580-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-03T16:06:29.6254854-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T16:06:29.6288727-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T16:06:29.7314839-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T16:06:29.7315143-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T16:06:29.7529384-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:dd12c44a successfully announced in 111.5091 ms +2020-04-03T16:06:29.7529396-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:15c956d5 successfully announced in 20.4625 ms +2020-04-03T16:06:29.7557415-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:dd12c44a is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T16:06:29.7557411-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:15c956d5 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T16:06:29.7606532-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:15c956d5 all the dispatchers started +2020-04-03T16:06:29.7608761-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18896:dd12c44a all the dispatchers started +2020-04-03T16:06:30.9858222-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T16:06:30.9940710-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T16:06:30.9966766-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-03T17:20:18.4268508-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T17:20:18.4340796-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T17:20:18.6487165-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T17:20:18.6500981-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T17:20:18.6947841-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:a6490904 successfully announced in 34.9489 ms +2020-04-03T17:20:18.6960854-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:b2e9a60c successfully announced in 230.7784 ms +2020-04-03T17:20:18.7105510-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:a6490904 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T17:20:18.7121052-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:b2e9a60c is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T17:20:18.8720117-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:a6490904 all the dispatchers started +2020-04-03T17:20:18.8774595-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:21168:b2e9a60c all the dispatchers started +2020-04-03T17:20:19.7386911-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T17:20:19.7416238-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T17:20:19.7486505-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-03T17:59:08.0700362-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T17:59:08.0750113-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T17:59:08.2398601-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-03T17:59:08.2411315-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-03T17:59:08.2913718-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:5ebe162c successfully announced in 42.0268 ms +2020-04-03T17:59:08.2925085-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:fb956c19 successfully announced in 197.5027 ms +2020-04-03T17:59:08.3038936-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:5ebe162c is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T17:59:08.3049895-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:fb956c19 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-03T17:59:08.4396784-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:5ebe162c all the dispatchers started +2020-04-03T17:59:08.4538634-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:10716:fb956c19 all the dispatchers started +2020-04-03T17:59:09.4423137-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-03T17:59:09.4523143-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-03T17:59:09.4550262-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T13:36:51.3899679-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:36:51.3954070-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:36:52.2046648-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:36:52.2058469-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:36:52.2165588-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:7232176f successfully announced in 5.629 ms +2020-04-04T13:36:52.2175955-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:0c930dc4 successfully announced in 783.68 ms +2020-04-04T13:36:52.2258088-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:0c930dc4 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:36:52.2270592-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:7232176f is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:36:52.3608248-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:0c930dc4 all the dispatchers started +2020-04-04T13:36:52.3651180-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18576:7232176f all the dispatchers started +2020-04-04T13:36:53.3815332-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T13:36:53.3835819-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T13:36:53.3855069-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T13:39:17.4290475-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:39:17.4351614-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:39:17.6188800-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:39:17.6200228-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:39:17.6633116-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:46ba0401 successfully announced in 205.437 ms +2020-04-04T13:39:17.6653058-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:626ef5ce successfully announced in 36.9854 ms +2020-04-04T13:39:17.6810736-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:46ba0401 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:39:17.6821454-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:626ef5ce is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:39:17.8313892-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:626ef5ce all the dispatchers started +2020-04-04T13:39:17.8518228-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:12312:46ba0401 all the dispatchers started +2020-04-04T13:39:18.6777049-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T13:39:18.6853112-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T13:39:18.6872717-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T13:45:52.2178972-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:45:52.2209059-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:45:52.7257245-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T13:45:52.7268420-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T13:45:52.7605590-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:371ab538 successfully announced in 29.31 ms +2020-04-04T13:45:52.7616163-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:beb90aed successfully announced in 527.3207 ms +2020-04-04T13:45:52.7947890-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:371ab538 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:45:52.7958992-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:beb90aed is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T13:45:52.9335865-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:beb90aed all the dispatchers started +2020-04-04T13:45:52.9433737-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:25428:371ab538 all the dispatchers started +2020-04-04T13:45:53.7610065-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T13:45:53.7667118-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T13:45:53.7686187-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:06:51.9875312-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:06:51.9910970-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:06:52.4849160-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:06:52.4860716-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:06:52.5226185-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:e9cb1104 successfully announced in 517.9776 ms +2020-04-04T14:06:52.5276877-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:2aa9fc9e successfully announced in 32.1255 ms +2020-04-04T14:06:52.5356524-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:2aa9fc9e is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:06:52.5368007-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:e9cb1104 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:06:52.6798034-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:e9cb1104 all the dispatchers started +2020-04-04T14:06:52.6843382-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:4496:2aa9fc9e all the dispatchers started +2020-04-04T14:06:53.5337779-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:06:53.5419397-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:06:53.5438903-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:09:28.6901247-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:09:28.6934944-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:09:28.8003885-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:09:28.8004416-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:09:28.8235716-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:2f63d054 successfully announced in 22.2108 ms +2020-04-04T14:09:28.8235763-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:6de68e38 successfully announced in 117.3382 ms +2020-04-04T14:09:28.8266785-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:6de68e38 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:09:28.8266804-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:2f63d054 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:09:28.8331305-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:6de68e38 all the dispatchers started +2020-04-04T14:09:28.8332117-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11712:2f63d054 all the dispatchers started +2020-04-04T14:09:30.0597550-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:09:30.0661109-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:09:30.0679982-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:10:30.2700465-06:00 FAIL [Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer] [ApplicationError] Connection ID "18158513750708060161", Request ID "80000002-000c-fc00-b63f-84710c7967bb": An unhandled exception was thrown by the application.System.Text.Json.JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. + at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth) + at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state) + at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken) + at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) + at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) + at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) + at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) + at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) + at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) + at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) + at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync() + +2020-04-04T14:11:12.4711056-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:11:12.4740177-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:11:12.5685353-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:11:12.5685678-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:11:12.5896902-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:f733969e successfully announced in 104.0185 ms +2020-04-04T14:11:12.5896902-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:85c7f28f successfully announced in 20.4301 ms +2020-04-04T14:11:12.5927719-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:85c7f28f is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:11:12.5927773-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:f733969e is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:11:12.5982186-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:85c7f28f all the dispatchers started +2020-04-04T14:11:12.5982679-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11556:f733969e all the dispatchers started +2020-04-04T14:11:13.8266479-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:11:13.8300908-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:11:13.8397525-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:23:21.7687075-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:23:21.7715620-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:23:21.8650133-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:23:21.8650397-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:23:21.8862045-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:63077182 successfully announced in 103.9192 ms +2020-04-04T14:23:21.8862045-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:8f1cdbd3 successfully announced in 19.9902 ms +2020-04-04T14:23:21.8889891-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:63077182 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:23:21.8889884-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:8f1cdbd3 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:23:21.8939088-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:63077182 all the dispatchers started +2020-04-04T14:23:21.8939936-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:13516:8f1cdbd3 all the dispatchers started +2020-04-04T14:23:23.0689646-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:23:23.0709673-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:23:23.0779228-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:24:37.9847342-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:24:37.9882985-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:24:38.0825819-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:24:38.0826064-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:24:38.1036306-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:c1366df7 successfully announced in 103.9479 ms +2020-04-04T14:24:38.1036305-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:e3e831b7 successfully announced in 20.0026 ms +2020-04-04T14:24:38.1065641-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:c1366df7 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:24:38.1065662-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:e3e831b7 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:24:38.1114065-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:e3e831b7 all the dispatchers started +2020-04-04T14:24:38.1114793-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22580:c1366df7 all the dispatchers started +2020-04-04T14:24:39.2282210-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:24:39.2423550-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:24:39.2447909-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:28:26.3492596-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:28:26.3544781-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:28:26.5174135-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:28:26.5187379-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:28:26.5578223-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:c6bc92b2 successfully announced in 182.3826 ms +2020-04-04T14:28:26.5588998-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:20d17df5 successfully announced in 34.3576 ms +2020-04-04T14:28:26.5704666-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:c6bc92b2 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:28:26.5716322-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:20d17df5 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:28:26.7149113-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:20d17df5 all the dispatchers started +2020-04-04T14:28:26.7226988-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:20488:c6bc92b2 all the dispatchers started +2020-04-04T14:28:27.5476942-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:28:27.5548312-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:28:27.5565374-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:29:22.4970867-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:29:22.5000868-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:29:22.5972226-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:29:22.5972498-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:29:22.6185406-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:7f562bd0 successfully announced in 20.3044 ms +2020-04-04T14:29:22.6185503-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:25c03ed6 successfully announced in 107.2263 ms +2020-04-04T14:29:22.6212883-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:25c03ed6 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:29:22.6212868-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:7f562bd0 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:29:22.6272480-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:25c03ed6 all the dispatchers started +2020-04-04T14:29:22.6275399-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:8704:7f562bd0 all the dispatchers started +2020-04-04T14:29:23.8020576-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:29:23.8092332-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:29:23.8114626-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:33:29.3029557-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:33:29.3060421-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:33:29.3995563-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:33:29.3996017-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:33:29.4210320-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:78e298c5 successfully announced in 103.7016 ms +2020-04-04T14:33:29.4210325-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:79ee0690 successfully announced in 20.3431 ms +2020-04-04T14:33:29.4238373-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:78e298c5 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:33:29.4238390-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:79ee0690 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:33:29.4293346-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:78e298c5 all the dispatchers started +2020-04-04T14:33:29.4294031-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:11468:79ee0690 all the dispatchers started +2020-04-04T14:33:30.5171651-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:33:30.5207118-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:33:30.5262510-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:35:26.4931298-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:35:26.4960740-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:35:26.5902385-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:35:26.5902725-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:35:26.6112554-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:548bcc22 successfully announced in 20.0425 ms +2020-04-04T14:35:26.6112554-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:a39fa135 successfully announced in 104.5842 ms +2020-04-04T14:35:26.6138703-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:548bcc22 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:35:26.6138680-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:a39fa135 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:35:26.6186498-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:a39fa135 all the dispatchers started +2020-04-04T14:35:26.6187979-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9364:548bcc22 all the dispatchers started +2020-04-04T14:35:27.7550471-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:35:27.7577200-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:35:27.7656669-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:36:13.4963403-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:36:13.4995347-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:36:13.5937296-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:36:13.5937661-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:36:13.6136017-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:d3f096b7 successfully announced in 102.8649 ms +2020-04-04T14:36:13.6136017-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:0486235c successfully announced in 18.8774 ms +2020-04-04T14:36:13.6161824-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:0486235c is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:36:13.6161810-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:d3f096b7 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:36:13.6211768-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:0486235c all the dispatchers started +2020-04-04T14:36:13.6212520-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:24484:d3f096b7 all the dispatchers started +2020-04-04T14:36:14.7116432-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:36:14.7193962-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:36:14.7217224-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:44:06.0389258-06:00 FAIL [Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer] [ApplicationError] Connection ID "18302628887244309062", Request ID "80000247-0000-fe00-b63f-84710c7967bb": An unhandled exception was thrown by the application.System.InvalidOperationException: The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request. + + at Microsoft.AspNetCore.SpaServices.SpaDefaultPageMiddleware.<>c__DisplayClass0_0.b__1(HttpContext context, Func`1 next) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_1.b__1(HttpContext context) + at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.TryServeStaticFile(HttpContext context, String contentType, PathString subPath) + at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_2.b__2() + at Microsoft.AspNetCore.SpaServices.SpaDefaultPageMiddleware.<>c__DisplayClass0_0.b__0(HttpContext context, Func`1 next) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_1.b__1(HttpContext context) + at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) + at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) + at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync() + +2020-04-04T14:44:07.1916383-06:00 FAIL [Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer] [ApplicationError] Connection ID "18302628930193982207", Request ID "80000300-000a-fe00-b63f-84710c7967bb": An unhandled exception was thrown by the application.System.InvalidOperationException: The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request. + + at Microsoft.AspNetCore.SpaServices.SpaDefaultPageMiddleware.<>c__DisplayClass0_0.b__1(HttpContext context, Func`1 next) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_1.b__1(HttpContext context) + at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.TryServeStaticFile(HttpContext context, String contentType, PathString subPath) + at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_2.b__2() + at Microsoft.AspNetCore.SpaServices.SpaDefaultPageMiddleware.<>c__DisplayClass0_0.b__0(HttpContext context, Func`1 next) + at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_1.b__1(HttpContext context) + at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) + at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) + at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync() + +2020-04-04T14:47:54.0323392-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:47:54.0367216-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:47:54.1985129-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:47:54.1996782-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:47:54.2766426-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:7a1f1a6b successfully announced in 70.8826 ms +2020-04-04T14:47:54.2806642-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:d0351e6f successfully announced in 214.1212 ms +2020-04-04T14:47:54.2889140-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:7a1f1a6b is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:47:54.2900438-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:d0351e6f is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:47:54.4139151-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:d0351e6f all the dispatchers started +2020-04-04T14:47:54.4368669-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:9924:7a1f1a6b all the dispatchers started +2020-04-04T14:47:55.2715624-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:47:55.2771286-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:47:55.2789075-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:48:49.5565338-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:48:49.5594208-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:48:49.6547594-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:48:49.6547886-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:48:49.6754648-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:b12db8cb successfully announced in 105.0262 ms +2020-04-04T14:48:49.6754648-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:fd149353 successfully announced in 19.6826 ms +2020-04-04T14:48:49.6783402-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:b12db8cb is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:48:49.6783431-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:fd149353 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:48:50.1962026-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:fd149353 all the dispatchers started +2020-04-04T14:48:50.1981452-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:22936:b12db8cb all the dispatchers started +2020-04-04T14:48:51.0147387-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:48:51.0220660-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:48:51.0239132-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web +2020-04-04T14:50:09.5762316-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:50:09.5792141-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:50:09.6739724-06:00 INFO [Hangfire.BackgroundJobServer] [0] Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage' +2020-04-04T14:50:09.6740028-06:00 INFO [Hangfire.BackgroundJobServer] [0] Using the following options for Hangfire Server: + Worker count: 20 + Listening queues: 'default' + Shutdown timeout: 00:00:15 + Schedule polling interval: 00:00:15 +2020-04-04T14:50:09.6944222-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:9c827089 successfully announced in 19.5569 ms +2020-04-04T14:50:09.6944222-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:a1cafa69 successfully announced in 104.5052 ms +2020-04-04T14:50:09.6970626-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:9c827089 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:50:09.6970626-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:a1cafa69 is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler... +2020-04-04T14:50:09.7022113-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:9c827089 all the dispatchers started +2020-04-04T14:50:09.7024526-06:00 INFO [Hangfire.Server.BackgroundServerProcess] [0] Server roger-pc:18196:a1cafa69 all the dispatchers started +2020-04-04T14:50:10.8721989-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Application started. Press Ctrl+C to shut down. +2020-04-04T14:50:10.8807198-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Hosting environment: Development +2020-04-04T14:50:10.8830457-06:00 INFO [Microsoft.Hosting.Lifetime] [0] Content root path: C:\Projects\rdt-client\server\RdtClient.Web diff --git a/server/RdtClient.Web/appsettings.json b/server/RdtClient.Web/appsettings.json index 0cd7023..1553cb2 100644 --- a/server/RdtClient.Web/appsettings.json +++ b/server/RdtClient.Web/appsettings.json @@ -3,11 +3,15 @@ "LogLevel": { "Default": "Information", "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.Hosting.Lifetime": "Information", + "Hangfire": "Information" + }, + "File": { + "Path": "app.log", + "Append": "True", + "FileSizeLimitBytes": 2048, + "MaxRollingFiles": 5 } }, - "AllowedHosts": "*", - "ConnectionStrings": { - "Client": "Host=localhost;Database=fieldcap;Username=fieldcap;Password=fieldcap;Maximum Pool Size=30" - } + "AllowedHosts": "*" } diff --git a/server/libs/RDNET.deps.json b/server/libs/RDNET.deps.json new file mode 100644 index 0000000..cae48d8 --- /dev/null +++ b/server/libs/RDNET.deps.json @@ -0,0 +1,63 @@ +{ + "runtimeTarget": { + "name": ".NETStandard,Version=v2.0/", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETStandard,Version=v2.0": {}, + ".NETStandard,Version=v2.0/": { + "RDNET/1.0.0": { + "dependencies": { + "NETStandard.Library": "2.0.3", + "Newtonsoft.Json": "12.0.3" + }, + "runtime": { + "RDNET.dll": {} + } + }, + "Microsoft.NETCore.Platforms/1.1.0": {}, + "NETStandard.Library/2.0.3": { + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "Newtonsoft.Json/12.0.3": { + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "assemblyVersion": "12.0.0.0", + "fileVersion": "12.0.3.23909" + } + } + } + } + }, + "libraries": { + "RDNET/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.NETCore.Platforms/1.1.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", + "path": "microsoft.netcore.platforms/1.1.0", + "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" + }, + "NETStandard.Library/2.0.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "path": "netstandard.library/2.0.3", + "hashPath": "netstandard.library.2.0.3.nupkg.sha512" + }, + "Newtonsoft.Json/12.0.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==", + "path": "newtonsoft.json/12.0.3", + "hashPath": "newtonsoft.json.12.0.3.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/server/libs/RDNET.dll b/server/libs/RDNET.dll new file mode 100644 index 0000000..9fb3f78 Binary files /dev/null and b/server/libs/RDNET.dll differ diff --git a/server/libs/RDNET.pdb b/server/libs/RDNET.pdb new file mode 100644 index 0000000..61640b9 Binary files /dev/null and b/server/libs/RDNET.pdb differ