diff --git a/HELP.md b/HELP.md index 75481ee..47eb320 100644 --- a/HELP.md +++ b/HELP.md @@ -108,6 +108,42 @@ docker-compose -f src/Docker/docker-compose.yml up -d ``` - For connection from pgadmin use hostename: **host.docker.internal** + +# How to run the application in Docker with HTTPS support + +To run the application in Docker with HTTPS support, follow these steps: + +1. **Cerbot** + + - Run this command below to get certificate and store it to volume certs. + + - Expanded(readable) command example: + + docker run -d \ + --name certbot \ + -v certs:/etc/letsencrypt \ + -v certs-data:/var/lib/letsencrypt \ + -p 80:80 \ + -p 443:443 \ + certbot/certbot \ + certonly --standalone --preferred-challenges http --email youremail@gmail.com -d yourdomain.com --agree-tos + + - Command to run certbot with specific domain and email: + ```bash + docker run -d --name certbot -v certs:/etc/letsencrypt -v certs-data:/var/lib/letsencrypt -p 8081:80 -p 8443:443 certbot/certbot certonly --standalone --preferred-challenges http --email liosha84@gmail.com -d jambotron.run.place --agree-tos + ``` + - Tip: You can generate certificate to local machine used next command: + ```bash + docker run -it --rm -p 8081:80 --name certbot -v "C:\certbot\etc\letsencrypt:/etc/letsencrypt" -v "C:\certbot\var\lib\letsencrypt:/var/lib/letsencrypt" certbot/certbot certonly --standalone -d jambotron.run.place + ``` + certificate files will be stored in `C:\certbot\etc\letsencrypt` and `C:\certbot\var\lib\letsencrypt` directories. + + - If you have same trouble before running certbot, run this project + `Helper_projects/spring-boot-https-main` insted of jambotron project. + + + - TODO: Add cron job to renew certificate automatically. + ### Reference Documentation For further reference, please consider the following sections: diff --git a/Helper_projects/spring-boot-https-main/.gitignore b/Helper_projects/spring-boot-https-main/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/Helper_projects/spring-boot-https-main/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/Helper_projects/spring-boot-https-main/Dockerfile b/Helper_projects/spring-boot-https-main/Dockerfile new file mode 100644 index 0000000..25cbb22 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/Dockerfile @@ -0,0 +1,10 @@ +FROM openjdk:20 as build +WORKDIR /app +COPY . ./ +RUN microdnf install findutils +RUN ./gradlew build -x test + +FROM openjdk:20-jdk-slim +WORKDIR /app +COPY --from=build /app/build/libs/spring-boot-https-0.0.1.jar . +CMD ["java", "-jar", "spring-boot-https-0.0.1.jar"] \ No newline at end of file diff --git a/Helper_projects/spring-boot-https-main/README.md b/Helper_projects/spring-boot-https-main/README.md new file mode 100644 index 0000000..02cca74 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/README.md @@ -0,0 +1 @@ +https://dev.to/beksultandev/how-to-add-https-support-to-your-spring-boot-app-2h53 \ No newline at end of file diff --git a/Helper_projects/spring-boot-https-main/build.gradle b/Helper_projects/spring-boot-https-main/build.gradle new file mode 100644 index 0000000..9f60ac2 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.5' + id 'io.spring.dependency-management' version '1.1.3' +} + +group = 'dev.beksultan' +version = '0.0.1' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('bootBuildImage') { + builder = 'paketobuildpacks/builder-jammy-base:latest' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/Helper_projects/spring-boot-https-main/compose.yml b/Helper_projects/spring-boot-https-main/compose.yml new file mode 100644 index 0000000..64e3042 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/compose.yml @@ -0,0 +1,17 @@ +services: + spring-boot-https: + container_name: spring-boot-https + image: beksultancs/spring-boot-https:1 + restart: unless-stopped + ports: + - "8081:80" + - "8443:443" + volumes: + - certs:/certs + environment: + - SERVER_PORT=443 + - FULLCHAINPEM=/certs/live/jambotron.run.place/fullchain.pem + - PRIVKEYPEM=/certs/live/jambotron.run.place/privkey.pem +volumes: + certs: + external: true diff --git a/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7f93135 Binary files /dev/null and b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3fa8f86 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Helper_projects/spring-boot-https-main/gradlew b/Helper_projects/spring-boot-https-main/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Helper_projects/spring-boot-https-main/gradlew.bat b/Helper_projects/spring-boot-https-main/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Helper_projects/spring-boot-https-main/settings.gradle b/Helper_projects/spring-boot-https-main/settings.gradle new file mode 100644 index 0000000..66302e0 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-boot-https' diff --git a/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java b/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java new file mode 100644 index 0000000..1bec660 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java @@ -0,0 +1,20 @@ +package https; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class SpringBootHttpsApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootHttpsApplication.class, args); + } + + @GetMapping("/hello") + public String hello() { + return "Hello World! 🚀"; + } +} diff --git a/Helper_projects/spring-boot-https-main/src/main/resources/application.properties b/Helper_projects/spring-boot-https-main/src/main/resources/application.properties new file mode 100644 index 0000000..da29fa2 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/src/main/resources/application.properties @@ -0,0 +1,3 @@ +server.ssl.enabled=true +server.ssl.certificate=${FULLCHAINPEM} +server.ssl.certificate-private-key=${PRIVKEYPEM} \ No newline at end of file diff --git a/Helper_projects/spring-boot-https-main/src/main/resources/application.yml b/Helper_projects/spring-boot-https-main/src/main/resources/application.yml new file mode 100644 index 0000000..ad1eab0 --- /dev/null +++ b/Helper_projects/spring-boot-https-main/src/main/resources/application.yml @@ -0,0 +1,5 @@ +server: + ssl: + enabled: true + certificate: ${FULLCHAINPEM} + certificate-private-key: ${PRIVKEYPEM} diff --git a/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java b/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java new file mode 100644 index 0000000..515002c --- /dev/null +++ b/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java @@ -0,0 +1,13 @@ +package https; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringBootHttpsApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/build.gradle b/build.gradle index f6dc56e..6eda973 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ java { } repositories { + mavenCentral() } @@ -37,6 +38,9 @@ dependencies { implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter:1.0.0-M6' + + + //implementation 'org.springframework.ai:spring-ai-starter-model-zhipuai' //implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter' //implementation platform("org.springframework.ai:spring-ai-bom:1.0.0-SNAPSHOT") @@ -94,8 +98,52 @@ apply plugin: 'java' } }*/ +tasks.register('buildAngular_dev', Exec) { + //dependsOn deleteStaticFolder + workingDir './jambotron-ui' // Path to your Angular project (e.g., './angular-app') + executable 'npm.cmd' // Use 'npm' for Unix-like systems or 'npm.cmd' for Windows + + // Your Angular build command + args = ['run', 'build_dev']//do not forget build_dev used port 8080 for connect to backend rest api + +} + +tasks.register('buildAngular', Exec) { + //dependsOn deleteStaticFolder + workingDir './jambotron-ui' // Path to your Angular project (e.g., './angular-app') + executable 'npm.cmd' // Use 'npm' for Unix-like systems or 'npm.cmd' for Windows + + // Your Angular build command + args = ['run', 'build']//do not forget build_dev used port 8080 for connect to backend rest api + +} + + +tasks.register('deleteStaticFolder', Delete) { + dependsOn buildAngular + def dirName = "src/main/resources/static" + file(dirName).list().each { + f -> + delete "${dirName}/${f}" + } +} + +tasks.register('deleteStaticFolder_dev', Delete) { + dependsOn buildAngular_dev + def dirName = "src/main/resources/static" + file(dirName).list().each { + f -> + delete "${dirName}/${f}" + } +} + tasks.register('copyAngularBuild', Copy) { - //dependsOn buildAngular + dependsOn deleteStaticFolder + from 'jambotron-ui/dist/jambotron-ui' // Path to your Angular dist folder + into "src/main/resources/static" // Path INSIDE the WAR +} +tasks.register('copyAngularBuild_dev', Copy) { + dependsOn deleteStaticFolder_dev from 'jambotron-ui/dist/jambotron-ui' // Path to your Angular dist folder into "src/main/resources/static" // Path INSIDE the WAR } diff --git a/build/libs/jambotron-0.0.1-SNAPSHOT.jar b/build/libs/jambotron-0.0.1-SNAPSHOT.jar deleted file mode 100644 index e52fd4e..0000000 Binary files a/build/libs/jambotron-0.0.1-SNAPSHOT.jar and /dev/null differ diff --git a/jambotron-ui/angular.json b/jambotron-ui/angular.json index ba195ca..cd85683 100644 --- a/jambotron-ui/angular.json +++ b/jambotron-ui/angular.json @@ -20,7 +20,6 @@ "outputPath": "dist/jambotron-ui", "index": "src/index.html", "main": "src/main.ts", - "polyfills": [ "zone.js" ], @@ -48,6 +47,12 @@ }, "configurations": { "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], "budgets": [ { "type": "initial", @@ -78,6 +83,7 @@ }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { "production": { "buildTarget": "jambotron-ui:build:production" diff --git a/jambotron-ui/package.json b/jambotron-ui/package.json index 27d5ca8..d22578b 100644 --- a/jambotron-ui/package.json +++ b/jambotron-ui/package.json @@ -4,8 +4,10 @@ "scripts": { "ng": "ng", "start": "ng serve", + "start_prod": "ng serve --configuration production", "build": "ng build", "watch": "ng build --watch --configuration development", + "build_dev":"ng build --configuration development", "test": "ng test", "serve:ssr:jambotron-ui": "node dist/jambotron-ui/server/server.mjs" }, diff --git a/jambotron-ui/src/app/admin-module/admin-module.service.spec.ts b/jambotron-ui/src/app/admin-module/admin-module.service.spec.ts new file mode 100644 index 0000000..1dfcd33 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/admin-module.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AdminModuleService } from './admin-module.service'; + +describe('AdminModuleService', () => { + let service: AdminModuleService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AdminModuleService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/admin-module/admin-module.service.ts b/jambotron-ui/src/app/admin-module/admin-module.service.ts new file mode 100644 index 0000000..3776756 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/admin-module.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import {Observable} from 'rxjs'; +import {HttpClient} from '@angular/common/http'; +import {User} from '../models/user.model'; +import {GlobalConstants} from '../global-constants'; + +@Injectable({ + providedIn: 'root' +}) +export class AdminModuleService { + baseUrl = GlobalConstants.API_URL; + + constructor(private http: HttpClient) { + +} + + updateUserRoles(id: any,data: User): Observable { + return this.http.put(`${this.baseUrl}/users/${id}`, data); + } +} diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.html b/jambotron-ui/src/app/admin-module/admin.component/admin.component.html index 3876019..a2adc38 100644 --- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.html +++ b/jambotron-ui/src/app/admin-module/admin.component/admin.component.html @@ -1,28 +1,13 @@ - - - - + +
-
-
-
+ -
-
+ -
-
-
-
-
-
+
diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss b/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss index 3cde704..9a8a04f 100644 --- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss +++ b/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss @@ -1,22 +1,16 @@ -.example-container { - width: 500px; - height: 300px; - border: 1px solid rgba(0, 0, 0, 0.5); -} - -.example-sidenav-content { - display: flex; - height: 100%; - align-items: center; - justify-content: center; -} - -.example-sidenav { - padding: 20px; -} - +//--------------- .pc-sidebar{ - top: 120px; + top: 65px; + overflow-y: auto; + background-color: rgba(153, 153, 153, 0.16); + + backdrop-filter: blur(8px); +} +.pc-container{ + top: 0px; + padding-left: 5px; + padding-right: 5px; } + diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts b/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts index c7496dc..7b2330a 100644 --- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts +++ b/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts @@ -9,6 +9,7 @@ import {MatButton} from '@angular/material/button'; import { ScrollPanelModule } from 'primeng/scrollpanel'; import {MenuItem} from 'primeng/api'; import {PanelMenu} from 'primeng/panelmenu'; +import {SideBarAdminComponent} from '../side-bar-admin.component/side-bar-admin.component'; @Component({ selector: 'app-admin.component', imports: [ @@ -24,6 +25,7 @@ import {PanelMenu} from 'primeng/panelmenu'; RouterLink, PanelMenu, NgStyle, + SideBarAdminComponent, ], schemas:[CUSTOM_ELEMENTS_SCHEMA], diff --git a/jambotron-ui/src/app/admin-module/admin.routing.ts b/jambotron-ui/src/app/admin-module/admin.routing.ts index 246ca73..eb1ff4f 100644 --- a/jambotron-ui/src/app/admin-module/admin.routing.ts +++ b/jambotron-ui/src/app/admin-module/admin.routing.ts @@ -8,7 +8,7 @@ const ADMIN_ROUTES: Routes = [ children: [ { - path: 'welcome', + path: 'admin-welcome', loadComponent: () => import('../admin-module/admin-welcome.component/admin-welcome.component').then((c) => c.AdminWelcomeComponent), }, { @@ -17,9 +17,12 @@ const ADMIN_ROUTES: Routes = [ }, { path: 'system', - loadComponent: () => import('../admin-module/system.component/system.component').then((c) => c.SystemComponent) - } + }, + { + path: 'users', + loadComponent: () => import('../admin-module/users.component/users.component').then((c) => c.UsersComponent) + } ] } ]; diff --git a/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.html b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.html new file mode 100644 index 0000000..3d40615 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.html @@ -0,0 +1,35 @@ + + + + house + @if (!isCollapsed) { + Dashboard + } + + + + + newspaper + @if (!isCollapsed) { + Users + } + + + + + newspaper + @if (!isCollapsed) { + Settings + } + + + + + newspaper + @if (!isCollapsed) { + System + } + + + + diff --git a/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.scss b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.scss new file mode 100644 index 0000000..62a5b67 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.scss @@ -0,0 +1,14 @@ +.entry{ + display: flex; + align-items: center; + gap: 1rem; + padding:0.75rem; + color: rgba(24, 255, 255, 0.96); + +} + +a.mdc-list-item +{ + + background-color: rgba(24,255,255,0.04); +} diff --git a/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts new file mode 100644 index 0000000..b6f9f21 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SideBarAdminComponent } from './side-bar-admin.component'; + +describe('SideBarAdminComponent', () => { + let component: SideBarAdminComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SideBarAdminComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SideBarAdminComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.ts b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.ts new file mode 100644 index 0000000..0940b6f --- /dev/null +++ b/jambotron-ui/src/app/admin-module/side-bar-admin.component/side-bar-admin.component.ts @@ -0,0 +1,20 @@ +import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {MatListItem, MatNavList} from '@angular/material/list'; +import {RouterLink} from '@angular/router'; +import {MatIcon} from '@angular/material/icon'; + +@Component({ + selector: 'app-side-bar-admin', + imports: [ + MatIcon, + MatListItem, + MatNavList, + RouterLink + ], + templateUrl: './side-bar-admin.component.html', + styleUrl: './side-bar-admin.component.scss', + schemas:[CUSTOM_ELEMENTS_SCHEMA] +}) +export class SideBarAdminComponent { + isCollapsed = false; +} diff --git a/jambotron-ui/src/app/admin-module/users.component/users.component.html b/jambotron-ui/src/app/admin-module/users.component/users.component.html new file mode 100644 index 0000000..4634e66 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/users.component/users.component.html @@ -0,0 +1,56 @@ + + @for (column of columnsSchema; track column){ + + + + + } + + +
+ {{column.label}} + + @switch (column.type) { + @case ('list') { + + + @for (role of element.roles; track $index) { + + {{role.name}} + + + cancel + + + + } + + + + @for (role of filteredRoles(element.roles); track role) { + {{role.name}} + } + + + } + @default { + {{ element[column.key] }} + } + } +
diff --git a/jambotron-ui/src/app/admin-module/users.component/users.component.scss b/jambotron-ui/src/app/admin-module/users.component/users.component.scss new file mode 100644 index 0000000..fecda9c --- /dev/null +++ b/jambotron-ui/src/app/admin-module/users.component/users.component.scss @@ -0,0 +1,3 @@ +.example-chip-list { + width: 100%; +} diff --git a/jambotron-ui/src/app/admin-module/users.component/users.component.spec.ts b/jambotron-ui/src/app/admin-module/users.component/users.component.spec.ts new file mode 100644 index 0000000..241dbac --- /dev/null +++ b/jambotron-ui/src/app/admin-module/users.component/users.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UsersComponent } from './users.component'; + +describe('UsersComponent', () => { + let component: UsersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UsersComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UsersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/admin-module/users.component/users.component.ts b/jambotron-ui/src/app/admin-module/users.component/users.component.ts new file mode 100644 index 0000000..5c45ed2 --- /dev/null +++ b/jambotron-ui/src/app/admin-module/users.component/users.component.ts @@ -0,0 +1,185 @@ +import {ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, model} from '@angular/core'; +import {UserService} from '../../services/user.service'; +import {RolesService} from '../../services/roles.service'; +import {Role} from '../../models/role'; +import {User} from '../../models/user.model'; +import { + MatCell, + MatCellDef, MatColumnDef, + MatHeaderCell, MatHeaderCellDef, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, MatTable +} from '@angular/material/table'; +import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import { + MatAutocomplete, + MatAutocompleteSelectedEvent, + MatAutocompleteTrigger, + MatOption +} from '@angular/material/autocomplete'; +import { + MatChip, + MatChipGrid, + MatChipInput, + MatChipInputEvent, + MatChipRow, + MatChipsModule +} from '@angular/material/chips'; +import {MatIcon} from '@angular/material/icon'; +import {COMMA, ENTER} from '@angular/cdk/keycodes'; +import {MatFormField} from '@angular/material/input'; +import {MatSelect, MatSelectTrigger} from '@angular/material/select'; +import {AdminModuleService} from '../admin-module.service'; + +@Component({ + selector: 'app-users.component', + imports: [ + MatCell, + MatCellDef, + MatHeaderCell, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, + MatTable, + MatColumnDef, + MatHeaderCellDef, + FormsModule, + ReactiveFormsModule, + MatChipGrid, + MatChipRow, + MatIcon, + MatAutocompleteTrigger, + MatChipInput, + MatAutocomplete, + MatOption, + MatFormField, + MatSelect, + MatSelectTrigger, + MatChip, + MatChipsModule + ], + templateUrl: './users.component.html', + styleUrl: './users.component.scss', + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class UsersComponent { + + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; + allRoles:Role[] = []; + users: User[] = []; + + displayedColumns: string[] = UserColumns.map((col) => col.key) + columnsSchema: any = UserColumns + + readonly currentRole = model(''); + + rolesControl = new FormControl([]); + protected ac = new FormControl(''); + + constructor(private userService: UserService, + private roleService: RolesService, + private adminService:AdminModuleService) { + this.getAllRoles(); + this.getUsers(); + } + + getAllRoles():any{ + this.roleService.getAllRoles().subscribe( + (data : any) => { + this.allRoles = data; + console.log(data); + }, + (err : any)=> { + this.allRoles = JSON.parse(err.error).message; + } + ); + } + + getUsers(){ + this.userService.getAdminBoard().subscribe( + (data : any) => { + this.users = data; + console.log(data); + }, + (err : any)=> { + console.log(JSON.parse(err.error).message); + } + ); + } + + filteredRoles(roles : Role[]):any { + return this.allRoles.filter( + (r:Role) => !roles.some((item) => item.id === r.id), + ); + } + + change($event: Event, roles: Role[]) { + roles.filter( + (r:Role) => !roles.some((item) => item.name?.toLowerCase() === r.name?.toLowerCase()), + ) + } + + selected(user: User, $event: MatAutocompleteSelectedEvent) { + user.roles.push($event.option.value); + this.saveUserRoles(user); + this.currentRole.set(''); + $event.option.deselect(); + } + + add($event: MatChipInputEvent, user:User) { + /*const value = ($event.value || '').trim(); + + const role = this.allRoles.find(value1 => value1.name === value); + if(role){ + user.roles.push(value); + this.saveUserRoles(user); + }*/ + // Clear the input value + this.currentRole.set(''); + } + + remove(role: any, user: User) { + const updatedRoles: Role[] = user.roles.filter( + (r: Role) => r.id !== role.id // Simple comparison with the role to remove + ); + + if (updatedRoles && updatedRoles.length >= 1) { + user.roles = updatedRoles; + this.saveUserRoles(user); + } + } + + saveUserRoles(user:User){ + this.adminService.updateUserRoles(user.id, user).subscribe( + (data:any) => { + console.log(data); + }, + (err:any) => { + console.log(err); + } + ) + } +} + +export const UserColumns = [ + { + key: 'username', + type: 'text', + label: 'Name', + required: true, + }, + { + key: 'email', + type: 'text', + label: 'Email', + }, + { + key: 'roles', + type: 'list', + label: 'Roles', + required: true, + } +]; diff --git a/jambotron-ui/src/app/app.routes.ts b/jambotron-ui/src/app/app.routes.ts index a1a9723..8e98b95 100644 --- a/jambotron-ui/src/app/app.routes.ts +++ b/jambotron-ui/src/app/app.routes.ts @@ -17,7 +17,7 @@ export const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forRoot(routes)],//, { useHash: true } + imports: [RouterModule.forRoot(routes, { useHash: true}) ], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/jambotron-ui/src/app/components/board-admin/board-admin.component.ts b/jambotron-ui/src/app/components/board-admin/board-admin.component.ts index 1fc3f98..b2471d6 100644 --- a/jambotron-ui/src/app/components/board-admin/board-admin.component.ts +++ b/jambotron-ui/src/app/components/board-admin/board-admin.component.ts @@ -80,7 +80,6 @@ export class BoardAdminComponent implements OnInit { return this.allRoles.filter( (r:Role) => !roles.some((item) => item.id === r.id), ); - } // private _filter(value: string): string[] { @@ -111,7 +110,7 @@ export class BoardAdminComponent implements OnInit { // this.announcer.announce(`Removed ${fruit}`); // return [...fruits]; - } + } diff --git a/jambotron-ui/src/app/global-constants.spec.ts b/jambotron-ui/src/app/global-constants.spec.ts new file mode 100644 index 0000000..e3b64b4 --- /dev/null +++ b/jambotron-ui/src/app/global-constants.spec.ts @@ -0,0 +1,7 @@ +import { GlobalConstants } from './global-constants'; + +describe('GlobalConstants', () => { + it('should create an instance', () => { + expect(new GlobalConstants()).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/global-constants.ts b/jambotron-ui/src/app/global-constants.ts new file mode 100644 index 0000000..fe73560 --- /dev/null +++ b/jambotron-ui/src/app/global-constants.ts @@ -0,0 +1,15 @@ +import {environment} from '../environments/environment'; + +export class GlobalConstants { + + public static readonly API_URL = (() => { + // ... calculate the value and return it + + if(environment.production) { + return `${environment.host_name}/api`; + }else { + return `${environment.host_name}:${environment.port}/api`; + } + + })(); +} diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.html b/jambotron-ui/src/app/main-module/main.component/main.component.html index 3f83962..e115b74 100644 --- a/jambotron-ui/src/app/main-module/main.component/main.component.html +++ b/jambotron-ui/src/app/main-module/main.component/main.component.html @@ -27,7 +27,7 @@ @if (showAdminBoard) { - @@ -39,6 +39,12 @@ User tools } + @if (showModeratorBoard) { + + } + @if (hasError){ + + {{errorMessage}} + + } + @if(submitted) { +

Tutorial was submitted successfully!

+ + + } + + + + + diff --git a/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.scss b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.scss new file mode 100644 index 0000000..183134c --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.scss @@ -0,0 +1,59 @@ +.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;*/ +} + +.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/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts new file mode 100644 index 0000000..0cfa641 --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TutorialPreviewComponent } from './tutorial-preview.component'; + +describe('TutorialPreviewComponent', () => { + let component: TutorialPreviewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TutorialPreviewComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TutorialPreviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.ts b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.ts new file mode 100644 index 0000000..ae41432 --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorial-preview.component/tutorial-preview.component.ts @@ -0,0 +1,94 @@ +import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {FormsModule} from "@angular/forms"; +import {MarkdownComponent} 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 {ActivatedRoute, RouterLink} from "@angular/router"; +import {Tutorial} from '../../models/tutorial.model'; +import {UserApiService} from '../../user-module/user-api.service'; +import {ModeratorApiService} from '../moderator-api.service'; + +@Component({ + selector: 'app-tutorial-preview.component', + imports: [ + FormsModule, + MarkdownComponent, + MatButton, + MatCard, + MatCardActions, + MatCardContent, + MatCardHeader, + MatError, + MatFormField, + MatInput, + MatLabel, + MatTab, + MatTabGroup, + RouterLink, + MatError, + MatFormField + ], + templateUrl: './tutorial-preview.component.html', + styleUrl: './tutorial-preview.component.scss', + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class TutorialPreviewComponent { + tutorial: Tutorial = new Tutorial(); + submitted = false; + hasError = false; + errorMessage = ''; + 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`; + + private id: string | null | undefined; + + constructor(private moderatorApiService: ModeratorApiService, private route: ActivatedRoute) { + + this.route.queryParams + .subscribe(params => { + console.log(params); + this.id = params['id']; + console.log(this.id); + }); + + this.moderatorApiService.getTutorial(this.id).subscribe( + data=>{ + this.tutorial = data; + } + ); + } + + publishTutorial(): void { + this.tutorial.published = true; + this.moderatorApiService.publish(this.id, this.tutorial) + .subscribe( + response => { + console.log(response); + this.submitted = true; + this.hasError = false; + }, + error => { + console.log(error); + + this.errorMessage = error.error.message; + + this.hasError = true; + }); + } +} diff --git a/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.html b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.html new file mode 100644 index 0000000..7d769b4 --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.html @@ -0,0 +1,44 @@ + + @for (column of columnsSchema; track column){ + + + + + + } + + + +
+ {{column.label}} + + @switch (column.type) { + + @case('isEdit') { +
+ + +
+ } + @case ('boolean') { + + + + } + @case ('datetime') { + {{ element[column.key] | date: 'medium' }} + } + @default { + {{ element[column.key] }} + } + } + +
+ diff --git a/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.scss b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.spec.ts b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.spec.ts new file mode 100644 index 0000000..c8453de --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TutorialsListComponent } from './tutorials-list.component'; + +describe('TutorialsListComponent', () => { + let component: TutorialsListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TutorialsListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TutorialsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.ts b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.ts new file mode 100644 index 0000000..61322a1 --- /dev/null +++ b/jambotron-ui/src/app/moderator-module/tutorials-list.component/tutorials-list.component.ts @@ -0,0 +1,144 @@ +import { Component } from '@angular/core'; +import {DatePipe} from "@angular/common"; +import {MatButton} from "@angular/material/button"; +import { + MatCell, + MatCellDef, MatColumnDef, + MatHeaderCell, MatHeaderCellDef, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, MatTable, MatTableDataSource +} from "@angular/material/table"; +import {MatCheckbox} from "@angular/material/checkbox"; +import {MatSlideToggle} from "@angular/material/slide-toggle"; +import {Router, RouterLink} from "@angular/router"; +import {Tutorial} from '../../models/tutorial.model'; +import {EventData} from '../../_shared/event.class'; +import {ModeratorApiService} from '../moderator-api.service'; +import {TokenStorageService} from '../../services/token-storage.service'; +import {EventBusService} from '../../_shared/event-bus.service'; +import {AuthService} from '../../services/auth.service'; +import {HttpHeaders} from '@angular/common/http'; + +@Component({ + selector: 'app-tutorials-list.component', + imports: [ + DatePipe, + MatButton, + MatCell, + MatCellDef, + MatHeaderCell, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, + MatSlideToggle, + MatTable, + RouterLink, + MatColumnDef, + MatHeaderCellDef + ], + templateUrl: './tutorials-list.component.html', + styleUrl: './tutorials-list.component.scss' +}) +export class TutorialsListComponent { + displayedColumns: string[] = TutorialColumns.map((col) => col.key) + columnsSchema: any = TutorialColumns + dataSource = new MatTableDataSource() + + + + constructor(private moderatorApiService: ModeratorApiService, + private storageService: TokenStorageService, + private eventBusService: EventBusService, + private authService: AuthService, + private router: Router + ) { + this.getTutorials(); + } + getTutorials(): void { + this.moderatorApiService.getBePublishedTutorials() + .subscribe(( data:Tutorial[] ) => { + this.dataSource.data = data; + console.log(data); + }, + error => { + console.log(error); + if ( + ( + error.status === 401 + + ) + && this.storageService.isLoggedIn() + ) { + this.eventBusService.emit(new EventData('logout', null)); + } + }); + } + + + publish(element: any, checked: boolean){ + element.published = checked; + this.moderatorApiService.publish(element.id, element).subscribe( + response => { + console.log(response); + }, + error => { + console.log(error); + }); + + } + + + logout(): void { + this.authService.logout().subscribe({ + next: res => { + console.log(res); + this.storageService.clean(); + + //window.location.reload(); + this.router.navigate(['main/generate-image']).then(() => { + //window.location.reload(); + }) + + }, + error: err => { + console.log(err); + } + }); + + } +} + +export const TutorialColumns = [ + + { + key: 'title', + type: 'text', + label: 'Title', + required: true, + }, + { + key: 'published', + type: 'boolean', + label: 'Is Published', + }, + { + key: 'created', + type: 'datetime', + label: 'Created Date', + required: true, + }, + { + key: 'modified', + type: 'datetime', + label: 'Modified Date', + required: true + }, + { + key: 'isEdit', + type: 'isEdit', + label: '', + } +]; diff --git a/jambotron-ui/src/app/services/auth.service.ts b/jambotron-ui/src/app/services/auth.service.ts index 0a920d3..f9d5c08 100644 --- a/jambotron-ui/src/app/services/auth.service.ts +++ b/jambotron-ui/src/app/services/auth.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; +import {GlobalConstants} from '../global-constants'; -const AUTH_API = 'http://localhost:8080/api/auth/'; +const AUTH_API = GlobalConstants.API_URL + '/auth/'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) @@ -15,6 +16,7 @@ export class AuthService { constructor(private http: HttpClient) { } login(username: string, password: string): Observable { + console.log(AUTH_API + 'signin'); return this.http.post(AUTH_API + 'signin', { username, password diff --git a/jambotron-ui/src/app/services/roles.service.ts b/jambotron-ui/src/app/services/roles.service.ts index dbee6f9..4266e87 100644 --- a/jambotron-ui/src/app/services/roles.service.ts +++ b/jambotron-ui/src/app/services/roles.service.ts @@ -2,8 +2,9 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Role } from '../models/role'; +import {GlobalConstants} from '../global-constants'; -const API_URL = 'http://localhost:8080/api/roles'; +const API_URL = `${GlobalConstants.API_URL}/roles`; @Injectable({ diff --git a/jambotron-ui/src/app/services/system.service.ts b/jambotron-ui/src/app/services/system.service.ts index 8885ca0..3fb9887 100644 --- a/jambotron-ui/src/app/services/system.service.ts +++ b/jambotron-ui/src/app/services/system.service.ts @@ -4,13 +4,14 @@ import {Observable} from 'rxjs'; import {Tutorial} from '../models/tutorial.model'; import {Bean} from '../models/Bean'; import {NameValueItem} from '../models/name-value-item'; +import {GlobalConstants} from '../global-constants'; @Injectable({ providedIn: 'root' }) export class SystemService { - baseUrl : string = 'http://localhost:8080/api/system'; + baseUrl : string = `${GlobalConstants.API_URL}/system`; constructor(private http: HttpClient) { } getDataSourceProperties(): Observable{ diff --git a/jambotron-ui/src/app/services/tutorial.service.ts b/jambotron-ui/src/app/services/tutorial.service.ts index f71c2ec..269c292 100644 --- a/jambotron-ui/src/app/services/tutorial.service.ts +++ b/jambotron-ui/src/app/services/tutorial.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Tutorial } from '../models/tutorial.model'; -import {text} from 'node:stream/consumers'; +import {GlobalConstants} from '../global-constants'; -const baseUrl = 'http://localhost:8080/api/public/tutorials'; +const baseUrl = `${GlobalConstants.API_URL}/public/tutorials`; @Injectable({ providedIn: 'root' diff --git a/jambotron-ui/src/app/services/user.service.ts b/jambotron-ui/src/app/services/user.service.ts index 21d6fa1..f6b1764 100644 --- a/jambotron-ui/src/app/services/user.service.ts +++ b/jambotron-ui/src/app/services/user.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import {User} from "../models/user.model"; -import {Tutorial} from "../models/tutorial.model"; +import {GlobalConstants} from '../global-constants'; -const API_URL = 'http://localhost:8080/api/users'; +const API_URL = `${GlobalConstants.API_URL}/users`; @Injectable({ providedIn: 'root' diff --git a/jambotron-ui/src/app/services/zhipuai-image.service.ts b/jambotron-ui/src/app/services/zhipuai-image.service.ts index ab843e2..6dc015c 100644 --- a/jambotron-ui/src/app/services/zhipuai-image.service.ts +++ b/jambotron-ui/src/app/services/zhipuai-image.service.ts @@ -4,6 +4,7 @@ import {Tutorial} from '../models/tutorial.model'; import {HttpClient} from '@angular/common/http'; import {Image} from '../models/image'; import {SpinnerService} from './spinner.service'; +import {GlobalConstants} from '../global-constants'; @@ -13,7 +14,7 @@ import {SpinnerService} from './spinner.service'; export class ZhipuaiImageService { - baseUrl : string = 'http://localhost:8080/api/zhipuai'; + baseUrl : string = `${GlobalConstants.API_URL}/public/zhipuai`; constructor(private http: HttpClient,public spinnerService: SpinnerService) { diff --git a/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.html b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.html new file mode 100644 index 0000000..c086fd8 --- /dev/null +++ b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.html @@ -0,0 +1,19 @@ + + + + house + @if (!isCollapsed) { + Dashboard + } + + + + + newspaper + @if (!isCollapsed) { + Tutorials + } + + + + diff --git a/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.scss b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.scss new file mode 100644 index 0000000..62a5b67 --- /dev/null +++ b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.scss @@ -0,0 +1,14 @@ +.entry{ + display: flex; + align-items: center; + gap: 1rem; + padding:0.75rem; + color: rgba(24, 255, 255, 0.96); + +} + +a.mdc-list-item +{ + + background-color: rgba(24,255,255,0.04); +} diff --git a/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.spec.ts b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.spec.ts new file mode 100644 index 0000000..86d79b6 --- /dev/null +++ b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SideBarUserComponent } from './side-bar-user.component'; + +describe('SideBarUserComponent', () => { + let component: SideBarUserComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SideBarUserComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SideBarUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.ts b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.ts new file mode 100644 index 0000000..7d71af6 --- /dev/null +++ b/jambotron-ui/src/app/user-module/side-bar-user.component/side-bar-user.component.ts @@ -0,0 +1,20 @@ +import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {MatListItem, MatNavList} from '@angular/material/list'; +import {RouterLink} from '@angular/router'; +import {MatIcon} from '@angular/material/icon'; + +@Component({ + selector: 'app-side-bar-user', + imports: [ + MatIcon, + MatListItem, + MatNavList, + RouterLink + ], + templateUrl: './side-bar-user.component.html', + styleUrl: './side-bar-user.component.scss', + schemas:[CUSTOM_ELEMENTS_SCHEMA] +}) +export class SideBarUserComponent { + isCollapsed = false; +} diff --git a/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.scss b/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.scss index bd7c2c2..183134c 100644 --- a/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.scss +++ b/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.scss @@ -4,7 +4,7 @@ } mat-card{ - margin: 20px; + //margin: 20px; } mat-card-title{ color: cyan; @@ -50,3 +50,10 @@ mat-card-title{ float: right;*/ } +.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/user-module/tutorial-edit.component/tutorial-edit.component.ts b/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.ts index 301c9db..b6119e3 100644 --- a/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.ts +++ b/jambotron-ui/src/app/user-module/tutorial-edit.component/tutorial-edit.component.ts @@ -4,7 +4,6 @@ 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 {NgIf} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {Tutorial} from '../../models/tutorial.model'; import {UserApiService} from '../user-api.service'; diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html index dd683bf..fb527b5 100644 --- a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html +++ b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html @@ -6,9 +6,6 @@ > Remove Rows - @@ -56,7 +53,9 @@ + [disabled]="column.key !== 'tobepublished'" + (change)="publish(element, $event.checked)" + > } 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 ba637d4..a30172c 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 @@ -1,6 +1,6 @@ import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core'; import {MatList, MatListItem} from '@angular/material/list'; -import {Tutorial, TutorialColumns} from '../../models/tutorial.model'; +import {Tutorial} from '../../models/tutorial.model'; import {TutorialService} from '../../services/tutorial.service'; import {UserApiService} from '../user-api.service'; import {MatButton} from '@angular/material/button'; @@ -23,22 +23,15 @@ import { MatTable, MatTableDataSource } from '@angular/material/table'; -import {MatFormField, MatInput} from '@angular/material/input'; -import {DatePipe, NgForOf, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common'; +import {DatePipe} from '@angular/common'; import {MatCheckbox} from '@angular/material/checkbox'; -import {MatDatepicker, MatDatepickerInput, MatDatepickerToggle} from '@angular/material/datepicker'; import {MatSlideToggle} from '@angular/material/slide-toggle'; @Component({ selector: 'app-tutorials-list.component', imports: [ - MatList, - MatListItem, - MatLine, MatButton, - MatIcon, - MatOption, RouterLink, ReactiveFormsModule, MatTable, @@ -47,22 +40,12 @@ import {MatSlideToggle} from '@angular/material/slide-toggle'; MatRowDef, MatRow, FormsModule, - MatInput, - MatFormField, MatColumnDef, MatHeaderCell, MatHeaderCellDef, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, MatCellDef, MatCheckbox, - MatDatepickerInput, - MatDatepickerToggle, - MatDatepicker, DatePipe, - NgIf, - NgForOf, MatCell, MatSlideToggle ], @@ -76,7 +59,7 @@ import {MatSlideToggle} from '@angular/material/slide-toggle'; displayedColumns: string[] = TutorialColumns.map((col) => col.key) columnsSchema: any = TutorialColumns dataSource = new MatTableDataSource() - valid: any = {} + constructor(private userApiService: UserApiService, private storageService: TokenStorageService, @@ -154,22 +137,16 @@ import {MatSlideToggle} from '@angular/material/slide-toggle'; }) } - editRow(row: Tutorial) { - /*if (row.id === 0) { - this.userService.addUser(row).subscribe((newUser: User) => { - row.id = newUser.id - row.isEdit = false - }) - } else { - this.userService.updateUser(row).subscribe(() => (row.isEdit = false)) - }*/ - } + publish(element: any, checked: boolean){ + element.tobepublished = checked; + this.userApiService.update(element.id, element).subscribe( + response => { + console.log(response); + }, + error => { + console.log(error); + }); - disableSubmit(id: number) { - if (this.valid[id]) { - return Object.values(this.valid[id]).some((item) => item === false) - } - return false } logout(): void { @@ -191,3 +168,47 @@ import {MatSlideToggle} from '@angular/material/slide-toggle'; } } + +export const TutorialColumns = [ + { + key: 'isSelected', + type: 'isSelected', + label: '', + }, + { + key: 'title', + type: 'text', + label: 'Title', + required: true, + }, + { + key: 'published', + type: 'boolean', + label: 'Is Published', + }, + { + key: 'created', + type: 'datetime', + label: 'Created Date', + required: true, + + }, + { + key: 'modified', + type: 'datetime', + label: 'Modified Date', + required: true + }, + { + key: 'tobepublished', + type: 'boolean', + label: 'To be published', + required: true + + }, + { + key: 'isEdit', + type: 'isEdit', + label: '', + } +]; 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 a26176c..81c4484 100644 --- a/jambotron-ui/src/app/user-module/user-api.service.ts +++ b/jambotron-ui/src/app/user-module/user-api.service.ts @@ -2,23 +2,21 @@ import { Injectable } from '@angular/core'; import {forkJoin, Observable} from 'rxjs'; import {Tutorial} from '../models/tutorial.model'; import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {GlobalConstants} from '../global-constants'; @Injectable({ providedIn: 'root' }) export class UserApiService { - baseUrl = 'http://localhost:8080/api/user'; + baseUrl = `${GlobalConstants.API_URL}/user`; - httpOptions = { - headers: new HttpHeaders({ 'Content-Type': 'application/json' } ) - }; constructor(private http: HttpClient) { } getUserAllTutorials(): Observable { - return this.http.get(`${this.baseUrl}/tutorials`, this.httpOptions); + return this.http.get(`${this.baseUrl}/tutorials`); } getTutorial(id: string | null | undefined): Observable { diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.html b/jambotron-ui/src/app/user-module/user.component/user.component.html index 9467823..1668b71 100644 --- a/jambotron-ui/src/app/user-module/user.component/user.component.html +++ b/jambotron-ui/src/app/user-module/user.component/user.component.html @@ -1,34 +1,9 @@ - - - +
- - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.scss b/jambotron-ui/src/app/user-module/user.component/user.component.scss index 0a94558..016384e 100644 --- a/jambotron-ui/src/app/user-module/user.component/user.component.scss +++ b/jambotron-ui/src/app/user-module/user.component/user.component.scss @@ -1,49 +1,10 @@ - -mat-toolbar{ - position:fixed; - top:0; - z-index: 2; -} - -mat-sidenav-container { - height:100%; -} - -// Move the content down so that it won't be hidden by the toolbar -mat-sidenav { - padding-top: 3.5rem; - transition: width 0.3s ease; - @media screen and (min-width: 600px) { - padding-top: 4rem; - } - - .entry{ - display: flex; - align-items: center; - gap: 1rem; - padding:0.75rem; - } - -} - -// Move the content down so that it won't be hidden by the toolbar -mat-sidenav-content{ - padding-top: 3.5rem; - @media screen and (min-width: 600px) { - padding-top: 4rem; - } -} - - - -.expanded { - width: 250px; -} - //--------------- .pc-sidebar{ top: 65px; overflow-y: auto; + background-color: rgba(153, 153, 153, 0.16); + + backdrop-filter: blur(8px); } .pc-container{ top: 0px; diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.ts b/jambotron-ui/src/app/user-module/user.component/user.component.ts index b76b805..0a52d2f 100644 --- a/jambotron-ui/src/app/user-module/user.component/user.component.ts +++ b/jambotron-ui/src/app/user-module/user.component/user.component.ts @@ -8,6 +8,7 @@ import {MatIcon} from '@angular/material/icon'; import {BreakpointObserver} from '@angular/cdk/layout'; import {MatToolbar} from '@angular/material/toolbar'; import {MatIconButton} from '@angular/material/button'; +import {SideBarUserComponent} from '../side-bar-user.component/side-bar-user.component'; @Component({ selector: 'app-user.component', @@ -21,7 +22,8 @@ import {MatIconButton} from '@angular/material/button'; MatListItem, MatIcon, MatToolbar, - MatIconButton + MatIconButton, + SideBarUserComponent ], templateUrl: './user.component.html', styleUrl: './user.component.scss', diff --git a/jambotron-ui/src/environments/environment.development.ts b/jambotron-ui/src/environments/environment.development.ts index dd5aecb..5cc9a43 100644 --- a/jambotron-ui/src/environments/environment.development.ts +++ b/jambotron-ui/src/environments/environment.development.ts @@ -1,3 +1,7 @@ export const environment = { - default_page: 'main/generate-image' + default_page: 'main/generate-image', + port: 8080, + fromWeb: false, + production: false, + host_name: 'http://localhost', }; diff --git a/jambotron-ui/src/environments/environment.prod.ts b/jambotron-ui/src/environments/environment.prod.ts new file mode 100644 index 0000000..1673ad0 --- /dev/null +++ b/jambotron-ui/src/environments/environment.prod.ts @@ -0,0 +1,7 @@ +export const environment = { + default_page: 'main/generate-image', + port: 8081, + production: true, + fromWeb: true, + host_name: 'https://jambotron.run.place' +}; diff --git a/jambotron-ui/src/environments/environment.ts b/jambotron-ui/src/environments/environment.ts index dd5aecb..038a3fc 100644 --- a/jambotron-ui/src/environments/environment.ts +++ b/jambotron-ui/src/environments/environment.ts @@ -1,3 +1,7 @@ export const environment = { - default_page: 'main/generate-image' + default_page: 'main/generate-image', + port: 8080, + production: true, + fromWeb: false, + host_name: 'http://localhost' }; diff --git a/src/Docker/Dockerfile b/src/Docker/Dockerfile index 766fe68..6712f0f 100644 --- a/src/Docker/Dockerfile +++ b/src/Docker/Dockerfile @@ -1,15 +1,14 @@ #FROM openjdk:24 AS BUILD_IMAGE -#ENV APP_HOME=/jambotron -#RUN mkdir -p $APP_HOME/src/main/java -#WORKDIR $APP_HOME -#COPY ./build.gradle ./gradlew ./gradlew.bat $APP_HOME/ -#COPY gradle $APP_HOME/gradle -#COPY ./src/ $APP_HOME/src/ -#RUN ./gradlew clean build + +#WORKDIR /jambotron/ +#COPY . ./ +#RUN microdnf install findutils +#RUN ./gradlew build -x test FROM openjdk:24 WORKDIR /jambotron/ 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' '/app/jambotron.jar' -EXPOSE 8080 -CMD ["java","-jar","/app/jambotron.jar"] \ No newline at end of file +#COPY --from=BUILD_IMAGE /jambotron/build/libs/jambotron-0.0.1-SNAPSHOT.jar . +#EXPOSE 8080 +CMD ["java","-jar","/app/jambotron.jar"] +#CMD ["java","-jar","jambotron-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/src/Docker/docker-compose.yml b/src/Docker/docker-compose.yml index 461ab69..b5f1f88 100644 --- a/src/Docker/docker-compose.yml +++ b/src/Docker/docker-compose.yml @@ -6,19 +6,38 @@ services: context: ../../ dockerfile: ./src/Docker/Dockerfile ports: - - "8080:8080" +# - "8081:80" + - "8443:443" depends_on: - postgres_jambotron + volumes: + - certs:/certs +# env_file: "webapp.env" environment: - SPRING_DATASOURCE_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/jambotronDB - SPRING_DATASOURCE_USERNAME: koyeb-adm - SPRING_DATASOURCE_PASSWORD: npg_HfFEUA7bay1i + SERVER_PORT: 443 + FULLCHAINPEM: /certs/live/jambotron.run.place/fullchain.pem + PRIVKEYPEM: /certs/live/jambotron.run.place/privkey.pem + + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB + SPRING_DATASOURCE_USERNAME: admin + SPRING_DATASOURCE_PASSWORD: postgrespw SPRING_FLYWAY_BASELINE-ON-MIGRATE: "true" SPRING_FLYWAY_VALIDATE-ON-MIGRATE: "true" - SPRING_FLYWAY_USER: koyeb-adm - SPRING_FLYWAY_PASSWORD: npg_HfFEUA7bay1i - SPRING_FLYWAY_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/jambotronDB + SPRING_FLYWAY_USER: admin + SPRING_FLYWAY_PASSWORD: postgrespw + SPRING_FLYWAY_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB + +# SPRING_DATASOURCE_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/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_USER: koyeb-adm +# SPRING_FLYWAY_PASSWORD: npg_HfFEUA7bay1i +# SPRING_FLYWAY_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/jambotronDB + # SPRING_DATASOURCE_PASSWORD: /run/secrets/db_password # secrets: @@ -41,3 +60,6 @@ services: #secrets: # db_password: # file: db_password.txt +volumes: + certs: + external: true \ No newline at end of file diff --git a/src/Docker/webapp.env b/src/Docker/webapp.env new file mode 100644 index 0000000..a463786 --- /dev/null +++ b/src/Docker/webapp.env @@ -0,0 +1,3 @@ +SERVER_PORT=443 +FULLCHAINPEM=/certs/live/jambotron.run.place/fullchain.pem +PRIVKEYPEM=/certs/live/jambotron.run.place/privkey.pem diff --git a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java index 2bfd3f8..a2779a5 100644 --- a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java +++ b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java @@ -1,5 +1,6 @@ package com.jambotronGroup.jambotron; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; @@ -7,6 +8,7 @@ import org.slf4j.event.Level; import org.slf4j.helpers.BasicMarker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; import java.util.Iterator; @@ -19,9 +21,9 @@ public class JambotronApplication { SpringApplication.run(JambotronApplication.class, args); - logger.error("Application Run."); - logger.debug("Application Run."); - logger.info("Application Run."); + 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."); } } diff --git a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java index d3efac7..a844230 100644 --- a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java +++ b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java @@ -12,13 +12,13 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/", - maxAge = 3600, - allowCredentials="true", - allowedHeaders = {"Content-Type", "Authorization", "X-Requested-With"} -) +//@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/", +// maxAge = 3600, +// allowCredentials="true", +// allowedHeaders = {"Content-Type", "Authorization", "X-Requested-With"} +//) @RestController -@RequestMapping("/api/zhipuai") +@RequestMapping("/api/public/zhipuai") public class ImageController { @Autowired diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java index 1e8401f..7bceb88 100644 --- a/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java +++ b/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; //@CrossOrigin(origins = "*", maxAge = 3600) -@CrossOrigin(origins = "http://localhost:4200,https://a576-5-248-149-207.ngrok-free.app, https://pony-sincere-chimp.ngrok-free.app", maxAge = 3600, allowCredentials="true") +//@CrossOrigin(origins = "http://localhost:4200,https://a576-5-248-149-207.ngrok-free.app, https://pony-sincere-chimp.ngrok-free.app", maxAge = 3600, allowCredentials="true") @RestController @RequestMapping("/api/auth") public class AuthController { diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java index b897497..c4ae116 100644 --- a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java +++ b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java @@ -20,7 +20,7 @@ import java.time.LocalDateTime; import java.util.*; -//@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600, allowCredentials="true") +//@CrossOrigin(origins = "http://localhost:4200,http://www.jambotron.run.place", maxAge = 3600, allowCredentials="true") @RestController @RequestMapping("/api") public class TutorialController { @@ -66,30 +66,6 @@ public class TutorialController { try { List tutorials = new ArrayList(); -/* Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - String name = authenticationFacade.getAuthentication().getName(); - - User user = userRepository.findById(((UserDetailsImpl)userDetails).getId()).get(); - if (user.getRoles().stream().anyMatch(role -> role.getName().name().equals("ROLE_ADMIN"))) {*/ -/* - if (title == null) - tutorialRepository.findAll().forEach(tutorials::add); - else - tutorialRepository.findByTitleContaining(title).forEach(tutorials::add); -*/ - -/* } else { - - // If the user is not an admin, filter tutorials by user - tutorialRepository.findByUserId(user.getId()).forEach(tutorials::add); - if (tutorials.isEmpty()) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - return new ResponseEntity<>(tutorials, HttpStatus.OK); - }*/ - - //all published tutorials without authentication tutorialRepository.findByPublished(true).forEach(tutorials::add); if (tutorials.isEmpty()) { @@ -102,17 +78,44 @@ public class TutorialController { } } + @GetMapping("/moderator/tutorials") + public ResponseEntity> getBePublishedTutorials(@RequestParam(required = false) String title) { + try { + List tutorials = new ArrayList(); + + tutorialRepository.findBytobepublished(true).forEach(tutorials::add); + + if (tutorials.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + return new ResponseEntity<>(tutorials, HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + @PostMapping("user/tutorial-add") public ResponseEntity createTutorial(@RequestBody Tutorial tutorial) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + // not work from white IP port 80 to docker container port 8080 or 8081 + //UserDetails userDetails = authenticationFacade.getUserDetails(); - User user = userRepository.findById(((UserDetailsImpl)userDetails).getId()).get(); + User user = authenticationFacade.getUser(); try { + Tutorial newTutorial =new Tutorial( + tutorial.getTitle(), + tutorial.getDescription(), + false, + false, + user, + Timestamp.valueOf(LocalDateTime.now()), + Timestamp.valueOf(LocalDateTime.now()) + ); + Tutorial _tutorial = tutorialRepository - .save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false, user, Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now()))); + .save(newTutorial); return new ResponseEntity<>(_tutorial, HttpStatus.CREATED); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); @@ -131,6 +134,18 @@ public class TutorialController { } } + @GetMapping("moderator/tutorial-get/{id}") + public ResponseEntity getBePublishedTutorial(@PathVariable("id") long id) { + Optional tutorialData = tutorialRepository.findById(id); + + if (tutorialData.isPresent()) { + + return new ResponseEntity(tutorialData.get(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + @PutMapping("user/tutorial-update/{id}") public ResponseEntity updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) { Optional tutorialData = tutorialRepository.findById(id); @@ -140,6 +155,31 @@ public class TutorialController { servTutorial.setTitle(tutorial.getTitle()); servTutorial.setDescription(tutorial.getDescription()); servTutorial.setPublished(tutorial.isPublished()); + servTutorial.setTobepublished(tutorial.isTobepublished()); + try { + servTutorial = tutorialRepository.save(servTutorial); + } catch (Exception e) { + + map.put("status", 0); + map.put("message", e.getMessage()); + return new ResponseEntity<>(map,HttpStatus.INTERNAL_SERVER_ERROR); + } + return new ResponseEntity<>(servTutorial, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @PutMapping("moderator/tutorial-update/{id}") + public ResponseEntity moderatorUpdateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) { + Optional tutorialData = tutorialRepository.findById(id); + Map map = new LinkedHashMap(); + if (tutorialData.isPresent()) { + Tutorial servTutorial = tutorialData.get(); + servTutorial.setTitle(tutorial.getTitle()); + servTutorial.setDescription(tutorial.getDescription()); + servTutorial.setPublished(tutorial.isPublished()); + servTutorial.setTobepublished(tutorial.isTobepublished()); try { servTutorial = tutorialRepository.save(servTutorial); } catch (Exception e) { diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java index 50882c7..39b5e03 100644 --- a/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java +++ b/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java @@ -1,6 +1,7 @@ package com.jambotronGroup.jambotron.controllers; +import com.jambotronGroup.jambotron.model.Tutorial; import com.jambotronGroup.jambotron.model.User; import com.jambotronGroup.jambotron.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -8,8 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @CrossOrigin(origins = "${app.origin}", maxAge = 3600, allowCredentials="true") @RestController @@ -35,4 +35,31 @@ public class UsersController { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } + + @PutMapping("users/{id}") + public ResponseEntity updateUserRoles(@PathVariable("id") long id, @RequestBody User user) { + Optional userData = userRepository.findById(id); + Map map = new LinkedHashMap(); + if (userData.isPresent()) { + User servUser = userData.get(); + if(user.getRoles().size() > 0) + servUser.setRoles(user.getRoles()); + else { + map.put("status", 0); + map.put("message", "User must have at least one role"); + return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST); + } + try { + servUser = userRepository.save(servUser); + } catch (Exception e) { + + map.put("status", 0); + map.put("message", e.getMessage()); + return new ResponseEntity<>(map,HttpStatus.INTERNAL_SERVER_ERROR); + } + return new ResponseEntity<>(servUser, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } } diff --git a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java index 3f41dde..f8fca87 100644 --- a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java +++ b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java @@ -2,6 +2,7 @@ package com.jambotronGroup.jambotron.model; import jakarta.persistence.*; +import java.sql.Timestamp; @Entity @@ -21,6 +22,9 @@ public class Tutorial { @Column(name = "published") private boolean published; + @Column(name = "tobepublished") + private boolean tobepublished; + @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "userID", nullable = false) private User user; @@ -37,10 +41,11 @@ public class Tutorial { } - public Tutorial(String title, String description, boolean published, User user, java.sql.Timestamp created, java.sql.Timestamp modified) { + public Tutorial(String title, String description, boolean published, boolean tobepublished, User user, Timestamp created, Timestamp modified) { this.title = title; this.description = description; this.published = published; + this.tobepublished = tobepublished; this.user = user; this.created = created; this.modified = modified; @@ -74,6 +79,14 @@ public class Tutorial { this.published = isPublished; } + public boolean isTobepublished() { + return tobepublished; + } + + public void setTobepublished(boolean tobepublished) { + this.tobepublished = tobepublished; + } + public void setCreated(java.sql.Timestamp created) { this.created = created; } diff --git a/src/main/java/com/jambotronGroup/jambotron/model/User.java b/src/main/java/com/jambotronGroup/jambotron/model/User.java index cc0843a..ea22f3c 100644 --- a/src/main/java/com/jambotronGroup/jambotron/model/User.java +++ b/src/main/java/com/jambotronGroup/jambotron/model/User.java @@ -19,6 +19,7 @@ import java.util.Set; public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private long id; @Column(name = "username") diff --git a/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java b/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java index 209b1b2..0709372 100644 --- a/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java +++ b/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java @@ -14,5 +14,7 @@ public interface TutorialRepository extends JpaRepository { List findByUserId(Long userId); List findByUserIdAndTitle(Long userId, String title); List findByPublished(boolean published); + List findBytobepublished(boolean tobepublished); + List findByIdAndTobepublished(Long id,boolean tobepublished); List findByTitleContaining(String title); } \ No newline at end of file diff --git a/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java b/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java index 2565d5c..da5406c 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java @@ -1,29 +1,76 @@ package com.jambotronGroup.jambotron.security; +import com.jambotronGroup.jambotron.controllers.AuthController; +import com.jambotronGroup.jambotron.model.User; +import com.jambotronGroup.jambotron.repository.UserRepository; import com.jambotronGroup.jambotron.security.services.UserDetailsImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; +import java.util.Optional; + @Component public class AuthenticationFacade implements IAuthenticationFacade { + + private static final Logger logger = LoggerFactory.getLogger(AuthenticationFacade.class); + + @Autowired + UserRepository userRepository; + @Override public Authentication getAuthentication() { return SecurityContextHolder.getContext().getAuthentication(); } + + //Deprecated method, use getUser() instead @Override public UserDetailsImpl getUserDetails() { + logger.warn("Retrieving user details from the security context"); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + if (authentication == null || !authentication.isAuthenticated()) { + throw new IllegalStateException("No authenticated user found"); + } + if (!(authentication.getPrincipal() instanceof UserDetails)) { + + throw new IllegalStateException("Authentication principal is not an instance of UserDetails"); + } + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + if (!(userDetails instanceof UserDetailsImpl)) { + throw new IllegalStateException("UserDetails is not an instance of UserDetailsImpl"); + } return (UserDetailsImpl) userDetails; } + @Override + public com.jambotronGroup.jambotron.model.User getUser() { + User returnValue = null; + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!(authentication.getPrincipal() instanceof UserDetails)) { + logger.info("Authentication principal is not an instance of UserDetails, returning null"); + logger.info(authentication.toString()); + + Optional user = userRepository.findByUsername(authentication.getPrincipal().toString()); + + returnValue = user.get(); + }else{ + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + UserDetailsImpl userDetailsImpl = (UserDetailsImpl) userDetails; + returnValue = userRepository.findById(userDetailsImpl.getId()).get(); + } + + return returnValue; + } } diff --git a/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java b/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java index 1db514b..c4f530d 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java @@ -1,5 +1,6 @@ package com.jambotronGroup.jambotron.security; +import com.jambotronGroup.jambotron.model.User; import com.jambotronGroup.jambotron.security.services.UserDetailsImpl; import org.springframework.security.core.Authentication; @@ -7,4 +8,6 @@ public interface IAuthenticationFacade { Authentication getAuthentication(); UserDetailsImpl getUserDetails(); + + User getUser(); } \ No newline at end of file diff --git a/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java new file mode 100644 index 0000000..20ef6a0 --- /dev/null +++ b/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java @@ -0,0 +1,41 @@ +package com.jambotronGroup.jambotron.security; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.io.IOException; +/* + +@Configuration +public class MyCorsFilterConfig extends CorsFilter { + + public MyCorsFilterConfig(CorsConfigurationSource source) { + super((CorsConfigurationSource) source); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + response.addHeader("Access-Control-Allow-Headers", + "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + if (response.getHeader("Access-Control-Allow-Origin") == null) + response.addHeader("Access-Control-Allow-Origin", "http://localhost:4200"); + + if(!request.getRequestURI().startsWith("/api/auth")) { + response.addHeader("Access-Control-Allow-Credentials", "true"); + } + + filterChain.doFilter(request, response); + } + +} +*/ diff --git a/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java new file mode 100644 index 0000000..9dcc696 --- /dev/null +++ b/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java @@ -0,0 +1,43 @@ +package com.jambotronGroup.jambotron.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; +/* +@Configuration +public class RestConfig { + + @Bean + public MyCorsFilterConfig corsFilter() { + CorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("http://localhost:4200,http://www.jambotron.run.place"); + config.addAllowedMethod(HttpMethod.DELETE); + config.addAllowedMethod(HttpMethod.GET); + config.addAllowedMethod(HttpMethod.OPTIONS); + config.addAllowedMethod(HttpMethod.PUT); + config.addAllowedMethod(HttpMethod.POST); + // ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config); + + + + config = new CorsConfiguration(); + config.setAllowCredentials(false); + config.setAllowedOrigins(List.of("http://localhost:4200","http://www.jambotron.run.place")); + config.addAllowedMethod(HttpMethod.DELETE); + config.addAllowedMethod(HttpMethod.GET); + config.addAllowedMethod(HttpMethod.OPTIONS); + config.addAllowedMethod(HttpMethod.PUT); + config.addAllowedMethod(HttpMethod.POST); + config.addAllowedHeader("*"); + ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("api/auth/**", config); + + return new MyCorsFilterConfig(source); + } +}*/ diff --git a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java index e8c0ade..cff92f1 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java @@ -5,12 +5,14 @@ import com.jambotronGroup.jambotron.security.jwt.AuthTokenFilter; import com.jambotronGroup.jambotron.security.services.UserDetailsServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -22,6 +24,8 @@ import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import static org.springframework.security.config.Customizer.withDefaults; + @Configuration @EnableMethodSecurity //@EnableWebSecurity @@ -29,6 +33,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; // securedEnabled = true, // jsr250Enabled = true, //prePostEnabled = true) +@ComponentScan("com.jambotronGroup.jambotron.security") public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecurityConfigurerAdapter { @Autowired UserDetailsServiceImpl userDetailsService; @@ -57,7 +62,6 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri return authProvider; } - // @Bean // @Override // public AuthenticationManager authenticationManagerBean() throws Exception { @@ -106,12 +110,39 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()).cors(cors -> cors.disable()) + http.csrf(csrf -> csrf.disable())//cors.configurationSource(corsCongigSource())) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(auth -> - auth.requestMatchers("/api/auth/**").permitAll() - .requestMatchers("/*", "/home", "/resources/**").permitAll() + .authorizeHttpRequests(auth ->auth + // Access without authentication + // Allow public access to the root and home pages + .requestMatchers("/*").permitAll() + .requestMatchers("/resources/**").permitAll() + + .requestMatchers("/api/auth/**").permitAll() + // Allow public access to tutorials (without login) + .requestMatchers("/api/public/tutorials").permitAll() + //.requestMatchers("/api/public/tutorials/**").permitAll() + //TODO: need check the puth + .requestMatchers("/api/public/zhipuai/image/**").permitAll() + + // need for cerbot why i don't know + .requestMatchers("/.well-known/acme-challenge/**").permitAll() + + // Access permitted for specific roles + .requestMatchers("/api/user/**").hasRole("USER") + .requestMatchers("/api/moderator/**").hasRole("MODERATOR") + .requestMatchers("/api/admin/**").hasRole("ADMIN") + + .anyRequest().authenticated() + + + /* // Allow access to specific API endpoints without authentication + .requestMatchers("/api/auth/**").permitAll() + + // Allow access to specific resources + + .requestMatchers("/*", "/home", "/resources/**").permitAll() .requestMatchers("/resources/public/media/**").permitAll() .requestMatchers("/resources/public/browser/**").permitAll() .requestMatchers("/media/**").permitAll() @@ -121,7 +152,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri .requestMatchers("/api/tutorials").permitAll() - .requestMatchers("/api/zhipuai/image/**").permitAll() + .requestMatchers("/api/users").permitAll() .requestMatchers("/api/users/**").permitAll() @@ -133,16 +164,10 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri .requestMatchers("/api/settings").permitAll() .requestMatchers("/api/system/**").permitAll() +*/ - // Allow public access to tutorials (without login) - .requestMatchers("/api/public/tutorials").permitAll() - .requestMatchers("/api/public/tutorials/**").permitAll() - - .requestMatchers("/api/user/**").permitAll() - - .anyRequest().authenticated() - ); + ).redirectToHttps(withDefaults()); http.authenticationProvider(authenticationProvider()); @@ -151,15 +176,77 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri return http.build(); } - @Bean + /*@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - config.addAllowedOrigin("http://localhost:4200"); +// config.setAllowCredentials(true); +// config.addAllowedOrigin("http://localhost:4200"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); +// +// config = new CorsConfiguration(); +// config.setAllowCredentials(true); +// config.addAllowedOrigin("http://213.111.120.199"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); + +// config = new CorsConfiguration(); +// config.setAllowCredentials(false); +// config.addAllowedOrigin("http://www.jambotron.run.place"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); + + config = new CorsConfiguration(); + config.setAllowCredentials(false); + config.addAllowedOrigin("http://www.jambotron.run.place"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); - source.registerCorsConfiguration("/**", config); + source.registerCorsConfiguration("/api/user/**", config); + return new CorsFilter(source); - } + }*/ + +/* public UrlBasedCorsConfigurationSource corsCongigSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); +// config.setAllowCredentials(true); +// config.addAllowedOrigin("http://localhost:4200"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); +// +// config = new CorsConfiguration(); +// config.setAllowCredentials(true); +// config.addAllowedOrigin("http://213.111.120.199"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); + +// config = new CorsConfiguration(); +// config.setAllowCredentials(false); +// config.addAllowedOrigin("http://www.jambotron.run.place"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); + + config = new CorsConfiguration(); + config.setAllowCredentials(false); + config.addAllowedOrigin("http://localhost:4200, http://www.jambotron.run.place"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/api/user/**", config); + + return source; + }*/ + +/* @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring().requestMatchers("/images/**", "/js/**", "/webjars/**"); + }*/ } + + diff --git a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java index fe7e239..9ed7fc1 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java @@ -9,7 +9,9 @@ import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; @@ -19,6 +21,9 @@ import java.io.IOException; public class AuthTokenFilter extends OncePerRequestFilter { + + @Autowired + AuthenticationManager authenticationManager; @Autowired private JwtUtils jwtUtils; @@ -35,8 +40,15 @@ public class AuthTokenFilter extends OncePerRequestFilter { if (jwt != null && jwtUtils.validateJwtToken(jwt)) { String username = jwtUtils.getUserNameFromJwtToken(jwt); + logger.warn("Username from JWT: {}", username); + UserDetails userDetails = userDetailsService.loadUserByUsername(username); +// Authentication authentication = authenticationManager.authenticate( +// new UsernamePasswordAuthenticationToken(userDetails.getUsername(),userDetails.getPassword())); + + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, diff --git a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java index f29733f..07ca4b6 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java @@ -40,6 +40,8 @@ public class UserDetailsImpl implements UserDetails { .map(role -> new SimpleGrantedAuthority(role.getName().name())) .collect(Collectors.toList()); + + return new UserDetailsImpl( user.getId(), user.getUsername(), diff --git a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java index 1747be3..b23a0f1 100644 --- a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java +++ b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java @@ -23,6 +23,8 @@ public class UserDetailsServiceImpl implements UserDetailsService { .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); return UserDetailsImpl.build(user); +// UserDetailsImpl userDetails = UserDetailsImpl.build(user); +// return new org.springframework.security.core.userdetails.User(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities()); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c8429f1..db841e0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,6 @@ spring.application.name=jambotron +server.port=443 #============Localhost Configurations======================== spring.datasource.url= jdbc:postgresql://localhost:5432/jambotronDB @@ -50,6 +51,8 @@ app.origin=http://localhost:4200 spring.mvc.throw-exception-if-no-handler-found=true +#spring.mvc.dispatch-options-request=true + # Disable the default mappings #spring.resources.add-mappings=false diff --git a/application.yml b/src/main/resources/application.yml similarity index 63% rename from application.yml rename to src/main/resources/application.yml index 733c6a1..5db9925 100644 --- a/application.yml +++ b/src/main/resources/application.yml @@ -10,3 +10,10 @@ logging: name: logs/application.log max-size: 10MB max-history: 30 + +server: + ssl: + enabled: true + certificate: ${FULLCHAINPEM} + certificate-private-key: ${PRIVKEYPEM} +# port: ${SERVER_PORT:443} # Default to 443 if SERVER_PORT is not set diff --git a/src/main/resources/db/migration/V1__Init.sql b/src/main/resources/db/migration/V1__Init.sql index 4480b4e..9132d24 100644 --- a/src/main/resources/db/migration/V1__Init.sql +++ b/src/main/resources/db/migration/V1__Init.sql @@ -1,7 +1,9 @@ + + CREATE TABLE IF NOT EXISTS roles ( id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - name character varying(20) COLLATE pg_catalog."default" + name character varying(20) ); diff --git a/src/main/resources/db/migration/V8__tutorial_remake.sql b/src/main/resources/db/migration/V8__tutorial_remake.sql index 3c476c6..7d58047 100644 --- a/src/main/resources/db/migration/V8__tutorial_remake.sql +++ b/src/main/resources/db/migration/V8__tutorial_remake.sql @@ -4,6 +4,7 @@ CREATE TABLE tutorials ( id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), published boolean, + tobepublished boolean, title character varying(255) COLLATE pg_catalog."default", userid bigint, created timestamp with time zone, diff --git a/src/main/resources/dnsexit_com_ssl/ca.cer b/src/main/resources/dnsexit_com_ssl/ca.cer new file mode 100644 index 0000000..f6a9302 --- /dev/null +++ b/src/main/resources/dnsexit_com_ssl/ca.cer @@ -0,0 +1,71 @@ +-----BEGIN CERTIFICATE----- +MIIG1TCCBL2gAwIBAgIQbFWr29AHksedBwzYEZ7WvzANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjAw +MTMwMDAwMDAwWhcNMzAwMTI5MjM1OTU5WjBLMQswCQYDVQQGEwJBVDEQMA4GA1UE +ChMHWmVyb1NTTDEqMCgGA1UEAxMhWmVyb1NTTCBSU0EgRG9tYWluIFNlY3VyZSBT +aXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAhmlzfqO1Mdgj +4W3dpBPTVBX1AuvcAyG1fl0dUnw/MeueCWzRWTheZ35LVo91kLI3DDVaZKW+TBAs +JBjEbYmMwcWSTWYCg5334SF0+ctDAsFxsX+rTDh9kSrG/4mp6OShubLaEIUJiZo4 +t873TuSd0Wj5DWt3DtpAG8T35l/v+xrN8ub8PSSoX5Vkgw+jWf4KQtNvUFLDq8mF +WhUnPL6jHAADXpvs4lTNYwOtx9yQtbpxwSt7QJY1+ICrmRJB6BuKRt/jfDJF9Jsc +RQVlHIxQdKAJl7oaVnXgDkqtk2qddd3kCDXd74gv813G91z7CjsGyJ93oJIlNS3U +gFbD6V54JMgZ3rSmotYbz98oZxX7MKbtCm1aJ/q+hTv2YK1yMxrnfcieKmOYBbFD +hnW5O6RMA703dBK92j6XRN2EttLkQuujZgy+jXRKtaWMIlkNkWJmOiHmErQngHvt +iNkIcjJumq1ddFX4iaTI40a6zgvIBtxFeDs2RfcaH73er7ctNUUqgQT5rFgJhMmF +x76rQgB5OZUkodb5k2ex7P+Gu4J86bS15094UuYcV09hVeknmTh5Ex9CBKipLS2W +2wKBakf+aVYnNCU6S0nASqt2xrZpGC1v7v6DhuepyyJtn3qSV2PoBiU5Sql+aARp +wUibQMGm44gjyNDqDlVp+ShLQlUH9x8CAwEAAaOCAXUwggFxMB8GA1UdIwQYMBaA +FFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBTI2XhootkZaNU9ct5fCj7c +tYaGpjAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUE +FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwIgYDVR0gBBswGTANBgsrBgEEAbIxAQIC +TjAIBgZngQwBAgEwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1 +c3QuY29tL1VTRVJUcnVzdFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYG +CCsGAQUFBwEBBGowaDA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3Qu +Y29tL1VTRVJUcnVzdFJTQUFkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRw +Oi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAVDwoIzQDV +ercT0eYqZjBNJ8VNWwVFlQOtZERqn5iWnEVaLZZdzxlbvz2Fx0ExUNuUEgYkIVM4 +YocKkCQ7hO5noicoq/DrEYH5IuNcuW1I8JJZ9DLuB1fYvIHlZ2JG46iNbVKA3ygA +Ez86RvDQlt2C494qqPVItRjrz9YlJEGT0DrttyApq0YLFDzf+Z1pkMhh7c+7fXeJ +qmIhfJpduKc8HEQkYQQShen426S3H0JrIAbKcBCiyYFuOhfyvuwVCFDfFvrjADjd +4jX1uQXd161IyFRbm89s2Oj5oU1wDYz5sx+hoCuh6lSs+/uPuWomIq3y1GDFNafW ++LsHBU16lQo5Q2yh25laQsKRgyPmMpHJ98edm6y2sHUabASmRHxvGiuwwE25aDU0 +2SAeepyImJ2CzB80YG7WxlynHqNhpE7xfC7PzQlLgmfEHdU+tHFeQazRQnrFkW2W +kqRGIq7cKRnyypvjPMkjeiV9lRdAM9fSJvsB3svUuu1coIG1xxI1yegoGM4r5QP4 +RGIVvYaiI76C0djoSbQ/dkIUUXQuB8AL5jyH34g3BZaaXyvpmnV4ilppMXVAnAYG +ON51WhJ6W0xNdNJwzYASZYH+tmCWI+N60Gv2NNMGHwMZ7e9bXgzUCZH5FaBFDGR5 +S9VWqHB73Q+OyIVvIbKYcSc2w/aSuFKGSA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7 +MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD +VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE +AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4 +MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5 +MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO +ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI +s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG +vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ +Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb +IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0 +tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E +xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV +icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5 +D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ +WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ +5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG +KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg +EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID +ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG +BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t +L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr +BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA +A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+ +rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+ +/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA +CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F +zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA +vGp4z7h/jnZymQyd/teRCBaho1+V +-----END CERTIFICATE----- diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place.cer b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.cer new file mode 100644 index 0000000..e8cbf1e --- /dev/null +++ b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.cer @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIGjTCCBHWgAwIBAgIQX5Qr0l0Q+rojBlPRWa822TANBgkqhkiG9w0BAQwFADBL +MQswCQYDVQQGEwJBVDEQMA4GA1UEChMHWmVyb1NTTDEqMCgGA1UEAxMhWmVyb1NT +TCBSU0EgRG9tYWluIFNlY3VyZSBTaXRlIENBMB4XDTI1MDcyNTAwMDAwMFoXDTI1 +MTAyMzIzNTk1OVowHjEcMBoGA1UEAxMTamFtYm90cm9uLnJ1bi5wbGFjZTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANiesRlM9apcZ0LHkHD3XoLd4fmR +YcakhAWHAI7AnsszC1nJ/ZY1IGD7+0ms6vMZSHrsZjiJcIOq1jX0Vbn9D8mrO/xR +tA4p1BRm5lalEIwLnwfzfOcu89kWcwegFA9tQBPO9aRaUhJCx9/c30b46IdJMfxu +LDmBafDd2r1DkMs4vBekuchwMM3/ASANdJRSJ2VPt3wXPewWt7jwhNiGbOXvBOGL +RigIkAuuCFRDTMh2R9jWhhVoPHuMK/FaSkjHU3+joYWi2LkAB0LiM2ItMleS3KjU +5t/FNiNLxvI3XTFbxPsX/tkvnFcnW9xuBCQGpctktCEUh/LZWISa7SSVyykCAwEA +AaOCApgwggKUMB8GA1UdIwQYMBaAFMjZeGii2Rlo1T1y3l8KPty1hoamMB0GA1Ud +DgQWBBT5othIQJ1JutIJzOplh40qeCxMwTAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0T +AQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwSQYDVR0gBEIw +QDA0BgsrBgEEAbIxAQICTjAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28u +Y29tL0NQUzAIBgZngQwBAgEwgYgGCCsGAQUFBwEBBHwwejBLBggrBgEFBQcwAoY/ +aHR0cDovL3plcm9zc2wuY3J0LnNlY3RpZ28uY29tL1plcm9TU0xSU0FEb21haW5T +ZWN1cmVTaXRlQ0EuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vemVyb3NzbC5vY3Nw +LnNlY3RpZ28uY29tMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcA3dzKNJXX4RYF +55Uy+sef+D0cUN/bADoUEnYKLKy7yCoAAAGYQutScwAABAMASDBGAiEAsXklZ2/W +quKwHNIv+EKesjXwEJgp/Fz41aMAIpAoNJgCIQDjrZOl4GGpDANYnraoUnGtxYqW +hMW51zVZOuyn2boDGQB1AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0 +AAABmELrUj4AAAQDAEYwRAIgChsIf08E36ymOBfBnOOFep9yyBdeiMYzSzPJGLmn +R+0CID5X2rqCJrYLOqEG/genTqpdfwb0wb1OUxz5JeCMD9B3MDcGA1UdEQQwMC6C +E2phbWJvdHJvbi5ydW4ucGxhY2WCF3d3dy5qYW1ib3Ryb24ucnVuLnBsYWNlMA0G +CSqGSIb3DQEBDAUAA4ICAQApWKMHQzOrkVA0D6y/Zty4dIGF6HD+ZxpEZuVVXhbb +K+GZBR4eVDY62ekp+pWH+uu+7TacNIL7p1Xn9SY/uV5tE1n5c2A8gssZojcCper2 +YD+9Mva+7ZwwIZ3D1xaCs5AeLMFAK1jtISR5KGt2XL9eROxHJ/UFJFtFMTkC8est +E7fMHmGca3OYWOxsfxa5aNJp4NbSeWDQG2XsVK982zj1x6iaVbJCN+uv3ShS4nnS +RKbfBvVzFxQbD8cLf24ZrB/LWpF0QeekE9DWVai1wSw1NtjLyntLv+RqY4L/aUbT +sBRI95k4KeIkoamZA+87wCC1WzsydPZkYtTuOW5jN18zoCXgEyuMwXQMPrKUG/de +ZsLKN16lIwc9pA3eKmlj4AU7Fo0goHkehvOTf/P38za5aHahFejp50rWxHw1FJBh +xFjJixarpQQi62hpEruq9pRnAtGQJd+4uYxzAMiHIEM/LD3gZOy4xFYJIDnNWQ8M +H3ZAdcHe+tbRzTcS93NikmTHjjMoJtI/j92kIFZsm0ZyIqNkxrLFJkUWWac66h1K +CgqRuubmLi3aZggMuDveyTXIztk+F6S7orrbHlHinLjMiJUTxkCnmJ1InLfMPJwW +K78/7AA0iqIvdZxVvA7d4FZpU4BIiMPI6B3brCoE+QfhawiPKotN+/tTPLeaytrD +DA== +-----END CERTIFICATE----- diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place.key b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.key new file mode 100644 index 0000000..d2aa6a6 --- /dev/null +++ b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2J6xGUz1qlxnQseQcPdegt3h+ZFhxqSEBYcAjsCeyzMLWcn9 +ljUgYPv7Sazq8xlIeuxmOIlwg6rWNfRVuf0Pyas7/FG0DinUFGbmVqUQjAufB/N8 +5y7z2RZzB6AUD21AE871pFpSEkLH39zfRvjoh0kx/G4sOYFp8N3avUOQyzi8F6S5 +yHAwzf8BIA10lFInZU+3fBc97Ba3uPCE2IZs5e8E4YtGKAiQC64IVENMyHZH2NaG +FWg8e4wr8VpKSMdTf6OhhaLYuQAHQuIzYi0yV5LcqNTm38U2I0vG8jddMVvE+xf+ +2S+cVydb3G4EJAaly2S0IRSH8tlYhJrtJJXLKQIDAQABAoIBAGcgJW/GE65nD0Hb +gAhHu9bRiyMk80d+StvWyY0sZmyWgHDemLtJz4BcdeU9TR0ujDL5GDESPV5xlklZ +aPaCY2G/A2+79dxITY7o7f+R1a7WWX+Pi4cD81MGPP0EWIh/mmHTxV3ZIflPkZgh +rqo7FXhSgFmMmcFGuwjQlbJE2EnmeCV1X8TmVbgekRm4BQ357lLvn8YdMRqua61o +Z8CAMG6WP87T/1p9AGtVdXm2ZhI/nbcu0kcguOG3lMbDAW+sZEMVgBc8vJHRChl6 +5ab/ePUTMekHPrYH8A+dFFc4pW99xJshQSqZBvFMWqjUfwH6OIXUjpwE1P8p3/7Y +XBLBbgECgYEA94vCQ51zKqXnSnwoAXkK480PwZQV5MEBK+GXQA3tha6lveDyQFnY +NH/NS9bWqEYCw8T0gkaV8j1d0FHwA8rn3lEICnJuY2PZiCU8t5O8n/8ITC1y9wqg +5zIivFzfjtv+Deb4JacBeOCk1W5vkI0RhsEnIrNmhlEatW593MfY/70CgYEA4ASN +nEllTl/aMqI+/nqY86tvEY9Ik0L7PNf4u+wj7FiTujnv0KYL5yl/1+9Hn3MtBIwI +y/bWfa95RESiqMzKjTYCwHSkaSKgXBvEeSpLjO0NhsxO9/6SiQm7yEAqaOq9H6Ms +e10GnChet+Fl1mRWk+FvjNQuySXS3IQwzERW6d0CgYA0l/j6LKWfVghCBwbo6TKr +G5JRaokMRQVesVtVPKBOWsDUCsrtaSlKXizcBBUvQ1CrD1lzpFOPWAJqlD4OUDnb +PhQbRBy3+Srqhh7UUgstYx38MVvPqO5usHQ42PKWg58CjSQDu+vQJspenkwNxisH +GlqaQMtzlh+6lHMhSUSNcQKBgQCUAe4eKFAKrEHZ2mCMeiu8MrQ7gdONmF+uH3Nz +ld1WNl/EVqsfy9VpcX3KCYnky5AexPa66+djOyB//mkJ5eSdz+WZindmDz6sHJx/ +AXbRMX4SZcJ3D3d5mzi6YcqjbxRtZr3o89l+Kx4Jl55VPA4HvpaZEUeoFpluoNgs +3aoe8QKBgCDlA0WEIlq4Idw1zFJzv+2rvJPb9fG77TGfDNGnTbx0/jDFN/L3e9A2 +71Je1hnhqpxKZS23gzD7SmU6fJdO1j6+qZs8FZewQmnMoPk3He9JSzrJtglWGF8Q +VLyuaylgRI7bv904DVmdwzlnLBPcG6SdHwqc3ufBeeeFbAI3KdFJ +-----END RSA PRIVATE KEY----- diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place.p12 b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.p12 new file mode 100644 index 0000000..2769d3e Binary files /dev/null and b/src/main/resources/dnsexit_com_ssl/jambotron.run.place.p12 differ diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.p7b b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.p7b new file mode 100644 index 0000000..4af5382 Binary files /dev/null and b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.p7b differ diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.json b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.json new file mode 100644 index 0000000..2f84114 --- /dev/null +++ b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.json @@ -0,0 +1 @@ +{"kty":"RSA","e":"AQAB","kid":"jambotron.run.place (zerossl rsa domain secure site ca)","n":"2J6xGUz1qlxnQseQcPdegt3h-ZFhxqSEBYcAjsCeyzMLWcn9ljUgYPv7Sazq8xlIeuxmOIlwg6rWNfRVuf0Pyas7_FG0DinUFGbmVqUQjAufB_N85y7z2RZzB6AUD21AE871pFpSEkLH39zfRvjoh0kx_G4sOYFp8N3avUOQyzi8F6S5yHAwzf8BIA10lFInZU-3fBc97Ba3uPCE2IZs5e8E4YtGKAiQC64IVENMyHZH2NaGFWg8e4wr8VpKSMdTf6OhhaLYuQAHQuIzYi0yV5LcqNTm38U2I0vG8jddMVvE-xf-2S-cVydb3G4EJAaly2S0IRSH8tlYhJrtJJXLKQ"} \ No newline at end of file diff --git a/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.pem b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.pem new file mode 100644 index 0000000..057fa18 --- /dev/null +++ b/src/main/resources/dnsexit_com_ssl/jambotron.run.place_zerossl_rsa_domain_secure_site_ca_.pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2J6xGUz1qlxnQseQcPde +gt3h+ZFhxqSEBYcAjsCeyzMLWcn9ljUgYPv7Sazq8xlIeuxmOIlwg6rWNfRVuf0P +yas7/FG0DinUFGbmVqUQjAufB/N85y7z2RZzB6AUD21AE871pFpSEkLH39zfRvjo +h0kx/G4sOYFp8N3avUOQyzi8F6S5yHAwzf8BIA10lFInZU+3fBc97Ba3uPCE2IZs +5e8E4YtGKAiQC64IVENMyHZH2NaGFWg8e4wr8VpKSMdTf6OhhaLYuQAHQuIzYi0y +V5LcqNTm38U2I0vG8jddMVvE+xf+2S+cVydb3G4EJAaly2S0IRSH8tlYhJrtJJXL +KQIDAQAB +-----END PUBLIC KEY-----