From a879d1bda52376c11cce69fc373ffaf8ef36af1b Mon Sep 17 00:00:00 2001
From: liosha84 <138026690+liosha84@users.noreply.github.com>
Date: Mon, 4 Aug 2025 13:30:06 +0300
Subject: [PATCH 1/4] Update application properties for development profile and
adjust cascade type in Tutorial entity
---
.../java/com/jambotronGroup/jambotron/model/Tutorial.java | 2 +-
src/main/resources/application.properties | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
index 75a1cd3..d969305 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
@@ -27,7 +27,7 @@ public class Tutorial {
@Column(name = "tobepublished")
private boolean tobepublished;
- @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+ @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinColumn(name = "userID", nullable = false)
private User user;
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 31a9236..39634b2 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,4 +1,4 @@
#bootJar with profile prod
-spring.profiles.active=prod
+#spring.profiles.active=prod
-#spring.profiles.active=dev
+spring.profiles.active=dev
From d2f46f4405b79c7990179f4336bff25042b8ce22 Mon Sep 17 00:00:00 2001
From: liosha84 <138026690+liosha84@users.noreply.github.com>
Date: Tue, 5 Aug 2025 21:13:21 +0300
Subject: [PATCH 2/4] Add file upload functionality with dialog components and
update image handling
---
jambotron-ui/package.json | 6 --
jambotron-ui/src/app/app.module.ts | 4 +-
.../file-upload.component.html | 40 ++++++++++
.../file-upload.component.scss | 47 +++++++++++
.../file-upload.component.spec.ts | 23 ++++++
.../file-upload.component.ts | 80 +++++++++++++++++++
.../src/app/helpers/auth.interceptor.ts | 6 +-
.../app/helpers/custom-http-interceptor.ts | 5 +-
.../generate-image.component.ts | 40 +---------
.../tutorials-module/tutorials.module.ts | 4 +-
.../dialog-select-image.component.html | 4 +-
.../dialog-select-image.component.scss | 25 ++++--
.../dialog-select-image.component.ts | 13 ++-
.../dialog-upload-image.component.html | 14 ++++
.../dialog-upload-image.component.scss | 6 ++
.../dialog-upload-image.component.spec.ts | 23 ++++++
.../dialog-upload-image.component.ts | 54 +++++++++++++
.../tutorial-add.component.html | 4 +-
.../tutorial-add.component.ts | 37 +++++++--
.../modules/user-module/user-api.service.ts | 15 +++-
.../app/modules/user-module/user.module.ts | 4 +-
.../services/file-upload-api.service.spec.ts | 16 ----
.../app/services/file-upload-api.service.ts | 29 -------
.../jambotron/JambotronApplication.java | 7 +-
.../jambotron/configuretions/MvcConfig.java | 25 ------
.../configuretions/NotFoundHandler.java | 35 --------
.../controllers/FilesController.java | 42 +++++++---
.../FileUploadExceptionHandler.java | 18 +++++
.../fileUpload/FilesStorageService.java | 2 +-
.../fileUpload/FilesStorageServiceImpl.java | 17 +++-
.../fileUpload/ResponseImageUploadResult.java | 21 +++++
.../jambotron/fileUpload/ResponseMessage.java | 2 +
.../jambotron/security/WebSecurityConfig.java | 3 +
33 files changed, 471 insertions(+), 200 deletions(-)
create mode 100644 jambotron-ui/src/app/components/file-upload.component/file-upload.component.html
create mode 100644 jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss
create mode 100644 jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
create mode 100644 jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts
create mode 100644 jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html
create mode 100644 jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss
create mode 100644 jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts
create mode 100644 jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
delete mode 100644 jambotron-ui/src/app/services/file-upload-api.service.spec.ts
delete mode 100644 jambotron-ui/src/app/services/file-upload-api.service.ts
delete mode 100644 src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java
delete mode 100644 src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java
create mode 100644 src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java
create mode 100644 src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java
diff --git a/jambotron-ui/package.json b/jambotron-ui/package.json
index f976e95..4e9a8bc 100644
--- a/jambotron-ui/package.json
+++ b/jambotron-ui/package.json
@@ -24,8 +24,6 @@
"@angular/platform-server": "^20.0.0",
"@angular/router": "^20.0.0",
"@angular/ssr": "^20.0.2",
- "@ant-design/icons-angular": "19.0.0",
- "@hreimer/angular-image-viewer": "^0.14.1",
"@ng-bootstrap/ng-bootstrap": "19.0.0",
"@popperjs/core": "2.11.8",
"@primeng/themes": "^19.1.3",
@@ -33,11 +31,7 @@
"bootstrap": "5.3.6",
"cropperjs": "^2.0.1",
"express": "^5.1.0",
- "file-saver": "^2.0.5",
- "ngx-filesaver": "^20.0.0",
"ngx-markdown": "^20.0.0",
- "ngx-scrollbar": "18.0.0",
- "primeng": "^19.1.3",
"prismjs": "^1.30.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
diff --git a/jambotron-ui/src/app/app.module.ts b/jambotron-ui/src/app/app.module.ts
index 6ef61e8..fb4aa21 100644
--- a/jambotron-ui/src/app/app.module.ts
+++ b/jambotron-ui/src/app/app.module.ts
@@ -17,6 +17,7 @@ import {AppRoutingModule} from './app.routes';
import {App} from './app';
import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
+import {MatFormFieldModule} from '@angular/material/form-field';
@NgModule({
@@ -33,7 +34,8 @@ import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
AdminWelcomeComponent,
SettingsComponent,
SystemComponent,
- AngularMarkdownEditorModule.forRoot({ iconlibrary: 'fa' })
+ AngularMarkdownEditorModule.forRoot({ iconlibrary: 'fa' }),
+ MatFormFieldModule
],
providers: [
authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html
new file mode 100644
index 0000000..18201d4
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html
@@ -0,0 +1,40 @@
+
+
+@if (progress) {
+
+
+ {{ progress }}%
+
+}
+
+@if (message) {
+
+ {{ message }}
+
+}
+
+@if (fileInfo){
+
+}
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss
new file mode 100644
index 0000000..2052132
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss
@@ -0,0 +1,47 @@
+.progress-bar {
+ padding: 0;
+}
+
+.progress {
+ width: 50px;
+}
+
+#fileInput {
+ position: absolute;
+ cursor: pointer;
+ z-index: 10;
+ opacity: 0;
+ height: 100%;
+ left: 0px;
+ top: 0px;
+}
+
+.mat-toolbar-single-row {
+ height: auto !important;
+ background: transparent;
+ padding: 0;
+}
+
+.mat-toolbar-single-row button {
+ width: 100px;
+}
+
+.mat-form-field {
+ width: 100%;
+}
+
+.mat-mdc-form-field {
+ display: block;
+}
+
+.message {
+ background-color: #ddd;
+ padding: 15px;
+ color: #333;
+ border: #aaa solid 1px;
+ border-radius: 4px;
+ margin-bottom: 10px;
+}
+img{
+ max-width: -webkit-fill-available;
+}
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
new file mode 100644
index 0000000..dd2958d
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileUploadComponent } from './file-upload.component';
+
+describe('FileUploadComponent', () => {
+ let component: FileUploadComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FileUploadComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(FileUploadComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts
new file mode 100644
index 0000000..5ef375e
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts
@@ -0,0 +1,80 @@
+import {Component, EventEmitter, OnInit, Output} from '@angular/core';
+import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
+import {MatList, MatListItem} from '@angular/material/list';
+import {AsyncPipe} from '@angular/common';
+import {MatToolbar} from '@angular/material/toolbar';
+import {MatProgressBar} from '@angular/material/progress-bar';
+import {MatFormField, MatInput} from '@angular/material/input';
+import {Observable} from 'rxjs';
+import {HttpEventType, HttpResponse} from '@angular/common/http';
+import {MatButton} from '@angular/material/button';
+import {UserApiService} from '../../modules/user-module/user-api.service';
+import {FileInfo} from '../../models/file-info';
+
+@Component({
+ selector: 'app-file-upload',
+ imports: [
+ MatToolbar,
+ MatProgressBar,
+ MatFormField,
+ MatButton,
+ MatInput
+ ],
+ templateUrl: './file-upload.component.html',
+ styleUrl: './file-upload.component.scss'
+})
+export class FileUploadComponent {
+ currentFile?: File;
+ progress = 0;
+ message = '';
+
+ fileName = 'Select File';
+
+ @Output() fileInfo: FileInfo | undefined;
+ @Output() onImageUploaded = new EventEmitter();
+ constructor(private userApiService: UserApiService) {}
+
+ selectFile(event: any): void {
+ this.progress = 0;
+ this.message = '';
+
+ if (event.target.files && event.target.files[0]) {
+ const file: File = event.target.files[0];
+ this.currentFile = file;
+ this.fileName = this.currentFile.name;
+ } else {
+ this.fileName = 'Select File';
+ }
+ }
+
+ upload(): void {
+ if (this.currentFile) {
+ this.userApiService.upload(this.currentFile).subscribe({
+ next: (event: any) => {
+ if (event.type === HttpEventType.UploadProgress) {
+ this.progress = Math.round((100 * event.loaded) / event.total);
+ } else if (event instanceof HttpResponse) {
+ this.message = event.body.message;
+ this.fileInfo =event.body.fileInfo;
+ this.onImageUploaded.emit(this.fileInfo);
+ console.log(event.body);
+ }
+ },
+ error: (err: any) => {
+ console.log(err);
+ this.progress = 0;
+
+ if (err.error && err.error.message) {
+ this.message = err.error.message;
+ } else {
+ this.message = 'Could not upload the file!';
+ this.onImageUploaded.emit(undefined);
+ }
+ },
+ complete: () => {
+ this.currentFile = undefined;
+ },
+ });
+ }
+ }
+}
diff --git a/jambotron-ui/src/app/helpers/auth.interceptor.ts b/jambotron-ui/src/app/helpers/auth.interceptor.ts
index 7145b6b..7769749 100644
--- a/jambotron-ui/src/app/helpers/auth.interceptor.ts
+++ b/jambotron-ui/src/app/helpers/auth.interceptor.ts
@@ -19,9 +19,9 @@ export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest, next: HttpHandler): Observable> {
- req = req.clone({
- withCredentials: true,
- });
+ // req = req.clone({
+ // withCredentials: true,
+ // });
return next.handle(req).pipe(
catchError((error) => {
diff --git a/jambotron-ui/src/app/helpers/custom-http-interceptor.ts b/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
index df18c65..04222ec 100644
--- a/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
+++ b/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
@@ -1,4 +1,4 @@
-import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
+import {HttpEvent, HttpEventType, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable, tap} from 'rxjs';
import {SpinnerService} from '../services/spinner.service';
import {Injectable} from '@angular/core';
@@ -19,6 +19,7 @@ export class CustomHttpInterceptor implements HttpInterceptor {
}
}, (error) => {
this.spinnerService.hide();
- }));
+ }))
+ ;
}
}
diff --git a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts
index 00b772c..70df3d9 100644
--- a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts
+++ b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts
@@ -1,33 +1,23 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA, Inject, inject, Renderer2} from '@angular/core';
-import {AsyncPipe, DOCUMENT, NgOptimizedImage} from "@angular/common";
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
+import {AsyncPipe} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {MatButton, MatMiniFabButton} from "@angular/material/button";
import {
MatCard,
MatCardActions,
MatCardContent,
- MatCardHeader,
- MatCardImage,
- MatCardXlImage
+ MatCardHeader
} from "@angular/material/card";
import {MatFormField, MatInput, MatLabel} from "@angular/material/input";
import {Image} from '../../../models/image';
import {ZhipuaiImageService} from '../zhipuai-image.service';
import {SpinnerService} from '../../../services/spinner.service';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
-
-import {FileUploadService} from '../../../services/file-upload-api.service';
-import {Observable, Subscription} from 'rxjs';
-import {HttpClient, HttpResponse} from '@angular/common/http';
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
-import {AuthService} from '../../../services/auth.service';
import {TokenStorageService} from '../../../services/token-storage.service';
-import {FileInfo} from '../../../models/file-info';
import {UserApiService} from '../../user-module/user-api.service';
-import {MatProgressBar} from '@angular/material/progress-bar';
-
@Component({
selector: 'app-generate-image.component',
@@ -43,26 +33,17 @@ import {MatProgressBar} from '@angular/material/progress-bar';
MatInput,
MatLabel,
MatProgressSpinner,
- NgOptimizedImage,
- MatCardXlImage,
- MatCardImage,
MatToolbarRow,
MatToolbar,
MatIcon,
MatMiniFabButton,
MatTooltip,
- MatProgressBar
-
-
],
templateUrl: './generate-image.component.html',
styleUrl: './generate-image.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GenerateImageComponent {
-
- isSpinnerVisible = false;
-
query : string = '';
images: Image[] = [];
@@ -70,10 +51,6 @@ export class GenerateImageComponent {
url: ''
};
- currentFile?: File;
- message = '';
-
-
isLoggedIn = false;
private storageService: TokenStorageService = inject(TokenStorageService);
@@ -81,13 +58,8 @@ export class GenerateImageComponent {
constructor(
private zhipuaiImageService: ZhipuaiImageService,
public spinnerService: SpinnerService,
- private uploadService: FileUploadService,
- private userApiService:UserApiService,
-
-
+ private userApiService:UserApiService
) {
-
-
this.isLoggedIn = this.storageService.isLoggedIn();
}
@@ -108,7 +80,6 @@ export class GenerateImageComponent {
)
}
-
save(url: string | undefined) {
let requestUrl:string = url?url:"";
console.log(requestUrl);
@@ -118,9 +89,6 @@ export class GenerateImageComponent {
console.log(error);
});
}
-
-
-
}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts
index b2d60c5..5d1f0a6 100644
--- a/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts
@@ -2,13 +2,15 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {tutorialsRouting} from './tutorials.routing';
import {TutorialsComponent} from './tutorials.component/tutorials.component';
+import {MatFormFieldModule} from '@angular/material/form-field';
@NgModule({
declarations: [],
imports: [
tutorialsRouting,
TutorialsComponent,
- CommonModule
+ CommonModule,
+ MatFormFieldModule
]
})
export class TutorialsModule { }
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html
index ba7a007..4210267 100644
--- a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html
@@ -1,5 +1,5 @@
Hi
-
+
@for (fileInfo of fileInfos; track fileInfo){
@@ -16,7 +16,7 @@
-
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss
index ad0d4e1..6a02f8a 100644
--- a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss
@@ -2,20 +2,30 @@
flex: 1 1 auto;
}
-.mat-dialog-content{
+.mat-mdc-list-item{
+
+}
+a.mdc-list-item
+{
+ cursor: grab;
+ height: 120px;
+ padding-bottom: 66px;
+ background-color: rgba(24,255,255,0.04);
+}
+/*.mat-dialog-content{
min-height: 300px;
min-width: 300px;
height: 75%;
width: 75%;
-}
-.mdc-dialog--open .mat-mdc-dialog-inner-container
+}*/
+/*.mdc-dialog--open .mat-mdc-dialog-inner-container
{
opacity: 1;
width: 600px;
-}
+}*/
-.mat-mdc-dialog-container {
+/*.mat-mdc-dialog-container {
width: 600px;
height: 500px;
display: block;
@@ -25,4 +35,9 @@
min-width: inherit;
max-width: inherit;
outline: 0;
+}*/
+/*
+.mat-mdc-dialog-content{
+ overflow: auto;
}
+*/
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts
index d030b1a..3dce61e 100644
--- a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts
@@ -7,8 +7,7 @@ import {
MatDialogTitle
} from '@angular/material/dialog';
import {MatButton} from '@angular/material/button';
-import {MatListItem, MatNavList} from '@angular/material/list';
-import {RouterLink} from '@angular/router';
+import {MatListItem} from '@angular/material/list';
import {FileInfo} from '../../../models/file-info';
import {UserApiService} from '../user-api.service';
@@ -20,10 +19,7 @@ import {UserApiService} from '../user-api.service';
MatButton,
MatDialogActions,
MatDialogClose,
-
- MatListItem,
- MatNavList,
-
+ MatListItem
],
templateUrl: './dialog-select-image.component.html',
styleUrl: './dialog-select-image.component.scss'
@@ -42,10 +38,10 @@ export class DialogSelectImageComponent {
console.log(data);
});
}
+
+ //open upload dialog
upload() {
-
this.uploadClicked.emit();
-
}
onNoClick() {
@@ -54,5 +50,6 @@ export class DialogSelectImageComponent {
select(fileInfo:FileInfo) {
this.selectClicked.emit(fileInfo);
+ this.dialogRef.close();
}
}
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html
new file mode 100644
index 0000000..07b03c7
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html
@@ -0,0 +1,14 @@
+
dialog-upload-image.component works!
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss
new file mode 100644
index 0000000..827c30d
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss
@@ -0,0 +1,6 @@
+.menu-spacer {
+ flex: 1 1 auto;
+}
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts
new file mode 100644
index 0000000..7e0f85f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DialogUploadImageComponent } from './dialog-upload-image.component';
+
+describe('DialogUploadImageComponent', () => {
+ let component: DialogUploadImageComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DialogUploadImageComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(DialogUploadImageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
new file mode 100644
index 0000000..45ba8a7
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
@@ -0,0 +1,54 @@
+import {Component, EventEmitter, inject, Output, ViewChild} from '@angular/core';
+import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
+import {MatDialogActions, MatDialogClose, MatDialogContent, MatDialogRef} from '@angular/material/dialog';
+import {MatButton} from '@angular/material/button';
+import {FileInfo} from '../../../models/file-info';
+
+@Component({
+ selector: 'app-dialog-upload-image.component',
+ imports: [
+ FileUploadComponent,
+ MatDialogContent,
+ MatButton,
+ MatDialogActions,
+ MatDialogClose
+ ],
+ templateUrl: './dialog-upload-image.component.html',
+ styleUrl: './dialog-upload-image.component.scss'
+})
+export class DialogUploadImageComponent {
+ //select uploaded file
+ @Output() selectUploadedImageClicked = new EventEmitter();
+
+ //open select dialog
+ @Output() openSelectDialogClicked = new EventEmitter();
+
+ readonly dialogRef = inject(MatDialogRef);
+
+ @ViewChild('imageUpload') imageUpload: FileUploadComponent | undefined;
+
+ imageUploaded:boolean = false;
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+
+ selectUploadedImage() {
+ this.selectUploadedImageClicked.emit(this.imageUpload?.fileInfo?.url);
+ this.dialogRef.close();
+ }
+
+ openSelectImageDialog() {
+ this.openSelectDialogClicked.emit();
+ this.dialogRef.close();
+ }
+
+ imageUpload_ImageUploaded($event: FileInfo | undefined) {
+ if($event){
+ this.imageUploaded = true;
+ }
+ else{
+ this.imageUploaded = false;
+ }
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
index 6dacd62..b35dfde 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
@@ -14,9 +14,9 @@
-
-
+
+
Description
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
index 34a29e9..bfa6464 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
@@ -12,10 +12,10 @@ import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
-import {DialogLoginComponent} from '../../main-module/main.component/dialog-login.component/dialog-login.component';
-import {GlobalConstants} from '../../../global-constants';
import {MatDialog} from '@angular/material/dialog';
import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
+import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
+import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
@Component({
selector: 'app-tutorial-add.component',
@@ -51,6 +51,8 @@ export class TutorialAddComponent implements OnInit{
readonly dialog = inject(MatDialog);
+
+
markdown = `## Markdown __rulez__!
---
@@ -72,10 +74,6 @@ const language = 'typescript';
constructor(private fb: FormBuilder,
private markdownService: MarkdownService,
private userApiService: UserApiService,
- private storageService: TokenStorageService,
- private eventBusService: EventBusService,
- private router: Router,
- private authService: AuthService
) {
this.tutorial.description = this.markdown;
@@ -185,7 +183,7 @@ const language = 'typescript';
});
dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
dialogSelectRef.close();
- //this.openSignupDialog(enterAnimationDuration, exitAnimationDuration);
+ this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
})
const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
@@ -199,4 +197,29 @@ const language = 'typescript';
});
}
+
+ openUploadImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+ let dialogSelectRef = this.dialog.open(DialogUploadImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.openSelectDialogClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openSelectImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogUploadSubscription = dialogSelectRef.componentInstance.selectUploadedImageClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
}
diff --git a/jambotron-ui/src/app/modules/user-module/user-api.service.ts b/jambotron-ui/src/app/modules/user-module/user-api.service.ts
index 0675f18..8dfda62 100644
--- a/jambotron-ui/src/app/modules/user-module/user-api.service.ts
+++ b/jambotron-ui/src/app/modules/user-module/user-api.service.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import {forkJoin, Observable} from 'rxjs';
import {Tutorial} from '../../models/tutorial.model';
-import {HttpClient, HttpHeaders} from '@angular/common/http';
+import {HttpClient, HttpEvent, HttpHeaders, HttpRequest} from '@angular/common/http';
import {GlobalConstants} from '../../global-constants';
import {FileInfo} from '../../models/file-info';
@@ -15,6 +15,19 @@ export class UserApiService {
}
+ upload(file: File): Observable> {
+ const formData: FormData = new FormData();
+ formData.append('file', file);
+
+ const req = new HttpRequest('POST', `${this.baseUrl}/upload`, formData, {
+ reportProgress: true,
+ responseType: 'json'
+ });
+
+ return this.http.request(req);
+ }
+
+
saveZhipuaiImage(url: string): Observable {
return this.http.post(`${this.baseUrl}/saveZhipuAiImage`, {imageUrl:url});
}
diff --git a/jambotron-ui/src/app/modules/user-module/user.module.ts b/jambotron-ui/src/app/modules/user-module/user.module.ts
index 93853ae..a0df98e 100644
--- a/jambotron-ui/src/app/modules/user-module/user.module.ts
+++ b/jambotron-ui/src/app/modules/user-module/user.module.ts
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import {userRouting} from './user.routing';
import {UserComponent} from './user.component/user.component';
import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
+import {MatFormFieldModule} from '@angular/material/form-field';
@@ -12,7 +13,8 @@ import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
userRouting,
UserComponent,
CommonModule,
- AngularMarkdownEditorModule
+ AngularMarkdownEditorModule,
+ MatFormFieldModule
]
})
export class UserModule { }
diff --git a/jambotron-ui/src/app/services/file-upload-api.service.spec.ts b/jambotron-ui/src/app/services/file-upload-api.service.spec.ts
deleted file mode 100644
index bc34a28..0000000
--- a/jambotron-ui/src/app/services/file-upload-api.service.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { FileUploadApiService } from './file-upload-api.service';
-
-describe('FileUploadApiService', () => {
- let service: FileUploadApiService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({});
- service = TestBed.inject(FileUploadApiService);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/services/file-upload-api.service.ts b/jambotron-ui/src/app/services/file-upload-api.service.ts
deleted file mode 100644
index 06c48de..0000000
--- a/jambotron-ui/src/app/services/file-upload-api.service.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Injectable } from '@angular/core';
-import {GlobalConstants} from '../global-constants';
-import {HttpClient, HttpEvent, HttpRequest} from '@angular/common/http';
-import {Observable} from 'rxjs';
-import {FileInfo} from '../models/file-info';
-
-@Injectable({
- providedIn: 'root'
-})
-export class FileUploadService {
- private baseUrl = GlobalConstants.API_URL + '/file';
-
- constructor(private http: HttpClient) {}
-
- upload(file: File): Observable> {
- const formData: FormData = new FormData();
- formData.append('file', file);
-
- const req = new HttpRequest('POST', `${this.baseUrl}/upload`, formData, {
- responseType: 'json',
- });
-
- return this.http.request(req);
- }
-
- getFiles(): Observable {
- return this.http.get(`${this.baseUrl}/files`);
- }
-}
diff --git a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
index 413376c..369760e 100644
--- a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
+++ b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
@@ -5,15 +5,9 @@ import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-import org.slf4j.event.Level;
-import org.slf4j.helpers.BasicMarker;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Import;
-
-import java.util.Iterator;
@SpringBootApplication
public class JambotronApplication implements CommandLineRunner {
@@ -35,4 +29,5 @@ public class JambotronApplication implements CommandLineRunner {
// storageService.deleteAll();
storageService.init();
}
+
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java b/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java
deleted file mode 100644
index a637531..0000000
--- a/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.jambotronGroup.jambotron.configuretions;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-/*
-@Configuration
-
-@EnableWebMvc
-public class MvcConfig implements WebMvcConfigurer {
- @Value("${spring.resources.static-locations}")
- String resourceLocations;
-
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- //registry.addResourceHandler("/public/**").addResourceLocations(resourceLocations);
- registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/");
-
- //registry.
- registry.addResourceHandler("/media/**").addResourceLocations("resources/main/public/media/");
-
- }
-}*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java b/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java
deleted file mode 100644
index df66fd9..0000000
--- a/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.jambotronGroup.jambotron.configuretions;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.ResourceUtils;
-import org.springframework.util.StreamUtils;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.servlet.NoHandlerFoundException;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.Charset;
-/*
-@ControllerAdvice
-public class NotFoundHandler {
- @Value("${spa.default-file}")
- String defaultFile;
-
- @ExceptionHandler(NoHandlerFoundException.class)
- public ResponseEntity renderDefaultPage() {
- try {
- File indexFile = ResourceUtils.getFile(defaultFile);
- FileInputStream inputStream = new FileInputStream(indexFile);
- String body = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
- return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(body);
- } catch (IOException e) {
- e.printStackTrace();
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("There was an error completing the action.");
- }
- }
-}*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
index b47f4d8..854ae34 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
@@ -1,6 +1,7 @@
package com.jambotronGroup.jambotron.controllers;
import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
+import com.jambotronGroup.jambotron.fileUpload.ResponseImageUploadResult;
import com.jambotronGroup.jambotron.fileUpload.ResponseMessage;
import com.jambotronGroup.jambotron.model.FileInfo;
import com.jambotronGroup.jambotron.model.User;
@@ -30,17 +31,32 @@ import java.util.List;
import java.util.stream.Collectors;
@Controller
-//@CrossOrigin("http://localhost:8081")
public class FilesController {
private static final Logger logger = LoggerFactory.getLogger(FilesController.class);
-
@Autowired
AuthenticationFacade authenticationFacade;
@Autowired
FilesStorageService storageService;
+ /**
+ * Generates a URL for accessing a user's image.
+ * format: /api/user/user-images/{filename:.+}
+ *
+ * @param filename The name of the file.
+ * @return The URL to access the file.
+ */
+ private static String getUserImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getUserImage", filename).build().toString();
+ }
+
+ private static String getPublicImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getFile", filename).build().toString();
+ }
+
@PostMapping("/api/user/saveZhipuAiImage")
public ResponseEntity saveZhipuAiImage(@Valid @RequestBody SaveZhipuAiImageRequest request) {
@@ -52,7 +68,6 @@ public class FilesController {
String message = "";
-
RestTemplate restTemplate = new RestTemplate();
try {
@@ -92,14 +107,21 @@ public class FilesController {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
}
- @PostMapping("/api/file/upload")
+ @PostMapping("/api/user/upload")
public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) {
String message = "";
try {
- storageService.save(file);
+ User user = authenticationFacade.getUser();
+
+ String localFullFileName = storageService.save(user.getId().toString(), file);
+
+ FileInfo fileInfo = new FileInfo(
+ file.getOriginalFilename(),
+ FilesController.getUserImageUrl(localFullFileName));
+
message = "Uploaded the file successfully: " + file.getOriginalFilename();
- return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
+ return ResponseEntity.status(HttpStatus.OK).body(new ResponseImageUploadResult(message,fileInfo));
} catch (Exception e) {
message = "Could not upload the file: " + file.getOriginalFilename() + ". Error: " + e.getMessage();
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
@@ -113,8 +135,8 @@ public class FilesController {
List fileInfos = storageService.loadUserImages(user.getId().toString()).map(path -> {
String filename = path.getFileName().toString();
- String url = MvcUriComponentsBuilder
- .fromMethodName(FilesController.class, "getUserImage", path.getFileName().toString()).build().toString();
+
+ String url = FilesController.getUserImageUrl(filename);
return new FileInfo(filename, url);
}).collect(Collectors.toList());
@@ -127,9 +149,7 @@ public class FilesController {
public ResponseEntity> getListFiles() {
List fileInfos = storageService.loadAll().map(path -> {
String filename = path.getFileName().toString();
- String url = MvcUriComponentsBuilder
- .fromMethodName(FilesController.class, "getFile", path.getFileName().toString()).build().toString();
-
+ String url = FilesController.getPublicImageUrl(filename);
return new FileInfo(filename, url);
}).collect(Collectors.toList());
diff --git a/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java b/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java
new file mode 100644
index 0000000..9e69a0c
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java
@@ -0,0 +1,18 @@
+package com.jambotronGroup.jambotron.exceptionHandlers;
+
+import com.jambotronGroup.jambotron.fileUpload.ResponseMessage;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+//@ControllerAdvice
+//public class FileUploadExceptionHandler extends ResponseEntityExceptionHandler {
+//
+// @ExceptionHandler(MaxUploadSizeExceededException.class)
+// public ResponseEntity handleMaxSizeException(MaxUploadSizeExceededException exc) {
+// return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage("File too large!"));
+// }
+//}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
index 606f005..4acd984 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
@@ -10,7 +10,7 @@ import java.util.stream.Stream;
public interface FilesStorageService {
public void init();
- public void save(String userID,MultipartFile file);
+ public String save(String userID,MultipartFile file);
public void save(MultipartFile file);
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
index aae255c..404c847 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
@@ -56,14 +56,25 @@ public class FilesStorageServiceImpl implements FilesStorageService {
return this.rootPublic.resolve(newFilename);
}
+ /**
+ * Saves a file to a user's directory.
+ * uploads/user-images/{userID}/{filename}
+ *
+ * @param userID The ID of the user.
+ * @param file The file to save.
+ */
@Override
- public void save(String userID,MultipartFile file) {
+ public String save(String userID,MultipartFile file) {
+ Path targetPath = null;
try {
Path path = this.root.resolve(userID);
path.toFile().mkdirs(); // Ensure user directory exists
- Files.copy(file.getInputStream(), path.resolve(file.getOriginalFilename()),
+ targetPath = path.resolve(file.getOriginalFilename());
+
+ Files.copy(file.getInputStream(), targetPath,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
+
} catch (Exception e) {
if (e instanceof FileAlreadyExistsException) {
throw new RuntimeException("A file of that name already exists.");
@@ -71,6 +82,8 @@ public class FilesStorageServiceImpl implements FilesStorageService {
throw new RuntimeException(e.getMessage());
}
+
+ return targetPath.getFileName().toString();
}
@Override
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java
new file mode 100644
index 0000000..26d2f09
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java
@@ -0,0 +1,21 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+import com.jambotronGroup.jambotron.model.FileInfo;
+
+public class ResponseImageUploadResult extends ResponseMessage {
+ private FileInfo fileInfo;
+
+
+ public ResponseImageUploadResult(String message, FileInfo fileInfo) {
+ super(message);
+ this.fileInfo = fileInfo;
+ }
+
+ public FileInfo getFileInfo() {
+ return this.fileInfo;
+ }
+
+ public void setFileInfo(FileInfo fileInfo) {
+ this.fileInfo = fileInfo;
+ }
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java
index 978fb01..d10fc55 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java
@@ -3,6 +3,8 @@ package com.jambotronGroup.jambotron.fileUpload;
public class ResponseMessage {
private String message;
+
+
public ResponseMessage(String message) {
this.message = message;
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
index 19ccd31..08bda7f 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
@@ -104,6 +104,9 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
.requestMatchers("/tutorials-images/**").permitAll()
+ .requestMatchers("/api/file/files").permitAll()
+ .requestMatchers("/api/file/upload").permitAll()
+
.requestMatchers("/files/**").permitAll()
.requestMatchers("/api/auth/**").permitAll()
From d6eed2e3e18ad0752c67befde1ef0351fafc8d2c Mon Sep 17 00:00:00 2001
From: liosha84 <138026690+liosha84@users.noreply.github.com>
Date: Wed, 6 Aug 2025 03:58:16 +0300
Subject: [PATCH 3/4] Refactor image URL handling and enhance tutorial
component layout
---
.../dialog-upload-image.component.ts | 2 +-
.../tutorial-add.component.html | 107 ++++++++------
.../tutorial-add.component.scss | 18 ++-
.../tutorial-add.component.ts | 11 +-
.../tutorial-edit.component.html | 102 +++++++++++--
.../tutorial-edit.component.ts | 139 +++++++++++++++++-
.../controllers/FilesController.java | 25 +---
.../controllers/TutorialController.java | 26 +++-
.../fileUpload/FilesStorageService.java | 2 +
.../fileUpload/FilesStorageServiceImpl.java | 20 +++
.../jambotron/utils/FilesRoutingHelper.java | 25 ++++
11 files changed, 380 insertions(+), 97 deletions(-)
create mode 100644 src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
index 45ba8a7..ae2879f 100644
--- a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
@@ -34,7 +34,7 @@ export class DialogUploadImageComponent {
}
selectUploadedImage() {
- this.selectUploadedImageClicked.emit(this.imageUpload?.fileInfo?.url);
+ this.selectUploadedImageClicked.emit(this.imageUpload?.fileInfo);
this.dialogRef.close();
}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
index b35dfde..571308f 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
@@ -4,38 +4,55 @@
- Title
-
+ Title
-
- Use title image
-
-
-
-
-
-
-
Description
-
-
-
-
-
+
Title image
+
+
+
+
+
Description
+
+
+
+
+
+
+
@@ -47,26 +64,26 @@
-
-
Body
-
-
-
+
+
Body
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss
index b5cf161..1ee9028 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss
@@ -9,9 +9,7 @@
.markdown-editor-container{
display: flex;
}
-mat-card{
- margin: 20px;
-}
+
mat-card-title{
color: cyan;
}
@@ -55,3 +53,17 @@ mat-card-title{
/* display: block;
float: right;*/
}
+
+.label-style{
+ font-size: -webkit-xxx-large;
+ font-weight: bold;
+}
+
+
+.mat-mdc-card-outlined {
+ background-color: var(--mat-card-outlined-container-color, var(--mat-sys-surface));
+ border-radius:0;
+ border-width: var(--mat-card-outlined-outline-width, 1px);
+ border-color: var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));
+ box-shadow: var(--mat-card-outlined-container-elevation, var(--mat-sys-level0));
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
index bfa6464..adce58d 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
@@ -16,6 +16,7 @@ import {MatDialog} from '@angular/material/dialog';
import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
+import {MatDivider} from '@angular/material/divider';
@Component({
selector: 'app-tutorial-add.component',
@@ -33,7 +34,8 @@ import {FileUploadComponent} from '../../../components/file-upload.component/fil
MatTabGroup,
ReactiveFormsModule,
FormsModule,
- AngularMarkdownEditorModule
+ AngularMarkdownEditorModule,
+ MatDivider
],
templateUrl: './tutorial-add.component.html',
styleUrl: './tutorial-add.component.scss',
@@ -43,9 +45,9 @@ export class TutorialAddComponent implements OnInit{
tutorial: Tutorial = new Tutorial();
submitted = false;
- bsEditorInstance!: EditorInstance;
markdownText = '';
showEditor = true;
+ bsEditorInstance!: EditorInstance;
tutorialForm!: FormGroup;
editorOptions!: EditorOption;
@@ -84,6 +86,7 @@ const language = 'typescript';
this.editorOptions = {
autofocus: false,
iconlibrary: 'fa',
+ height: 300,
savable: false,
onFullscreenExit: (e) => this.hidePreview(),
onShow: (e) => this.bsEditorInstance = e,
@@ -99,9 +102,7 @@ const language = 'typescript';
isPreview: [true]
});
}
- selectImage() {
- throw new Error('Method not implemented.');
- }
+
/** highlight all code found, needs to be wrapped in timer to work properly */
highlight() {
setTimeout(() => {
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html
index 9a3eefd..fbd0be4 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html
@@ -4,23 +4,97 @@
- Title
+ Title
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ Title image
+
+
+
+
+
Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Body
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts
index 9898445..0872c20 100644
--- a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts
@@ -1,13 +1,18 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {MarkdownComponent} from 'ngx-markdown';
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
+import {MarkdownComponent, MarkdownService} from 'ngx-markdown';
import {MatButton} from '@angular/material/button';
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
import {MatError, MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
-import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {Tutorial} from '../../../models/tutorial.model';
import {UserApiService} from '../user-api.service';
import {ActivatedRoute, RouterLink} from '@angular/router';
+import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
+import {MatDivider} from '@angular/material/divider';
+import {MatDialog} from '@angular/material/dialog';
+import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
+import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
@Component({
selector: 'app-tutorial-edit.component',
@@ -27,17 +32,28 @@ import {ActivatedRoute, RouterLink} from '@angular/router';
FormsModule,
MatFormField,
RouterLink,
- MatError
+ MatError,
+ AngularMarkdownEditorModule,
+ MatDivider
],
templateUrl: './tutorial-edit.component.html',
styleUrl: './tutorial-edit.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
-export class TutorialEditComponent {
+export class TutorialEditComponent implements OnInit{
+
tutorial: Tutorial = new Tutorial();
submitted = false;
hasError = false;
errorMessage = '';
+ markdownText="";
+ bsEditorInstance!: EditorInstance;
+ tutorialForm!: FormGroup;
+ editorOptions!: EditorOption;
+
+ readonly dialog = inject(MatDialog);
+
+
markdown = `## Markdown __rulez__!
---
@@ -56,7 +72,11 @@ const language = 'typescript';
> Blockquote to the max`;
private id: string | null | undefined;
- constructor(private userApiService: UserApiService, private route: ActivatedRoute) {
+ constructor(private userApiService: UserApiService,
+ private route: ActivatedRoute,
+ private fb: FormBuilder,
+ private markdownService: MarkdownService
+ ) {
this.route.queryParams
.subscribe(params => {
@@ -71,6 +91,62 @@ const language = 'typescript';
}
);
}
+
+ ngOnInit(): void {
+ this.editorOptions = {
+ autofocus: false,
+ iconlibrary: 'fa',
+ height: 300,
+ savable: false,
+ onFullscreenExit: (e) => this.hidePreview(),
+ onShow: (e) => this.bsEditorInstance = e,
+ parser: (val) => this.parse(val)
+ };
+ this.buildForm(this.tutorial.description);
+
+ }
+
+ buildForm(markdownText: string | undefined) {
+ this.tutorialForm = this.fb.group({
+ body: [markdownText],
+ isPreview: [true]
+ });
+ }
+ /** highlight all code found, needs to be wrapped in timer to work properly */
+ highlight() {
+ setTimeout(() => {
+ this.markdownService.highlight();
+ });
+ }
+
+ hidePreview() {
+ if (this.bsEditorInstance && this.bsEditorInstance.hidePreview) {
+ this.bsEditorInstance.hidePreview();
+ }
+ }
+
+ showFullScreen(isFullScreen: boolean) {
+ if (this.bsEditorInstance && this.bsEditorInstance.setFullscreen) {
+ this.bsEditorInstance.showPreview();
+ this.bsEditorInstance.setFullscreen(isFullScreen);
+ }
+ }
+
+ parse(inputValue: string) {
+ const markedOutput = this.markdownService.parse(inputValue.trim());
+ this.highlight();
+
+ return markedOutput;
+ }
+
+ onFormChanges(): void {
+ this.tutorialForm.valueChanges.subscribe(formData => {
+ if (formData) {
+ this.markdownText = formData.body;
+ }
+ });
+ }
+
updateTutorial(): void {
this.userApiService.update(this.id, this.tutorial)
.subscribe(
@@ -87,4 +163,55 @@ const language = 'typescript';
this.hasError = true;
});
}
+
+ openSelectImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+
+ let dialogSelectRef = this.dialog.open(DialogSelectImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
+
+ openUploadImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+ let dialogSelectRef = this.dialog.open(DialogUploadImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.openSelectDialogClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openSelectImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogUploadSubscription = dialogSelectRef.componentInstance.selectUploadedImageClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
index 854ae34..1641451 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
@@ -8,6 +8,7 @@ import com.jambotronGroup.jambotron.model.User;
import com.jambotronGroup.jambotron.payload.request.SaveZhipuAiImageRequest;
import com.jambotronGroup.jambotron.security.AuthenticationFacade;
import com.jambotronGroup.jambotron.system.SystemServiceImpl;
+import com.jambotronGroup.jambotron.utils.FilesRoutingHelper;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,22 +41,6 @@ public class FilesController {
@Autowired
FilesStorageService storageService;
- /**
- * Generates a URL for accessing a user's image.
- * format: /api/user/user-images/{filename:.+}
- *
- * @param filename The name of the file.
- * @return The URL to access the file.
- */
- private static String getUserImageUrl(String filename) {
- return MvcUriComponentsBuilder
- .fromMethodName(FilesController.class, "getUserImage", filename).build().toString();
- }
-
- private static String getPublicImageUrl(String filename) {
- return MvcUriComponentsBuilder
- .fromMethodName(FilesController.class, "getFile", filename).build().toString();
- }
@PostMapping("/api/user/saveZhipuAiImage")
public ResponseEntity saveZhipuAiImage(@Valid @RequestBody SaveZhipuAiImageRequest request) {
@@ -117,8 +102,8 @@ public class FilesController {
FileInfo fileInfo = new FileInfo(
file.getOriginalFilename(),
- FilesController.getUserImageUrl(localFullFileName));
-
+ FilesRoutingHelper.getUserImageUrl(localFullFileName));
+
message = "Uploaded the file successfully: " + file.getOriginalFilename();
return ResponseEntity.status(HttpStatus.OK).body(new ResponseImageUploadResult(message,fileInfo));
@@ -136,7 +121,7 @@ public class FilesController {
List fileInfos = storageService.loadUserImages(user.getId().toString()).map(path -> {
String filename = path.getFileName().toString();
- String url = FilesController.getUserImageUrl(filename);
+ String url = FilesRoutingHelper.getUserImageUrl(filename);
return new FileInfo(filename, url);
}).collect(Collectors.toList());
@@ -149,7 +134,7 @@ public class FilesController {
public ResponseEntity> getListFiles() {
List fileInfos = storageService.loadAll().map(path -> {
String filename = path.getFileName().toString();
- String url = FilesController.getPublicImageUrl(filename);
+ String url = FilesRoutingHelper.getPublicImageUrl(filename);
return new FileInfo(filename, url);
}).collect(Collectors.toList());
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
index 99d9bf3..70f1434 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
@@ -9,6 +9,7 @@ import com.jambotronGroup.jambotron.repository.TutorialRepository;
import com.jambotronGroup.jambotron.repository.UserRepository;
import com.jambotronGroup.jambotron.security.AuthenticationFacade;
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
+import com.jambotronGroup.jambotron.utils.FilesRoutingHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -118,8 +119,7 @@ public class TutorialController {
String.format("Tutorial_%s",newFilename )
);
- String url = MvcUriComponentsBuilder
- .fromMethodName(FilesController.class, "getFile", path.getFileName().toString()).build().toString();
+ String url = FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString());
try {
@@ -160,7 +160,12 @@ public class TutorialController {
}
@PutMapping("user/tutorial-update/{id}")
- public ResponseEntity> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
+ public ResponseEntity> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial){
+
+ User user = authenticationFacade.getUser();
+
+
+
Optional tutorialData = tutorialRepository.findById(id);
Map map = new LinkedHashMap();
if (tutorialData.isPresent()) {
@@ -169,7 +174,22 @@ public class TutorialController {
servTutorial.setDescription(tutorial.getDescription());
servTutorial.setPublished(tutorial.isPublished());
servTutorial.setTobepublished(tutorial.isTobepublished());
+
try {
+
+ String imageFileName = FilesStorageServiceImpl.getFileNameFromUrl(tutorial.getTitleimage());
+
+ String servImageFileName = FilesStorageServiceImpl.getFileNameFromUrl(servTutorial.getTitleimage());
+ filesStorageService.deletePublicFile(servImageFileName);
+
+ Path path= filesStorageService.moveFile(
+ user.getId().toString(),
+ tutorial.getTitleimage(),
+ String.format("Tutorial_%s",imageFileName )
+ );
+
+ servTutorial.setTitleimage(FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString()));
+
servTutorial = tutorialRepository.save(servTutorial);
} catch (Exception e) {
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
index 4acd984..b56dd4e 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
@@ -25,4 +25,6 @@ public interface FilesStorageService {
public Stream loadUserImages(String userID);
public Path moveFile(String userID, String url, String newFilename) throws Exception;
+
+ public void deletePublicFile(String filename);
}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
index 404c847..4b4cee0 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
@@ -39,6 +39,16 @@ public class FilesStorageServiceImpl implements FilesStorageService {
return path.substring(path.lastIndexOf('/') + 1); // Extract the file name
}
+ /**
+ * Moves a file from a user's directory to the public directory with a new name.
+ * StandardCopyOption.REPLACE_EXISTING
+ *
+ * @param userID The ID of the user owning the file.
+ * @param url The URL of the file to move.
+ * @param newFilename The new name for the file in the public directory.
+ * @return The path to the moved file in the public directory.
+ * @throws Exception If the file cannot be moved.
+ */
@Override
public Path moveFile(String userID, String url, String newFilename) throws Exception {
@@ -56,6 +66,16 @@ public class FilesStorageServiceImpl implements FilesStorageService {
return this.rootPublic.resolve(newFilename);
}
+ @Override
+ public void deletePublicFile(String filename) {
+ try {
+ Path filePath = this.rootPublic.resolve(filename);
+ Files.deleteIfExists(filePath);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not delete the file: " + e.getMessage());
+ }
+ }
+
/**
* Saves a file to a user's directory.
* uploads/user-images/{userID}/{filename}
diff --git a/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java b/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java
new file mode 100644
index 0000000..49e9a66
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java
@@ -0,0 +1,25 @@
+package com.jambotronGroup.jambotron.utils;
+
+import com.jambotronGroup.jambotron.controllers.FilesController;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+
+@Component
+public class FilesRoutingHelper {
+ /**
+ * Generates a URL for accessing a user's image.
+ * format: /api/user/user-images/{filename:.+}
+ *
+ * @param filename The name of the file.
+ * @return The URL to access the file.
+ */
+ public static String getUserImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getUserImage", filename).build().toString();
+ }
+
+ public static String getPublicImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getFile", filename).build().toString();
+ }
+}
From b5bfb677d9b29219ec092b3ffe3acb3fe863b294 Mon Sep 17 00:00:00 2001
From: liosha84 <138026690+liosha84@users.noreply.github.com>
Date: Wed, 6 Aug 2025 20:26:24 +0300
Subject: [PATCH 4/4] Refactor Docker setup and update file storage paths for
improved containerization
---
src/{Docker => DevOps}/Dockerfile | 1 +
src/{Docker => DevOps}/db_password.txt | 0
src/{Docker => DevOps}/docker-compose.yml | 7 +++++--
src/DevOps/run.bat | 5 +++++
src/DevOps/run.sh | 9 +++++++++
src/{Docker => DevOps}/webapp.env | 0
.../jambotron/JambotronApplication.java | 1 +
.../controllers/TutorialController.java | 18 ++++++++++--------
.../fileUpload/FilesStorageServiceImpl.java | 9 +++++++--
9 files changed, 38 insertions(+), 12 deletions(-)
rename src/{Docker => DevOps}/Dockerfile (93%)
rename src/{Docker => DevOps}/db_password.txt (100%)
rename src/{Docker => DevOps}/docker-compose.yml (93%)
create mode 100644 src/DevOps/run.bat
create mode 100644 src/DevOps/run.sh
rename src/{Docker => DevOps}/webapp.env (100%)
diff --git a/src/Docker/Dockerfile b/src/DevOps/Dockerfile
similarity index 93%
rename from src/Docker/Dockerfile
rename to src/DevOps/Dockerfile
index 6712f0f..32cb7a8 100644
--- a/src/Docker/Dockerfile
+++ b/src/DevOps/Dockerfile
@@ -7,6 +7,7 @@
FROM openjdk:24
WORKDIR /jambotron/
+VOLUME /jambotron_data/uploads
COPY './build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
#COPY --from=BUILD_IMAGE /jambotron/build/libs/jambotron-0.0.1-SNAPSHOT.jar .
#EXPOSE 8080
diff --git a/src/Docker/db_password.txt b/src/DevOps/db_password.txt
similarity index 100%
rename from src/Docker/db_password.txt
rename to src/DevOps/db_password.txt
diff --git a/src/Docker/docker-compose.yml b/src/DevOps/docker-compose.yml
similarity index 93%
rename from src/Docker/docker-compose.yml
rename to src/DevOps/docker-compose.yml
index 500330f..687b871 100644
--- a/src/Docker/docker-compose.yml
+++ b/src/DevOps/docker-compose.yml
@@ -4,7 +4,7 @@ services:
container_name: 'jambotron-container'
build:
context: ../../
- dockerfile: ./src/Docker/Dockerfile
+ dockerfile: /Dockerfile
ports:
# - "8081:80"
- "8443:443"
@@ -12,6 +12,7 @@ services:
- postgres_jambotron
volumes:
- certs:/certs
+ - jambotron_data:/jambotron_data
# env_file: "webapp.env"
environment:
SSL_ENABLED: "true"
@@ -63,4 +64,6 @@ services:
# file: db_password.txt
volumes:
certs:
- external: true
\ No newline at end of file
+ external: true
+ jambotron_data:
+# external: true
\ No newline at end of file
diff --git a/src/DevOps/run.bat b/src/DevOps/run.bat
new file mode 100644
index 0000000..512684e
--- /dev/null
+++ b/src/DevOps/run.bat
@@ -0,0 +1,5 @@
+// investigate docker-compose.yml files for details on how to run the application
+@echo off
+//docker compose -f docker-compose.yml down
+//docker compose -f docker-compose.yml build
+//docker compose -f docker-compose.yml up -d
\ No newline at end of file
diff --git a/src/DevOps/run.sh b/src/DevOps/run.sh
new file mode 100644
index 0000000..578245a
--- /dev/null
+++ b/src/DevOps/run.sh
@@ -0,0 +1,9 @@
+#additional investigation needed to run this script
+##!/bin/bash
+#
+## stop any previously running containers
+#docker compose --env-file .env -f DevOps/docker-compose.yml down
+## build the images
+#docker compose --env-file .env -f DevOps/docker-compose.yml build
+## start the containers
+#docker compose --env-file .env -f DevOps/docker-compose.yml up -d
\ No newline at end of file
diff --git a/src/Docker/webapp.env b/src/DevOps/webapp.env
similarity index 100%
rename from src/Docker/webapp.env
rename to src/DevOps/webapp.env
diff --git a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
index 369760e..3c64e86 100644
--- a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
+++ b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
@@ -23,6 +23,7 @@ public class JambotronApplication implements CommandLineRunner {
logger.error("Application Run. This is an error message.");
logger.debug("Application Run. This is a debug message.");
logger.info("Application Run. This is an info message.");
+ logger.info("Application Run. This is an info message.!!!!!!");
}
@Override
public void run(String... arg) throws Exception {
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
index 70f1434..dd1c6d8 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
@@ -178,17 +178,19 @@ public class TutorialController {
try {
String imageFileName = FilesStorageServiceImpl.getFileNameFromUrl(tutorial.getTitleimage());
-
String servImageFileName = FilesStorageServiceImpl.getFileNameFromUrl(servTutorial.getTitleimage());
- filesStorageService.deletePublicFile(servImageFileName);
+
+ if(!servImageFileName.equals(imageFileName)){
+ filesStorageService.deletePublicFile(servImageFileName);
- Path path= filesStorageService.moveFile(
- user.getId().toString(),
- tutorial.getTitleimage(),
- String.format("Tutorial_%s",imageFileName )
- );
+ Path path= filesStorageService.moveFile(
+ user.getId().toString(),
+ tutorial.getTitleimage(),
+ String.format("Tutorial_%s",imageFileName )
+ );
- servTutorial.setTitleimage(FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString()));
+ servTutorial.setTitleimage(FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString()));
+ }
servTutorial = tutorialRepository.save(servTutorial);
} catch (Exception e) {
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
index 4b4cee0..b572440 100644
--- a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
@@ -19,9 +19,14 @@ import java.util.stream.Stream;
@Service
public class FilesStorageServiceImpl implements FilesStorageService {
- private final Path root = Paths.get("uploads/user-images/");
+// private final Path root = Paths.get("uploads/user-images/");
+//
+// private final Path rootPublic = Paths.get("uploads/public-images/");
+
+ // Update paths to use the Docker volume
+ private final Path root = Paths.get("/jambotron_data/uploads/user-images/");
+ private final Path rootPublic = Paths.get("/jambotron_data/uploads/public-images/");
- private final Path rootPublic = Paths.get("uploads/public-images/");
@Override
public void init() {