diff --git a/build/libs/jambotron-0.0.1-SNAPSHOT.jar b/build/libs/jambotron-0.0.1-SNAPSHOT.jar
index a7b3f99..06b0e5c 100644
Binary files a/build/libs/jambotron-0.0.1-SNAPSHOT.jar and b/build/libs/jambotron-0.0.1-SNAPSHOT.jar differ
diff --git a/jambotron-ui/src/app/helpers/auth.interceptor.ts b/jambotron-ui/src/app/helpers/auth.interceptor.ts
index 390b93c..7145b6b 100644
--- a/jambotron-ui/src/app/helpers/auth.interceptor.ts
+++ b/jambotron-ui/src/app/helpers/auth.interceptor.ts
@@ -28,11 +28,7 @@ export class AuthInterceptor implements HttpInterceptor {
if (
error instanceof HttpErrorResponse &&
!req.url.includes('auth/signin') &&
- (error.status === 401
- || error.status === 500
- || error.status === 0
-
- ) /// must be only 401 without 500 and 0
+ (error.status === 401) /// must be only 401 without 500 and 0
) {
return this.handle401Error(req, next);
}
diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.ts b/jambotron-ui/src/app/main-module/main.component/main.component.ts
index c36bee0..6486241 100644
--- a/jambotron-ui/src/app/main-module/main.component/main.component.ts
+++ b/jambotron-ui/src/app/main-module/main.component/main.component.ts
@@ -141,8 +141,10 @@ export class MainComponent implements OnInit{
this.storageService.clean();
//window.location.reload();
- this.router.navigate([environment.default_page]);
- window.location.reload();
+ this.router.navigate([environment.default_page]).then(() => {
+ window.location.reload();
+ });
+ //window.location.reload();
},
error: err => {
console.log(err);
@@ -175,7 +177,7 @@ export class MainComponent implements OnInit{
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
data => {
- //this.storageService.saveToken(data.token);
+
this.storageService.saveUser(data);
this.isLoggedIn = true;
@@ -191,8 +193,7 @@ export class MainComponent implements OnInit{
}
);
- // do something here with the data
- dialogSubmitSubscription.unsubscribe();
+
});
}
diff --git a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.html b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.html
index be62c28..aff86e0 100644
--- a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.html
+++ b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.html
@@ -1 +1,34 @@
-
tutorial-add.component works!
+
+
+ Add tutorial
+
+
+
+ Title
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tutorial was submitted successfully!
+
+
+
+
+
diff --git a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.scss b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.scss
index e69de29..ab4c093 100644
--- a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.scss
+++ b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.scss
@@ -0,0 +1,51 @@
+.submit-form {
+ max-width: 400px;
+ margin: auto;
+}
+
+mat-card{
+ margin: 20px;
+}
+mat-card-title{
+ color: cyan;
+}
+
+.example-form {
+ min-width: 150px;
+ max-width: 500px;
+ width: 100%;
+}
+
+
+.example-full-width {
+ width: 100%;
+}
+
+
+.variable-binding,
+.variable-textarea {
+ width: 49%;
+}
+
+.variable-textarea {
+ border-radius: 5px;
+ box-shadow: 0 1px 2px rgba(0,0,0,.07);
+ min-height: 420px;
+ padding: 8px;
+ transition: all 300ms ease-out;
+}
+
+.variable-textarea:hover {
+ box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
+ 0 2px 3px 1px rgba(0,0,0,.06);
+}
+
+.variable-binding {
+ display: block;
+ float: right;
+}
+
+.preview {
+ /* display: block;
+ float: right;*/
+}
diff --git a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.ts b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.ts
index e4e54d8..1394347 100644
--- a/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.ts
+++ b/jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.ts
@@ -1,11 +1,91 @@
-import { Component } from '@angular/core';
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {UserApiService} from '../user-api.service';
+import {TokenStorageService} from '../../services/token-storage.service';
+import {EventBusService} from '../../_shared/event-bus.service';
+import {Router} from '@angular/router';
+import {AuthService} from '../../services/auth.service';
+import {Tutorial} from '../../models/tutorial.model';
+import {MarkdownComponent} from 'ngx-markdown';
+import {MatButton} from '@angular/material/button';
+import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
+import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
+import {MatTab, MatTabGroup} from '@angular/material/tabs';
+import {NgIf} from '@angular/common';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
@Component({
selector: 'app-tutorial-add.component',
- imports: [],
+ imports: [
+ MarkdownComponent,
+ MatButton,
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader,
+ MatFormField,
+ MatInput,
+ MatLabel,
+ MatTab,
+ MatTabGroup,
+ NgIf,
+ ReactiveFormsModule,
+ FormsModule
+ ],
templateUrl: './tutorial-add.component.html',
- styleUrl: './tutorial-add.component.scss'
+ styleUrl: './tutorial-add.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class TutorialAddComponent {
+ tutorial: Tutorial = new Tutorial();
+ submitted = false
+ markdown = `## Markdown __rulez__!
+---
+
+### Syntax highlight
+\`\`\`typescript
+const language = 'typescript';
+\`\`\`
+
+### Lists
+1. Ordered list
+2. Another bullet point
+ - Unordered list
+ - Another unordered bullet
+
+### Blockquote
+> Blockquote to the max`;
+
+
+ constructor(private userApiService: UserApiService,
+ private storageService: TokenStorageService,
+ private eventBusService: EventBusService,
+ private router: Router, private authService: AuthService) {
+
+ this.tutorial.description = this.markdown;
+ }
+ saveTutorial(): void {
+ const data = {
+ title: this.tutorial.title,
+ description: this.tutorial.description
+ };
+
+ this.userApiService.create(data)
+ .subscribe(
+ response => {
+ console.log(response);
+ this.submitted = true;
+ },
+ error => {
+ console.log(error);
+ });
+ }
+ newTutorial(): void {
+ this.submitted = false;
+ this.tutorial = {
+ title: '',
+ description: '',
+ published: false
+ };
+ }
}
diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts
index 26b88a2..88f5b2d 100644
--- a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts
+++ b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts
@@ -56,8 +56,7 @@ export class TutorialsListComponent implements OnInit {
if (
(
error.status === 401
- || error.status === 500
- || error.status === 0
+
)
&& this.storageService.isLoggedIn()
) {
@@ -77,7 +76,7 @@ export class TutorialsListComponent implements OnInit {
//window.location.reload();
this.router.navigate(['main/generate-image']).then(() => {
- window.location.reload();
+ //window.location.reload();
})
},
diff --git a/jambotron-ui/src/app/user-module/user-api.service.ts b/jambotron-ui/src/app/user-module/user-api.service.ts
index 335f966..0ae9fc4 100644
--- a/jambotron-ui/src/app/user-module/user-api.service.ts
+++ b/jambotron-ui/src/app/user-module/user-api.service.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs';
import {Tutorial} from '../models/tutorial.model';
-import {HttpClient} from '@angular/common/http';
+import {HttpClient, HttpHeaders} from '@angular/common/http';
@Injectable({
providedIn: 'root'
@@ -9,11 +9,19 @@ import {HttpClient} from '@angular/common/http';
export class UserApiService {
baseUrl = 'http://localhost:8080/api/user';
+ httpOptions = {
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' } )
+ };
+
constructor(private http: HttpClient) {
}
getUserAllTutorials(): Observable {
- return this.http.get(`${this.baseUrl}/tutorials`,{withCredentials: true});
+ return this.http.get(`${this.baseUrl}/tutorials`, this.httpOptions);
+ }
+
+ create(data: any): Observable {
+ return this.http.post(`${this.baseUrl}/tutorial-add`, data);
}
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
index f0d5a4e..9f9e334 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
@@ -24,7 +24,7 @@ import java.util.Optional;
-@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600, allowCredentials="true")
+//@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600, allowCredentials="true")
@RestController
@RequestMapping("/api")
public class TutorialController {
@@ -117,7 +117,7 @@ public class TutorialController {
}
}
- @PostMapping("/tutorials")
+ @PostMapping("user/tutorial-add")
public ResponseEntity createTutorial(@RequestBody Tutorial tutorial) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
index 4fac5c5..e8c0ade 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
@@ -16,6 +16,9 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -134,7 +137,9 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
// Allow public access to tutorials (without login)
.requestMatchers("/api/public/tutorials").permitAll()
- .requestMatchers("/api/user").permitAll()
+ .requestMatchers("/api/public/tutorials/**").permitAll()
+
+ .requestMatchers("/api/user/**").permitAll()
.anyRequest().authenticated()
);
@@ -145,4 +150,16 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
return http.build();
}
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOrigin("http://localhost:4200");
+ config.addAllowedHeader("*");
+ config.addAllowedMethod("*");
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+ }
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
index 62108a2..c307684 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
@@ -25,6 +25,7 @@ public class AuthEntryPointJwt implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
+ logger.error("Unauthorized error to resource {}", request.getRequestURI());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 0babeee..6ad8c66 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,9 +1,33 @@
spring.application.name=jambotron
+#============Localhost Configurations========================
spring.datasource.url= jdbc:postgresql://localhost:5432/jambotronDB
spring.datasource.username= admin
spring.datasource.password= postgrespw
+#
+spring.flyway.baseline-on-migrate=true
+spring.flyway.validate-on-migrate=true
+
+spring.flyway.url=jdbc:postgresql://localhost:5432/jambotronDB
+spring.flyway.user=admin
+spring.flyway.password=postgrespw
+
+
+#============Koyeb Configurations========================
+
+#spring.datasource.url= jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
+#spring.datasource.username= koyeb-adm
+#spring.datasource.password= npg_HfFEUA7bay1i
+#
+#spring.flyway.baseline-on-migrate=true
+#spring.flyway.validate-on-migrate=true
+#
+#spring.flyway.url=jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
+#spring.flyway.user=koyeb-adm
+#spring.flyway.password=npg_HfFEUA7bay1i
+
+
#spring.datasource.url=${SPRING_DATASOURCE_URL}
#spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
#spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
@@ -38,13 +62,6 @@ spring.mvc.throw-exception-if-no-handler-found=true
spring.docker.compose.file=../Docker/docker-compose.yml
-spring.flyway.baseline-on-migrate=true
-spring.flyway.validate-on-migrate=true
-
-spring.flyway.user=admin
-spring.flyway.password=postgrespw
-spring.flyway.url=jdbc:postgresql://localhost:5432/jambotronDB
-
diff --git a/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql b/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql
new file mode 100644
index 0000000..e579d78
--- /dev/null
+++ b/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS public.tutorials;
+
+CREATE TABLE IF NOT EXISTS public.tutorials
+(
+ description character varying(255) COLLATE pg_catalog."default",
+ published boolean,
+ title character varying(255) COLLATE pg_catalog."default",
+ id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
+ userid bigint,
+ CONSTRAINT "tutorial_user_FK" FOREIGN KEY (userid)
+ REFERENCES public.users (id) MATCH SIMPLE
+ ON UPDATE NO ACTION
+ ON DELETE NO ACTION
+ NOT VALID
+)
+
+TABLESPACE pg_default;
+
+
+-- Index: fki_tutorial_user_FK
+
+-- DROP INDEX IF EXISTS public."fki_tutorial_user_FK";
+
+CREATE INDEX IF NOT EXISTS "fki_tutorial_user_FK"
+ ON public.tutorials USING btree
+ (userid ASC NULLS LAST)
+ TABLESPACE pg_default;
\ No newline at end of file