Refactor particle management: remove secret key from file operations, add ParticleManagerComponent, update frame naming conventions, and enhance style and functionality.
This commit is contained in:
parent
63aa7fd550
commit
8a0843128c
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -34,24 +34,17 @@ public class ParticleController implements ParticlesApi {
|
|||
private String notificationServerUrl;
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Resource> downloadFile(String authorization, String filename) throws Exception {
|
||||
if (authorization == null || !authorization.equals(loginSecret)) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
public ResponseEntity<Resource> 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<Resource> downloadFileForUser(String authorization, String uuid, String filename) throws Exception {
|
||||
if (authorization == null || !authorization.equals(loginSecret)) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
public ResponseEntity<Resource> 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<Resource> 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<Void> saveFile(String filename, MultipartFile content) throws Exception {
|
||||
public ResponseEntity<Void> 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<Void> saveFileForUser(String uuid, String filename, MultipartFile content) throws Exception {
|
||||
public ResponseEntity<Void> 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<Void> 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());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
<div class="card-div">
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<span>Particle Manager</span>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
<mat-form-field appearance="outline" class="type-field">
|
||||
<mat-label>Particle to download</mat-label>
|
||||
<input type="text"
|
||||
[(ngModel)]="selectedParticle"
|
||||
placeholder="Name of particle to download"
|
||||
matInput>
|
||||
</mat-form-field>
|
||||
<button mat-icon-button (click)="downloadParticle()">
|
||||
<mat-icon>download</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-form-field appearance="outline" class="type-field">
|
||||
<mat-label>Particle to upload</mat-label>
|
||||
<input type="text"
|
||||
disabled
|
||||
[ngModel]="createdParticleName"
|
||||
placeholder="Name of particle to upload"
|
||||
matInput>
|
||||
</mat-form-field>
|
||||
<button mat-icon-button (click)="uploadParticle()">
|
||||
<mat-icon>upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.card-div {
|
||||
mat-card {
|
||||
background-color: var(--color-primary);
|
||||
color: var(--font-color);
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,6 +26,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex side-column">
|
||||
<app-particle-manager>
|
||||
|
||||
</app-particle-manager>
|
||||
<app-frames>
|
||||
<button mat-icon-button matTooltip="Copy JSON to clipboard" (click)="copyJson()">
|
||||
<mat-icon>content_copy</mat-icon>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {RenderContainerComponent} from './components/render-container/render-con
|
|||
import {ParticlesService} from '@api';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {FullSizeComponent} from '@shared-components/full-size/full-size.component';
|
||||
import {ParticleManagerComponent} from '@pages/particles/components/particle-manager/particle-manager.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-particles',
|
||||
|
|
@ -47,7 +48,8 @@ import {FullSizeComponent} from '@shared-components/full-size/full-size.componen
|
|||
FramesComponent,
|
||||
RenderContainerComponent,
|
||||
MatTooltipModule,
|
||||
FullSizeComponent
|
||||
FullSizeComponent,
|
||||
ParticleManagerComponent
|
||||
],
|
||||
templateUrl: './particles.component.html',
|
||||
styleUrl: './particles.component.scss'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {inject, Injectable} from '@angular/core';
|
||||
import {ParticleManagerService} from './particle-manager.service';
|
||||
|
||||
/**
|
||||
|
|
@ -8,15 +8,14 @@ import { ParticleManagerService } from './particle-manager.service';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class FrameManagerService {
|
||||
|
||||
constructor(private particleManager: ParticleManagerService) {}
|
||||
private readonly particleManager = inject(ParticleManagerService);
|
||||
|
||||
/**
|
||||
* Adds a new frame
|
||||
*/
|
||||
addFrame(): void {
|
||||
const frames = this.particleManager.getFrames();
|
||||
const frameId = `frame${frames.length + 1}`;
|
||||
const frameId = `frame-${frames.length}`;
|
||||
|
||||
frames.push(frameId);
|
||||
this.particleManager.setFrames(frames);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class IntersectionPlaneService {
|
|||
* Creates the intersection plane and adds it to the scene
|
||||
*/
|
||||
createIntersectionPlane(): THREE.Mesh {
|
||||
const planeGeometry = new THREE.PlaneGeometry(3, 3);
|
||||
const planeGeometry = new THREE.PlaneGeometry(5, 5);
|
||||
const planeMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x00AA00,
|
||||
transparent: true,
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ export class ParticleManagerService {
|
|||
random_offset: 0,
|
||||
stationary: true,
|
||||
frames: {
|
||||
'frame1': []
|
||||
'frame-0': []
|
||||
}
|
||||
};
|
||||
private currentFrame: string = 'frame1';
|
||||
private frames: string[] = ['frame1'];
|
||||
private currentFrame: string = 'frame-0';
|
||||
private frames: string[] = ['frame-0'];
|
||||
private selectedColor: string = '#ff0000';
|
||||
private selectedParticle: Particle = Particle.DUST;
|
||||
private selectedSize: number = 1;
|
||||
|
|
@ -301,4 +301,10 @@ export class ParticleManagerService {
|
|||
this.clearParticleVisuals();
|
||||
this.renderFrameParticles(this.currentFrame);
|
||||
}
|
||||
|
||||
public loadParticleData(data: string): void {
|
||||
this.particleData = JSON.parse(data);
|
||||
this.setCurrentFrame('frame-0');
|
||||
this.frames = Object.keys(this.particleData.frames);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ paths:
|
|||
$ref: './schemas/particles/particles.yml#/SaveFile'
|
||||
/api/files/save/{uuid}/{filename}:
|
||||
$ref: './schemas/particles/particles.yml#/SaveFileForUser'
|
||||
/api/files/download/{filename}/{secret}:
|
||||
/api/files/download/{filename}:
|
||||
$ref: './schemas/particles/particles.yml#/DownloadFile'
|
||||
/api/files/download/{uuid}/{filename}:
|
||||
$ref: './schemas/particles/particles.yml#/DownloadFileForUser'
|
||||
|
|
|
|||
|
|
@ -7,13 +7,6 @@ components:
|
|||
schema:
|
||||
type: string
|
||||
description: The name of the file
|
||||
Secret:
|
||||
name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Secret
|
||||
schemas:
|
||||
FileData:
|
||||
type: object
|
||||
|
|
@ -109,10 +102,9 @@ DownloadFile:
|
|||
tags:
|
||||
- particles
|
||||
summary: Download a file
|
||||
description: Download a file from the server using a secret key
|
||||
description: Download a file from the server
|
||||
operationId: downloadFile
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Secret'
|
||||
- $ref: '#/components/parameters/Filename'
|
||||
responses:
|
||||
'200':
|
||||
|
|
@ -140,12 +132,11 @@ DownloadFileForUser:
|
|||
tags:
|
||||
- particles
|
||||
summary: Download a file for a specific user
|
||||
description: Download a file from the server for a specific user (requires authorization)
|
||||
description: Download a file from the server for a specific user
|
||||
operationId: downloadFileForUser
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Secret'
|
||||
- $ref: '../generic/parameters.yml#/components/parameters/Uuid'
|
||||
- $ref: '#/components/parameters/Filename'
|
||||
responses:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user