diff --git a/packages/bridge-server/src/upload/upload.controller.ts b/packages/bridge-server/src/upload/upload.controller.ts
index b21b680..36720de 100644
--- a/packages/bridge-server/src/upload/upload.controller.ts
+++ b/packages/bridge-server/src/upload/upload.controller.ts
@@ -1,14 +1,17 @@
-import { Controller, Post, UseInterceptors } from '@nestjs/common';
+import { Controller, NotAcceptableException, Post, UseInterceptors, Request } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
-import { Body, UploadedFile } from '@nestjs/common/decorators';
+import { Body, UploadedFile, UseGuards } from '@nestjs/common/decorators';
import { Express } from 'express'
import { UploadService } from './upload.service';
+import { JwtAuthGuard } from 'src/authz/authz.guard';
+import { UsersService } from 'src/users/users.service';
@Controller('upload')
export class UploadController {
constructor(
- private uploadService: UploadService
+ private uploadService: UploadService,
+ private usersService: UsersService
){}
@Post('replay')
@@ -16,4 +19,15 @@ export class UploadController {
uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body: any) {
return this.uploadService.replayUploaded(file, body);
}
+
+ @UseGuards(JwtAuthGuard)
+ @Post('claim-racer')
+ @UseInterceptors(FileInterceptor('file'))
+ async claimRacer(@UploadedFile() file: Express.Multer.File, @Body() body: any, @Request() req) {
+ let racer = await this.uploadService.getRacerFromReplay(file);
+ if(!racer) {
+ throw new NotAcceptableException('NoRacerFound', {cause: new Error(), description: 'No existing racer found.'});
+ }
+ return this.usersService.claimRacer(req.user, racer);
+ }
}
diff --git a/packages/bridge-server/src/upload/upload.module.ts b/packages/bridge-server/src/upload/upload.module.ts
index 904a4da..2d31b15 100644
--- a/packages/bridge-server/src/upload/upload.module.ts
+++ b/packages/bridge-server/src/upload/upload.module.ts
@@ -5,6 +5,7 @@ import { RacersModule } from 'src/racers/racers.module';
import { RacesModule } from 'src/races/races.module';
import { SeasonStandingsModule } from 'src/season-standings/season-standings.module';
import { RaceResultsModule } from 'src/race-results/race-results.module';
+import { UsersModule } from 'src/users/users.module';
@Module({
imports:[
@@ -12,6 +13,7 @@ import { RaceResultsModule } from 'src/race-results/race-results.module';
RacesModule,
RaceResultsModule,
SeasonStandingsModule,
+ UsersModule
],
providers: [UploadService],
controllers: [UploadController],
diff --git a/packages/bridge-ui/src/app/app.config.ts b/packages/bridge-ui/src/app/app.config.ts
index 1011b5f..9eac268 100644
--- a/packages/bridge-ui/src/app/app.config.ts
+++ b/packages/bridge-ui/src/app/app.config.ts
@@ -32,40 +32,11 @@ export const appConfig: ApplicationConfig = {
{
uriMatcher: (uri) => {
let is_create = uri.match('.+\/create');
- return is_create != null;
- },
- tokenOptions: {
- authorizationParams: {
- audience: 'https://ponyta.pkmn.cloud'
- }
- }
- },
- {
- uriMatcher: (uri) => {
let is_update = uri.match('.+\/update');
- return is_update != null;
- },
- tokenOptions: {
- authorizationParams: {
- audience: 'https://ponyta.pkmn.cloud'
- }
- }
- },
- {
- uriMatcher: (uri) => {
let is_delete = uri.match('.+\/delete');
- return is_delete != null;
- },
- tokenOptions: {
- authorizationParams: {
- audience: 'https://ponyta.pkmn.cloud'
- }
- }
- },
- {
- uriMatcher: (uri) => {
let is_login = uri.match('.+\/login');
- return is_login != null;
+ let is_claim = uri.match('.+\/claim-racer');
+ return is_create != null || is_update != null || is_delete != null || is_claim != null;
},
tokenOptions: {
authorizationParams: {
diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html
new file mode 100644
index 0000000..23b53c4
--- /dev/null
+++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html
@@ -0,0 +1,17 @@
+
diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss
new file mode 100644
index 0000000..b1bc93e
--- /dev/null
+++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss
@@ -0,0 +1,16 @@
+.file-input {
+ display: none;
+}
+
+.signInForm {
+ width: 400px;
+ padding: 12px 24px 24px;
+
+ igx-input-group + igx-input-group {
+ margin-top: 24px;
+ }
+ }
+
+ .full-width {
+ width: 100%;
+ }
\ No newline at end of file
diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts
new file mode 100644
index 0000000..e92b1ce
--- /dev/null
+++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ClaimRacerDialogComponent } from './claim-racer-dialog.component';
+
+describe('ClaimRacerDialogComponent', () => {
+ let component: ClaimRacerDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ClaimRacerDialogComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ClaimRacerDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts
new file mode 100644
index 0000000..2fe36fa
--- /dev/null
+++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts
@@ -0,0 +1,73 @@
+import { Component, Inject } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule, FormControl } from '@angular/forms';
+import { FormsModule } from '@angular/forms';
+
+import { MatDialog, MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle,
+ MatDialogContent, MatDialogActions, MatDialogClose,} from '@angular/material/dialog';
+
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatSnackBar } from '@angular/material/snack-bar';
+
+import { ReplaysService } from '../../services/replays.service';
+import { catchError, throwError } from 'rxjs';
+import { HttpErrorResponse } from '@angular/common/http';
+
+@Component({
+ selector: 'app-claim-racer-dialog',
+ standalone: true,
+ imports: [
+ CommonModule,
+ ReactiveFormsModule,
+ MatButtonModule,
+ MatDialogModule,
+ MatInputModule,
+ MatFormFieldModule,
+ FormsModule,
+ MatDialogTitle,
+ MatDialogContent,
+ MatDialogActions,
+ MatDialogClose,
+ MatIconModule,
+ ],
+ templateUrl: './claim-racer-dialog.component.html',
+ styleUrl: './claim-racer-dialog.component.scss'
+})
+export class ClaimRacerDialogComponent {
+ fileName: string = "";
+ file?: File;
+ errors: string[];
+
+ constructor(
+ private replaysService: ReplaysService,
+ private _snackBar: MatSnackBar
+ ) {
+ this.errors = [];
+ }
+
+ onFileSelected(event: any) {
+ const file: File = event.target.files[0];
+ if (file)
+ {
+ this.fileName = file.name;
+ this.file = file;
+ }
+ }
+
+ onClickSubmit() {
+ if(this.file == undefined) {
+ return;
+ }
+
+ let formData = new FormData();
+ formData.append("file", this.file);
+ let filetime = this.file.lastModified;
+ formData.append("fileTime", filetime.toString());
+ const upload$ = this.replaysService.claimRacer(formData);
+ upload$.subscribe();
+ }
+}
diff --git a/packages/bridge-ui/src/app/pages/user/user.component.html b/packages/bridge-ui/src/app/pages/user/user.component.html
index a1a107a..a62fe8d 100644
--- a/packages/bridge-ui/src/app/pages/user/user.component.html
+++ b/packages/bridge-ui/src/app/pages/user/user.component.html
@@ -22,7 +22,7 @@
-
+
diff --git a/packages/bridge-ui/src/app/pages/user/user.component.ts b/packages/bridge-ui/src/app/pages/user/user.component.ts
index c8dff83..00bb9fb 100644
--- a/packages/bridge-ui/src/app/pages/user/user.component.ts
+++ b/packages/bridge-ui/src/app/pages/user/user.component.ts
@@ -4,8 +4,11 @@ import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
+import { MatDialog } from '@angular/material/dialog';
import { UsersService } from '../../services/users.service';
+import { ClaimRacerDialogComponent } from '../../components/claim-racer-dialog/claim-racer-dialog.component';
+
@Component({
selector: 'app-user',
@@ -22,5 +25,11 @@ import { UsersService } from '../../services/users.service';
export class UserComponent {
constructor(
public usersService: UsersService,
+ private dialog: MatDialog,
) {}
+
+ openClaimRacerDialog()
+ {
+ this.dialog.open(ClaimRacerDialogComponent);
+ }
}
diff --git a/packages/bridge-ui/src/app/services/replays.service.ts b/packages/bridge-ui/src/app/services/replays.service.ts
index e084281..1ff8bd5 100644
--- a/packages/bridge-ui/src/app/services/replays.service.ts
+++ b/packages/bridge-ui/src/app/services/replays.service.ts
@@ -19,4 +19,10 @@ export class ReplaysService {
headers.append('Content-Type', 'multipart/form-data');
return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"upload/replay", newReplay, { headers });
}
+
+ claimRacer(newReplay: FormData) {
+ const headers = new HttpHeaders();
+ headers.append('Content-Type', 'multipart/form-data');
+ return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"upload/claim-racer", newReplay, { headers });
+ }
}