diff --git a/.github/workflows/build-test-scan-push-images.yml b/.github/workflows/build-test-scan-push-images.yml
index 4e47fc5..84a9921 100644
--- a/.github/workflows/build-test-scan-push-images.yml
+++ b/.github/workflows/build-test-scan-push-images.yml
@@ -95,6 +95,7 @@ jobs:
image-ref: '${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.SUB_NAMESPACE }}/${{ matrix.image.name }}:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
+ limit-severities-for-sarif: true
exit-code: '1'
severity: 'CRITICAL,HIGH'
diff --git a/frontend/indiestream/src/app/app.component.html b/frontend/indiestream/src/app/app.component.html
index e4b6df0..91e85a7 100644
--- a/frontend/indiestream/src/app/app.component.html
+++ b/frontend/indiestream/src/app/app.component.html
@@ -204,6 +204,8 @@
Hello, {{ title }}
+
+
diff --git a/frontend/indiestream/src/app/app.component.spec.ts b/frontend/indiestream/src/app/app.component.spec.ts
index 32a0f83..8680c1e 100644
--- a/frontend/indiestream/src/app/app.component.spec.ts
+++ b/frontend/indiestream/src/app/app.component.spec.ts
@@ -1,10 +1,11 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
+import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [AppComponent],
+ imports: [AppComponent, BrowserAnimationsModule],
}).compileComponents();
});
diff --git a/frontend/indiestream/src/app/app.component.ts b/frontend/indiestream/src/app/app.component.ts
index f35e409..56078cf 100644
--- a/frontend/indiestream/src/app/app.component.ts
+++ b/frontend/indiestream/src/app/app.component.ts
@@ -5,11 +5,12 @@ import { Game } from "./modules/games";
import { CommonModule } from "@angular/common";
import { MatButtonModule } from '@angular/material/button';
import { HttpClientModule } from "@angular/common/http";
+import { GameUploadComponent} from "./components/game-upload/game-upload.component";
@Component({
selector: 'app-root',
standalone: true,
- imports: [RouterOutlet, CommonModule, MatButtonModule, HttpClientModule],
+ imports: [RouterOutlet, CommonModule, MatButtonModule, HttpClientModule, GameUploadComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
diff --git a/frontend/indiestream/src/app/app.config.ts b/frontend/indiestream/src/app/app.config.ts
index f1c3753..9e293ec 100644
--- a/frontend/indiestream/src/app/app.config.ts
+++ b/frontend/indiestream/src/app/app.config.ts
@@ -1,10 +1,11 @@
-import { ApplicationConfig } from '@angular/core';
+import {ApplicationConfig, importProvidersFrom} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
-import {provideHttpClient} from "@angular/common/http";
+import { provideHttpClient } from "@angular/common/http";
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
+import {provideAnimations} from "@angular/platform-browser/animations";
export const appConfig: ApplicationConfig = {
- providers: [provideRouter(routes), provideHttpClient(), provideAnimationsAsync()]
+ providers: [provideRouter(routes), provideHttpClient(), provideAnimationsAsync(), provideAnimations()]
};
diff --git a/frontend/indiestream/src/app/components/game-upload/game-upload.component.html b/frontend/indiestream/src/app/components/game-upload/game-upload.component.html
new file mode 100644
index 0000000..e11cba9
--- /dev/null
+++ b/frontend/indiestream/src/app/components/game-upload/game-upload.component.html
@@ -0,0 +1,29 @@
+
diff --git a/frontend/indiestream/src/app/components/game-upload/game-upload.component.scss b/frontend/indiestream/src/app/components/game-upload/game-upload.component.scss
new file mode 100644
index 0000000..41e0ac7
--- /dev/null
+++ b/frontend/indiestream/src/app/components/game-upload/game-upload.component.scss
@@ -0,0 +1,20 @@
+.game-upload-container {
+ display: inline-block;
+}
+
+.input-file {
+ display: none;
+}
+
+.input-fields {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.input-title {
+ margin-right: 10px;
+}
+
+.progress-bar {
+ margin-bottom: 5px;
+}
diff --git a/frontend/indiestream/src/app/components/game-upload/game-upload.component.spec.ts b/frontend/indiestream/src/app/components/game-upload/game-upload.component.spec.ts
new file mode 100644
index 0000000..06afcec
--- /dev/null
+++ b/frontend/indiestream/src/app/components/game-upload/game-upload.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GameUploadComponent } from './game-upload.component';
+import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
+
+describe('GameUploadComponent', () => {
+ let component: GameUploadComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [GameUploadComponent, BrowserAnimationsModule]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(GameUploadComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/indiestream/src/app/components/game-upload/game-upload.component.ts b/frontend/indiestream/src/app/components/game-upload/game-upload.component.ts
new file mode 100644
index 0000000..969dcdd
--- /dev/null
+++ b/frontend/indiestream/src/app/components/game-upload/game-upload.component.ts
@@ -0,0 +1,71 @@
+import { Component } from '@angular/core';
+import { MatButtonModule } from '@angular/material/button';
+import { MatProgressBar } from "@angular/material/progress-bar";
+import { MatIcon } from "@angular/material/icon";
+import { HttpClientModule, HttpEventType } from "@angular/common/http";
+import {finalize, Subscription} from "rxjs";
+import {NgIf} from "@angular/common";
+import {GamesService} from "../../services/games.service";
+import {MatFormField, MatHint, MatInput, MatLabel, MatSuffix} from "@angular/material/input";
+import {FormBuilder, ReactiveFormsModule, Validators} from "@angular/forms";
+
+@Component({
+ selector: 'app-game-upload',
+ standalone: true,
+ imports: [
+ MatButtonModule,
+ MatProgressBar,
+ MatIcon,
+ NgIf,
+ MatInput,
+ MatHint,
+ MatFormField,
+ MatLabel,
+ MatSuffix,
+ ReactiveFormsModule,
+ HttpClientModule,
+ ],
+ templateUrl: './game-upload.component.html',
+ styleUrl: './game-upload.component.scss'
+})
+export class GameUploadComponent {
+
+ uploadProgress: number = 0;
+ uploadSub: Subscription = new Subscription();
+ file_store: FileList = new DataTransfer().files;
+ gameForm = this.fb.group({
+ title: ['', Validators.required],
+ filename: ['', Validators.required]
+ });
+
+ constructor(private gamesService: GamesService, private fb: FormBuilder) {}
+
+ onFileSelected(event: any) {
+ const file:File = event.target.files[0];
+ if (file) {
+ this.file_store = event.target.files;
+ this.gameForm.patchValue({ filename: file.name});
+ }
+ }
+
+ onUpload() {
+ const upload$ = this.gamesService.uploadGame(this.file_store[0])
+ .pipe(
+ finalize(() => this.reset())
+ );
+
+ this.uploadSub = upload$.subscribe(event => {
+ if (event.type == HttpEventType.UploadProgress && event.total !== undefined) {
+ this.uploadProgress = Math.round(100 * (event.loaded / event.total));
+ }
+ })
+ }
+
+ reset() {
+ this.uploadProgress = 0;
+ this.uploadSub.unsubscribe();
+ this.uploadSub = new Subscription();
+ this.gameForm.reset();
+ this.file_store = new DataTransfer().files;
+ }
+}
diff --git a/frontend/indiestream/src/app/services/games.service.ts b/frontend/indiestream/src/app/services/games.service.ts
index 77d32a9..ef3f400 100644
--- a/frontend/indiestream/src/app/services/games.service.ts
+++ b/frontend/indiestream/src/app/services/games.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from "@angular/core";
-import { HttpClient } from '@angular/common/http';
+import {HttpClient, HttpEvent} from '@angular/common/http';
import { Games, Game } from '../modules/games';
import { Observable } from "rxjs";
import { environment } from "../environment";
@@ -22,5 +22,12 @@ export class GamesService {
deleteGame(id: string): void{
this.http.delete(this.apiUrl + "/games/" + id + "/")
}
+
+ uploadGame(file: File): Observable>{
+ return this.http.post(this.apiUrl + "/games/", file, {
+ reportProgress: true,
+ observe: 'events'
+ });
+ }
}