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