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/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
.requestMatchers("/api/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
.requestMatchers("/api/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
.requestMatchers("/api/files/save/**").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/history/admin/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
.requestMatchers("/api/login/userLogin/**").permitAll()
|
.requestMatchers("/api/login/userLogin/**").permitAll()
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
|
|
|
||||||
|
|
@ -34,24 +34,17 @@ public class ParticleController implements ParticlesApi {
|
||||||
private String notificationServerUrl;
|
private String notificationServerUrl;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Resource> downloadFile(String authorization, String filename) throws Exception {
|
public ResponseEntity<Resource> downloadFile(String filename) {
|
||||||
if (authorization == null || !authorization.equals(loginSecret)) {
|
|
||||||
return ResponseEntity.status(401).build();
|
|
||||||
}
|
|
||||||
File file = new File(particlesFilePath);
|
File file = new File(particlesFilePath);
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
if (!file.exists() || !file.isDirectory()) {
|
||||||
log.error("Particles file path {} is not a directory, not downloading particles file", particlesFilePath);
|
log.error("Particles file path {} is not a directory, not downloading particles file", particlesFilePath);
|
||||||
return ResponseEntity.status(404).build();
|
return ResponseEntity.status(404).build();
|
||||||
}
|
}
|
||||||
File targetFile = new File(file, filename);
|
return getFileForDownload(file, filename);
|
||||||
return getFileForDownload(targetFile, filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Resource> downloadFileForUser(String authorization, String uuid, String filename) throws Exception {
|
public ResponseEntity<Resource> downloadFileForUser(String uuid, String filename) {
|
||||||
if (authorization == null || !authorization.equals(loginSecret)) {
|
|
||||||
return ResponseEntity.status(401).build();
|
|
||||||
}
|
|
||||||
File file = new File(particlesFilePath);
|
File file = new File(particlesFilePath);
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
if (!file.exists() || !file.isDirectory()) {
|
||||||
log.error("Particles file path {} is not a directory, not downloading particles user file", particlesFilePath);
|
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) {
|
private ResponseEntity<Resource> getFileForDownload(File file, String filename) {
|
||||||
|
filename += ".json";
|
||||||
File targetFile = new File(file, filename);
|
File targetFile = new File(file, filename);
|
||||||
if (!targetFile.exists()) {
|
if (!targetFile.exists()) {
|
||||||
log.warn("Particles file {} does not exist", targetFile.getAbsolutePath());
|
log.warn("Particles file {} does not exist", targetFile.getAbsolutePath());
|
||||||
|
|
@ -95,7 +89,7 @@ public class ParticleController implements ParticlesApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> saveFile(String filename, MultipartFile content) throws Exception {
|
public ResponseEntity<Void> saveFile(String filename, MultipartFile content) {
|
||||||
File file = new File(particlesFilePath);
|
File file = new File(particlesFilePath);
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
if (!file.exists() || !file.isDirectory()) {
|
||||||
log.error("Particles file path {} is not a directory, not saving particles file", particlesFilePath);
|
log.error("Particles file path {} is not a directory, not saving particles file", particlesFilePath);
|
||||||
|
|
@ -107,7 +101,7 @@ public class ParticleController implements ParticlesApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
File file = new File(particlesFilePath);
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
if (!file.exists() || !file.isDirectory()) {
|
||||||
log.error("Particles file path {} is not a directory, not saving particles user file", particlesFilePath);
|
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) {
|
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));
|
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) {
|
private ResponseEntity<Void> writeContentToFile(File dir, String filename, MultipartFile content) {
|
||||||
|
filename += ".json";
|
||||||
File targetFile = new File(dir, filename);
|
File targetFile = new File(dir, filename);
|
||||||
if (!Files.isWritable(targetFile.toPath())) {
|
if (!Files.isWritable(targetFile.toPath())) {
|
||||||
log.error("Particles file {} is not writable", targetFile.getAbsolutePath());
|
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>
|
</div>
|
||||||
<div class="flex side-column">
|
<div class="flex side-column">
|
||||||
|
<app-particle-manager>
|
||||||
|
|
||||||
|
</app-particle-manager>
|
||||||
<app-frames>
|
<app-frames>
|
||||||
<button mat-icon-button matTooltip="Copy JSON to clipboard" (click)="copyJson()">
|
<button mat-icon-button matTooltip="Copy JSON to clipboard" (click)="copyJson()">
|
||||||
<mat-icon>content_copy</mat-icon>
|
<mat-icon>content_copy</mat-icon>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {RenderContainerComponent} from './components/render-container/render-con
|
||||||
import {ParticlesService} from '@api';
|
import {ParticlesService} from '@api';
|
||||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||||
import {FullSizeComponent} from '@shared-components/full-size/full-size.component';
|
import {FullSizeComponent} from '@shared-components/full-size/full-size.component';
|
||||||
|
import {ParticleManagerComponent} from '@pages/particles/components/particle-manager/particle-manager.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-particles',
|
selector: 'app-particles',
|
||||||
|
|
@ -47,7 +48,8 @@ import {FullSizeComponent} from '@shared-components/full-size/full-size.componen
|
||||||
FramesComponent,
|
FramesComponent,
|
||||||
RenderContainerComponent,
|
RenderContainerComponent,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
FullSizeComponent
|
FullSizeComponent,
|
||||||
|
ParticleManagerComponent
|
||||||
],
|
],
|
||||||
templateUrl: './particles.component.html',
|
templateUrl: './particles.component.html',
|
||||||
styleUrl: './particles.component.scss'
|
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';
|
import {ParticleManagerService} from './particle-manager.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -8,15 +8,14 @@ import { ParticleManagerService } from './particle-manager.service';
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class FrameManagerService {
|
export class FrameManagerService {
|
||||||
|
private readonly particleManager = inject(ParticleManagerService);
|
||||||
constructor(private particleManager: ParticleManagerService) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new frame
|
* Adds a new frame
|
||||||
*/
|
*/
|
||||||
addFrame(): void {
|
addFrame(): void {
|
||||||
const frames = this.particleManager.getFrames();
|
const frames = this.particleManager.getFrames();
|
||||||
const frameId = `frame${frames.length + 1}`;
|
const frameId = `frame-${frames.length}`;
|
||||||
|
|
||||||
frames.push(frameId);
|
frames.push(frameId);
|
||||||
this.particleManager.setFrames(frames);
|
this.particleManager.setFrames(frames);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export class IntersectionPlaneService {
|
||||||
* Creates the intersection plane and adds it to the scene
|
* Creates the intersection plane and adds it to the scene
|
||||||
*/
|
*/
|
||||||
createIntersectionPlane(): THREE.Mesh {
|
createIntersectionPlane(): THREE.Mesh {
|
||||||
const planeGeometry = new THREE.PlaneGeometry(3, 3);
|
const planeGeometry = new THREE.PlaneGeometry(5, 5);
|
||||||
const planeMaterial = new THREE.MeshBasicMaterial({
|
const planeMaterial = new THREE.MeshBasicMaterial({
|
||||||
color: 0x00AA00,
|
color: 0x00AA00,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ export class ParticleManagerService {
|
||||||
random_offset: 0,
|
random_offset: 0,
|
||||||
stationary: true,
|
stationary: true,
|
||||||
frames: {
|
frames: {
|
||||||
'frame1': []
|
'frame-0': []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private currentFrame: string = 'frame1';
|
private currentFrame: string = 'frame-0';
|
||||||
private frames: string[] = ['frame1'];
|
private frames: string[] = ['frame-0'];
|
||||||
private selectedColor: string = '#ff0000';
|
private selectedColor: string = '#ff0000';
|
||||||
private selectedParticle: Particle = Particle.DUST;
|
private selectedParticle: Particle = Particle.DUST;
|
||||||
private selectedSize: number = 1;
|
private selectedSize: number = 1;
|
||||||
|
|
@ -301,4 +301,10 @@ export class ParticleManagerService {
|
||||||
this.clearParticleVisuals();
|
this.clearParticleVisuals();
|
||||||
this.renderFrameParticles(this.currentFrame);
|
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'
|
$ref: './schemas/particles/particles.yml#/SaveFile'
|
||||||
/api/files/save/{uuid}/{filename}:
|
/api/files/save/{uuid}/{filename}:
|
||||||
$ref: './schemas/particles/particles.yml#/SaveFileForUser'
|
$ref: './schemas/particles/particles.yml#/SaveFileForUser'
|
||||||
/api/files/download/{filename}/{secret}:
|
/api/files/download/{filename}:
|
||||||
$ref: './schemas/particles/particles.yml#/DownloadFile'
|
$ref: './schemas/particles/particles.yml#/DownloadFile'
|
||||||
/api/files/download/{uuid}/{filename}:
|
/api/files/download/{uuid}/{filename}:
|
||||||
$ref: './schemas/particles/particles.yml#/DownloadFileForUser'
|
$ref: './schemas/particles/particles.yml#/DownloadFileForUser'
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,6 @@ components:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
description: The name of the file
|
description: The name of the file
|
||||||
Secret:
|
|
||||||
name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Secret
|
|
||||||
schemas:
|
schemas:
|
||||||
FileData:
|
FileData:
|
||||||
type: object
|
type: object
|
||||||
|
|
@ -109,10 +102,9 @@ DownloadFile:
|
||||||
tags:
|
tags:
|
||||||
- particles
|
- particles
|
||||||
summary: Download a file
|
summary: Download a file
|
||||||
description: Download a file from the server using a secret key
|
description: Download a file from the server
|
||||||
operationId: downloadFile
|
operationId: downloadFile
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/Secret'
|
|
||||||
- $ref: '#/components/parameters/Filename'
|
- $ref: '#/components/parameters/Filename'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
|
|
@ -140,12 +132,11 @@ DownloadFileForUser:
|
||||||
tags:
|
tags:
|
||||||
- particles
|
- particles
|
||||||
summary: Download a file for a specific user
|
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
|
operationId: downloadFileForUser
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/Secret'
|
|
||||||
- $ref: '../generic/parameters.yml#/components/parameters/Uuid'
|
- $ref: '../generic/parameters.yml#/components/parameters/Uuid'
|
||||||
- $ref: '#/components/parameters/Filename'
|
- $ref: '#/components/parameters/Filename'
|
||||||
responses:
|
responses:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user