From 8a0843128ca5a897a175cbf355463cb037beb597 Mon Sep 17 00:00:00 2001 From: akastijn Date: Sun, 4 Jan 2026 05:22:04 +0100 Subject: [PATCH] Refactor particle management: remove secret key from file operations, add `ParticleManagerComponent`, update frame naming conventions, and enhance style and functionality. --- .../altitudeweb/config/SecurityConfig.java | 2 + .../particles/ParticleController.java | 21 ++--- .../particle-manager.component.html | 39 +++++++++ .../particle-manager.component.scss | 6 ++ .../particle-manager.component.ts | 86 +++++++++++++++++++ .../pages/particles/particles.component.html | 3 + .../pages/particles/particles.component.ts | 4 +- .../services/frame-manager.service.ts | 9 +- .../services/intersection-plane.service.ts | 2 +- .../services/particle-manager.service.ts | 12 ++- open_api/src/main/resources/api.yml | 2 +- .../resources/schemas/particles/particles.yml | 13 +-- 12 files changed, 164 insertions(+), 35 deletions(-) create mode 100644 frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.html create mode 100644 frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.scss create mode 100644 frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.ts diff --git a/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java b/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java index d953edb..3517e09 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java +++ b/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java @@ -57,6 +57,8 @@ public class SecurityConfig { .requestMatchers("/api/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/files/save/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) + //TODO allow users access to their own folder + .requestMatchers("/api/files/download/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/history/admin/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/login/userLogin/**").permitAll() .anyRequest().permitAll() diff --git a/backend/src/main/java/com/alttd/altitudeweb/controllers/particles/ParticleController.java b/backend/src/main/java/com/alttd/altitudeweb/controllers/particles/ParticleController.java index 276b170..6e00f1c 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/controllers/particles/ParticleController.java +++ b/backend/src/main/java/com/alttd/altitudeweb/controllers/particles/ParticleController.java @@ -34,24 +34,17 @@ public class ParticleController implements ParticlesApi { private String notificationServerUrl; @Override - public ResponseEntity downloadFile(String authorization, String filename) throws Exception { - if (authorization == null || !authorization.equals(loginSecret)) { - return ResponseEntity.status(401).build(); - } + public ResponseEntity downloadFile(String filename) { File file = new File(particlesFilePath); if (!file.exists() || !file.isDirectory()) { log.error("Particles file path {} is not a directory, not downloading particles file", particlesFilePath); return ResponseEntity.status(404).build(); } - File targetFile = new File(file, filename); - return getFileForDownload(targetFile, filename); + return getFileForDownload(file, filename); } @Override - public ResponseEntity downloadFileForUser(String authorization, String uuid, String filename) throws Exception { - if (authorization == null || !authorization.equals(loginSecret)) { - return ResponseEntity.status(401).build(); - } + public ResponseEntity downloadFileForUser(String uuid, String filename) { File file = new File(particlesFilePath); if (!file.exists() || !file.isDirectory()) { log.error("Particles file path {} is not a directory, not downloading particles user file", particlesFilePath); @@ -67,6 +60,7 @@ public class ParticleController implements ParticlesApi { } private ResponseEntity getFileForDownload(File file, String filename) { + filename += ".json"; File targetFile = new File(file, filename); if (!targetFile.exists()) { log.warn("Particles file {} does not exist", targetFile.getAbsolutePath()); @@ -95,7 +89,7 @@ public class ParticleController implements ParticlesApi { } @Override - public ResponseEntity saveFile(String filename, MultipartFile content) throws Exception { + public ResponseEntity saveFile(String filename, MultipartFile content) { File file = new File(particlesFilePath); if (!file.exists() || !file.isDirectory()) { log.error("Particles file path {} is not a directory, not saving particles file", particlesFilePath); @@ -107,7 +101,7 @@ public class ParticleController implements ParticlesApi { } @Override - public ResponseEntity saveFileForUser(String uuid, String filename, MultipartFile content) throws Exception { + public ResponseEntity saveFileForUser(String uuid, String filename, MultipartFile content) { File file = new File(particlesFilePath); if (!file.exists() || !file.isDirectory()) { log.error("Particles file path {} is not a directory, not saving particles user file", particlesFilePath); @@ -127,7 +121,7 @@ public class ParticleController implements ParticlesApi { } private void notifyServerOfFileUpload(String filename) { - String notificationUrl = String.format("%s/notify/%s.json", notificationServerUrl, filename); + String notificationUrl = String.format("http://%s/notify/%s.json", notificationServerUrl, filename); sendNotification(notificationUrl, String.format("file upload: %s", filename)); } @@ -154,6 +148,7 @@ public class ParticleController implements ParticlesApi { private ResponseEntity writeContentToFile(File dir, String filename, MultipartFile content) { + filename += ".json"; File targetFile = new File(dir, filename); if (!Files.isWritable(targetFile.toPath())) { log.error("Particles file {} is not writable", targetFile.getAbsolutePath()); diff --git a/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.html b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.html new file mode 100644 index 0000000..4ba0982 --- /dev/null +++ b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.html @@ -0,0 +1,39 @@ +
+ + + + Particle Manager + + + +
+
+
+ + Particle to download + + + +
+
+ + Particle to upload + + + +
+
+
+
+
diff --git a/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.scss b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.scss new file mode 100644 index 0000000..a368066 --- /dev/null +++ b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.scss @@ -0,0 +1,6 @@ +.card-div { + mat-card { + background-color: var(--color-primary); + color: var(--font-color); + } +} diff --git a/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.ts b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.ts new file mode 100644 index 0000000..4431bb0 --- /dev/null +++ b/frontend/src/app/pages/particles/components/particle-manager/particle-manager.component.ts @@ -0,0 +1,86 @@ +import {Component, inject} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {MatInput, MatLabel} from '@angular/material/input'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card'; +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import {ParticlesService} from '@api'; +import {ParticleManagerService} from '@pages/particles/services/particle-manager.service'; +import {FrameManagerService} from '@pages/particles/services/frame-manager.service'; + +@Component({ + selector: 'app-particle-manager', + imports: [ + FormsModule, + MatFormFieldModule, + MatInput, + MatLabel, + MatCard, + MatCardContent, + MatCardHeader, + MatCardTitle, + MatIconModule, + MatButtonModule, + ], + templateUrl: './particle-manager.component.html', + styleUrl: './particle-manager.component.scss' +}) +export class ParticleManagerComponent { + + private readonly particlesService = inject(ParticlesService) + private readonly particleManagerService = inject(ParticleManagerService) + private readonly frameManagerService = inject(FrameManagerService) + + private selectedParticleName: string = ''; + + set selectedParticle(particle: string) { + this.selectedParticleName = particle; + } + + get selectedParticle(): string { + return this.selectedParticleName; + } + + get createdParticleName(): string { + return this.particleManagerService.getParticleData().particle_name; + } + + protected downloadParticle() { + if (this.selectedParticleName === '') { + return; + } + this.particlesService + .downloadFile(this.selectedParticleName) + .subscribe({ + next: data => { + data.text().then(text => { + if (text.startsWith('<')) { + console.error("Failed to download particle: Invalid file format") + return; + } + this.particleManagerService.loadParticleData(text) + this.frameManagerService.switchFrame(this.particleManagerService.getCurrentFrame()) + }) + }, + error: () => { + console.error("Failed to download particle: Invalid file name") + }, + } + ) + } + + protected uploadParticle() { + this.particlesService + .saveFile(this.createdParticleName, new Blob([this.particleManagerService.generateJson()], {type: 'application/json'})) + .subscribe({ + next: () => { + console.log("Successfully uploaded particle") + }, + error: () => { + console.error("Failed to upload particle") + } + }) + } + +} diff --git a/frontend/src/app/pages/particles/particles.component.html b/frontend/src/app/pages/particles/particles.component.html index 8d920de..42138bd 100644 --- a/frontend/src/app/pages/particles/particles.component.html +++ b/frontend/src/app/pages/particles/particles.component.html @@ -26,6 +26,9 @@
+ + +