diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..adfb668 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,66 @@ + + +stages: + - build + - prepare + - test + - deploy + +variables: + # Configure mysql environment variables (https://hub.docker.com/_/mysql/) + MYSQL_DATABASE: "aitrainer" + MYSQL_ROOT_PASSWORD: "andio2009" + MYSQL_USER: "aitrainer" + MYSQL_PASSWORD: "andio2009" + + +before_script: + - echo `pwd` # debug + - echo "$CI_BUILD_NAME, $CI_BUILD_REF_NAME $CI_BUILD_STAGE" # debug + - export GRADLE_USER_HOME=`pwd`/.gradle + +cache: + paths: + - .gradle/wrapper + - .gradle/caches + +build: + stage: build + image: openjdk:latest + script: + - ./gradlew assemble + artifacts: + paths: + - build/libs/*.jar + expire_in: 1 week + only: + - master + +connect: + stage: prepare + image: mysql:latest + script: + - apt-get update && apt-get --assume-yes install mysql-client + - mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql -e "use $MYSQL_DATABASE; show tables;" + - mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql -e "use $MYSQL_DATABASE; DROP table if exists customer; DROP table if exists exercises; DROP table if exists exercise_type; DROP table if exists exercise_ages;" + - mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql < "data/db/install.sql" #first time + +test: + stage: test + image: openjdk:latest + script: + - export spring_profiles_active=test + - ./gradlew check + +deploy: + stage: deploy + image: mysql:latest + script: + - apt-get update && apt-get --assume-yes install sshpass + - chmod +x ci-cd/deploy.sh + - ci-cd/deploy.sh + only: + - master + +after_script: + - echo "End CI" diff --git a/.gitlab-runner-register b/.gitlab-runner-register new file mode 100644 index 0000000..644d1dc --- /dev/null +++ b/.gitlab-runner-register @@ -0,0 +1 @@ +docker run --rm -t -i -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..52600c6 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,58 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + + +plugins { + id("org.springframework.boot") version "2.3.0.RELEASE" + id("io.spring.dependency-management") version "1.0.9.RELEASE" + kotlin("jvm") version "1.3.72" + kotlin("plugin.spring") version "1.3.72" + kotlin("plugin.jpa") version "1.3.72" + kotlin("plugin.serialization") version "1.3.70" +} + +group = "com.aitrainer" +version = "0.0.5" +java.sourceCompatibility = JavaVersion.VERSION_1_8 + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-aop") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.3.0.RELEASE") + implementation("org.springframework.security.oauth:spring-security-oauth2:2.5.0.RELEASE") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.apache.logging.log4j:log4j-core:2.13.3") + implementation("org.apache.logging.log4j:log4j-api:2.13.3") + implementation("org.slf4j:slf4j-api:1.7.30") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") // JVM dependency + implementation("io.jsonwebtoken:jjwt:0.9.1") + + + + runtimeOnly("mysql:mysql-connector-java") + testImplementation("org.springframework.boot:spring-boot-starter-test") { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation("junit:junit:4.13") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.3.72") + +} + +tasks.withType { + useJUnitPlatform() +} + +tasks.withType { + kotlinOptions { + freeCompilerArgs = listOf("-Xjsr305=strict") + jvmTarget = "1.8" + } +} diff --git a/ci-cd/.ssh/.scp b/ci-cd/.ssh/.scp new file mode 100644 index 0000000..7f7f899 --- /dev/null +++ b/ci-cd/.ssh/.scp @@ -0,0 +1 @@ +tbi6012AndiBossanyi \ No newline at end of file diff --git a/ci-cd/config.toml b/ci-cd/config.toml new file mode 100644 index 0000000..5ebdc82 --- /dev/null +++ b/ci-cd/config.toml @@ -0,0 +1,52 @@ +concurrent = 4 +log_level = "warning" + +[session_server] + listen_address = "0.0.0.0:8093" # listen on all available interfaces on port 8093 + advertise_address = "andio.club:8093" + session_timeout = 1800 + +[[runners]] + name = "aitraner-server-docker" + url = "https://andio.club" + token = "R_-WrxvRXkP6HuU95dEs" + limit = 0 + executor = "shell" + builds_dir = "/home/bosi/build" + shell = "bash" + environment = ["ENV=value", "LC_ALL=en_US.UTF-8"] + clone_url = "http://localhost" + +[runners.docker] + host = "" + hostname = "" + tls_cert_path = "" + image = "docker-runner" + memory = "128m" + memory_swap = "256m" + memory_reservation = "64m" + oom_kill_disable = false + cpuset_cpus = "0,1" + cpus = "2" + dns = ["8.8.8.8"] + dns_search = [""] + privileged = false + userns_mode = "host" + cap_add = ["NET_ADMIN"] + cap_drop = ["DAC_OVERRIDE"] + devices = ["/dev/net/tun"] + disable_cache = false + wait_for_services_timeout = 30 + cache_dir = "" + volumes = ["/data", "/home/project/cache"] + extra_hosts = ["127.0.0.1"] + shm_size = 300000 + volumes_from = ["storage_container:ro"] + links = ["mysql_container:mysql"] + allowed_images = ["ruby:*", "python:*", "php:*"] + allowed_services = ["mysql"] + [[runners.docker.services]] + name = "mysql" + alias = "db" + [runners.docker.sysctls] + "net.ipv4.ip_forward" = "1" \ No newline at end of file diff --git a/ci-cd/deploy.sh b/ci-cd/deploy.sh new file mode 100644 index 0000000..1e7a4ca --- /dev/null +++ b/ci-cd/deploy.sh @@ -0,0 +1 @@ +sshpass -f /ci-cd/.ssh/.scp scp -p 6622 build/libs/aitrainer_server-0.0.2.jar bosi@andio.shop:/home/bosi/aitrainer/deploy/aitrainer_server.jar \ No newline at end of file diff --git a/ci-cd/docker.development.yml b/ci-cd/docker.development.yml new file mode 100644 index 0000000..55c2c83 --- /dev/null +++ b/ci-cd/docker.development.yml @@ -0,0 +1,98 @@ +version: '3.8' +services: + jira: + image: 'atlassian/jira-software:latest' + container_name: 'jira' + restart: 'always' + environment: + ATL_TOMCAT_PORT: 8082 + ATL_TOMCAT_SCHEME: "https" + ATL_TOMCAT_SECURE: "true" + ATL_DB_DRIVER: "com.mysql.jdbc.Driver" + ATL_DB_TYPE: "mysql" + volumes: + - jiraVolume:/var/atlassian/application-data/jira + - db_data:/var/lib/mysql + ports: + - 8082:80 + gitlab: + image: 'gitlab/gitlab-ce:latest' + container_name: 'gitlab' + restart: always + hostname: 'localhost' + environment: + GITLAB_OMNIBUS_CONFIG: | + external_url 'https://andio.club:443' + gitlab_rails['smtp_enable'] = true + gitlab_rails['smtp_address'] = "email-smtp.eu-west-1.amazonaws.com" + gitlab_rails['smtp_port'] = 587 + gitlab_rails['smtp_user_name'] = "AKIAIWHHQDMPADT7ETHQ" + gitlab_rails['smtp_password'] = "AjCB8NA+61i/URp09gik0HHtbEuy48e4JXhuPaqGacFs" + gitlab_rails['smtp_domain'] = "andio.club" + gitlab_rails['smtp_authentication'] = "login" + gitlab_rails['smtp_enable_starttls_auto'] = true + gitlab_rails['smtp_openssl_verify_mode'] = 'peer' + # Add any other gitlab.rb configuration here, each on its own line + gitlab_rails['gitlab_shell_ssh_port'] = 6622 + ports: + - '80:80' + - '443:443' + - '6622:22' + - '587:587' + volumes: + - '/srv/gitlab/config:/etc/gitlab' + - '/srv/gitlab/logs:/var/log/gitlab' + - '/srv/gitlab/data:/var/opt/gitlab' + mysql: + image: mysql:latest + volumes: + - db_data:/var/lib/mysql + restart: always + ports: + - 33061:33061 + environment: + MYSQL_ROOT_PASSWORD: andio2009 + MYSQL_DATABASE: aitrainer + MYSQL_USER: aitrainer + MYSQL_PASSWORD: andio2009 + networks: + - bosi_default + phpmyadmin: + depends_on: + - mysql + image: phpmyadmin/phpmyadmin + restart: always + ports: + - '8081:80' + environment: + PMA_HOST: mysql + MYSQL_ROOT_PASSWORD: andio2009 + networks: + - bosi_default + php: + image: php:7.2-fpm + volumes: + - php:/var/www/html + - ./php/php.ini:/usr/local/etc/php/php.ini + depends_on: + - mysql + gitlab-runner: + image: gitlab/gitlab-runner:latest + container_name: gitlab-runner + restart: always + networks: + - bosi_default + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /srv/gitlab-runner/config:/etc/gitlab-runner +secrets: + mysql_root_pwd: + file: /.sec/mysql_root_pwd + mysql_user_pwd: + file: /.sec/mysql_user_pwd +networks: + bosi_default: +volumes: + db_data: + php: + jiraVolume: \ No newline at end of file diff --git a/ci-cd/production-docker-compose.yml b/ci-cd/production-docker-compose.yml new file mode 100644 index 0000000..2cc74b7 --- /dev/null +++ b/ci-cd/production-docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.8' + +services: + mysql: + image: mysql:latest + container_name: mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: andio2009 + MYSQL_DATABASE: aitrainer + MYSQL_USER: aitrainer + MYSQL_PASSWORD: andio2009 + volumes: + - ./docker/db:/docker-entrypoint-initdb.d + ports: + - "33061:33061" + command: mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=localhost < "/home/bosi/aitrainer/data/install.sql" + + java: + image: openjdk:latest + + + tomcat: + image: tomcat:latest + container_name: tomcat + volumes: + - ./docker/aitrainer_server.jar:/home/bosi/aitrainer/deploy/aitrainer_server.jar + ports: + - "8080:8080" + depends_on: + - java \ No newline at end of file diff --git a/ci-cd/readme.md b/ci-cd/readme.md new file mode 100644 index 0000000..d56a3a1 --- /dev/null +++ b/ci-cd/readme.md @@ -0,0 +1 @@ +jar file to deploy \ No newline at end of file diff --git a/data/config/docker-compose_cowmail.yml b/data/config/docker-compose_cowmail.yml new file mode 100644 index 0000000..8e424e8 --- /dev/null +++ b/data/config/docker-compose_cowmail.yml @@ -0,0 +1,631 @@ +version: '2.1' +services: + + unbound-mailcow: + image: mailcow/unbound:1.12 + environment: + - TZ=${TZ} + volumes: + - ./data/hooks/unbound:/hooks + - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro + restart: always + tty: true + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.254 + aliases: + - unbound + + mysql-mailcow: + image: mariadb:10.3 + depends_on: + - unbound-mailcow + stop_grace_period: 45s + volumes: + - mysql-vol-1:/var/lib/mysql/ + - mysql-socket-vol-1:/var/run/mysqld/ + - ./data/conf/mysql/:/etc/mysql/conf.d/:ro + environment: + - TZ=${TZ} + - MYSQL_ROOT_PASSWORD=${DBROOT} + - MYSQL_DATABASE=${DBNAME} + - MYSQL_USER=${DBUSER} + - MYSQL_PASSWORD=${DBPASS} + - MYSQL_INITDB_SKIP_TZINFO=1 + restart: always + ports: + - "${SQL_PORT:-127.0.0.1:13306}:3306" + networks: + mailcow-network: + aliases: + - mysql + + redis-mailcow: + image: redis:5-alpine + volumes: + - redis-vol-1:/data/ + restart: always + ports: + - "${REDIS_PORT:-127.0.0.1:7654}:6379" + environment: + - TZ=${TZ} + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.249 + aliases: + - redis + + clamd-mailcow: + image: mailcow/clamd:1.36 + restart: always + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - TZ=${TZ} + - SKIP_CLAMD=${SKIP_CLAMD:-n} + volumes: + - ./data/conf/clamav/:/etc/clamav/ + networks: + mailcow-network: + aliases: + - clamd + + rspamd-mailcow: + image: mailcow/rspamd:1.68 + stop_grace_period: 30s + depends_on: + - nginx-mailcow + - dovecot-mailcow + environment: + - TZ=${TZ} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + volumes: + - ./data/hooks/rspamd:/hooks + - ./data/conf/rspamd/custom/:/etc/rspamd/custom + - ./data/conf/rspamd/override.d/:/etc/rspamd/override.d + - ./data/conf/rspamd/local.d/:/etc/rspamd/local.d + - ./data/conf/rspamd/plugins.d/:/etc/rspamd/plugins.d + - ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro + - ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local + - ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override + - rspamd-vol-1:/var/lib/rspamd + restart: always + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + hostname: rspamd + networks: + mailcow-network: + aliases: + - rspamd + + php-fpm-mailcow: + image: mailcow/phpfpm:1.63 + command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" + depends_on: + - redis-mailcow + volumes: + - ./data/hooks/phpfpm:/hooks + - ./data/web:/web:rw + - ./data/conf/rspamd/dynmaps:/dynmaps:ro + - ./data/conf/rspamd/custom/:/rspamd_custom_maps + - rspamd-vol-1:/var/lib/rspamd + - mysql-socket-vol-1:/var/run/mysqld/ + - ./data/conf/sogo/:/etc/sogo/ + - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro + - ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/ + - ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf + - ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini + - ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini + - ./data/conf/phpfpm/php-conf.d/other.ini:/usr/local/etc/php/conf.d/zzz-other.ini + - ./data/conf/dovecot/global_sieve_before:/global_sieve/before + - ./data/conf/dovecot/global_sieve_after:/global_sieve/after + - ./data/assets/templates:/tpls + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - LOG_LINES=${LOG_LINES:-9999} + - TZ=${TZ} + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - IMAP_PORT=${IMAP_PORT:-143} + - IMAPS_PORT=${IMAPS_PORT:-993} + - POP_PORT=${POP_PORT:-110} + - POPS_PORT=${POPS_PORT:-995} + - SIEVE_PORT=${SIEVE_PORT:-4190} + - SUBMISSION_PORT=${SUBMISSION_PORT:-587} + - SMTPS_PORT=${SMTPS_PORT:-465} + - SMTP_PORT=${SMTP_PORT:-25} + - API_KEY=${API_KEY:-invalid} + - API_KEY_READ_ONLY=${API_KEY_READ_ONLY:-invalid} + - API_ALLOW_FROM=${API_ALLOW_FROM:-invalid} + - COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized} + - SKIP_SOLR=${SKIP_SOLR:-y} + - SKIP_CLAMD=${SKIP_CLAMD:-n} + - SKIP_SOGO=${SKIP_SOGO:-n} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} + - MASTER=${MASTER:-y} + restart: always + networks: + mailcow-network: + aliases: + - phpfpm + + sogo-mailcow: + image: mailcow/sogo:1.74 + environment: + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - TZ=${TZ} + - LOG_LINES=${LOG_LINES:-9999} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - ACL_ANYONE=${ACL_ANYONE:-disallow} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - SOGO_EXPIRE_SESSION=${SOGO_EXPIRE_SESSION:-480} + - SKIP_SOGO=${SKIP_SOGO:-n} + - MASTER=${MASTER:-y} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + volumes: + - ./data/conf/sogo/:/etc/sogo/ + - ./data/web/inc/init_db.inc.php:/init_db.inc.php + - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js + - mysql-socket-vol-1:/var/run/mysqld/ + - sogo-web-vol-1:/sogo_web + - sogo-userdata-backup-vol-1:/sogo_backup + restart: always + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.248 + aliases: + - sogo + + dovecot-mailcow: + image: mailcow/dovecot:1.125 + depends_on: + - mysql-mailcow + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + cap_add: + - NET_BIND_SERVICE + volumes: + - ./data/hooks/dovecot:/hooks + - ./data/conf/dovecot:/etc/dovecot + - ./data/assets/ssl:/etc/ssl/mail/:ro + - ./data/conf/sogo/:/etc/sogo/ + - ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/ + - vmail-vol-1:/var/vmail + - vmail-attachments-vol-1:/var/attachments + - crypt-vol-1:/mail_crypt/ + - ./data/conf/rspamd/custom/:/etc/rspamd/custom + - ./data/assets/templates:/templates + - rspamd-vol-1:/var/lib/rspamd + - mysql-socket-vol-1:/var/run/mysqld/ + environment: + - LOG_LINES=${LOG_LINES:-9999} + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - TZ=${TZ} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} + - MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-1440} + - ACL_ANYONE=${ACL_ANYONE:-disallow} + - SKIP_SOLR=${SKIP_SOLR:-y} + - MAILDIR_SUB=${MAILDIR_SUB:-} + - MASTER=${MASTER:-y} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + ports: + - "${DOVEADM_PORT:-127.0.0.1:19991}:12345" + - "${IMAP_PORT:-143}:143" + - "${IMAPS_PORT:-993}:993" + - "${POP_PORT:-110}:110" + - "${POPS_PORT:-995}:995" + - "${SIEVE_PORT:-4190}:4190" + restart: always + tty: true + ulimits: + nproc: 65535 + nofile: + soft: 20000 + hard: 40000 + hostname: ${MAILCOW_HOSTNAME} + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.250 + aliases: + - dovecot + + postfix-mailcow: + image: mailcow/postfix:1.49 + depends_on: + - mysql-mailcow + volumes: + - ./data/hooks/postfix:/hooks + - ./data/conf/postfix:/opt/postfix/conf + - ./data/assets/ssl:/etc/ssl/mail/:ro + - postfix-vol-1:/var/spool/postfix + - crypt-vol-1:/var/lib/zeyple + - rspamd-vol-1:/var/lib/rspamd + - mysql-socket-vol-1:/var/run/mysqld/ + environment: + - LOG_LINES=${LOG_LINES:-9999} + - TZ=${TZ} + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + cap_add: + - NET_BIND_SERVICE + ports: + - "${SMTP_PORT:-25}:25" + - "${SMTPS_PORT:-465}:465" + - "${SUBMISSION_PORT:-587}:587" + restart: always + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + hostname: ${MAILCOW_HOSTNAME} + networks: + mailcow-network: + aliases: + - postfix + + memcached-mailcow: + image: memcached:alpine + restart: always + environment: + - TZ=${TZ} + networks: + mailcow-network: + aliases: + - memcached + + nginx-mailcow: + depends_on: + - sogo-mailcow + - php-fpm-mailcow + - redis-mailcow + image: nginx:mainline-alpine + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active && + envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active && + envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active && + envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active && + . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active && + . /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active && + nginx -qt && + until ping phpfpm -c1 > /dev/null; do sleep 1; done && + until ping sogo -c1 > /dev/null; do sleep 1; done && + until ping redis -c1 > /dev/null; do sleep 1; done && + until ping rspamd -c1 > /dev/null; do sleep 1; done && + exec nginx -g 'daemon off;'" + environment: + - HTTPS_PORT=${HTTPS_PORT:-443} + - HTTP_PORT=${HTTP_PORT:-80} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - TZ=${TZ} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} + volumes: + - ./data/web:/web:ro + - ./data/conf/rspamd/dynmaps:/dynmaps:ro + - ./data/assets/ssl/:/etc/ssl/mail/:ro + - ./data/conf/nginx/:/etc/nginx/conf.d/:rw + - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro + - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/ + ports: + - "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}" + - "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}" + restart: always + networks: + mailcow-network: + aliases: + - nginx + + acme-mailcow: + depends_on: + - nginx-mailcow + image: mailcow/acme:1.70 + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - LOG_LINES=${LOG_LINES:-9999} + - ADDITIONAL_SAN=${ADDITIONAL_SAN} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n} + - ENABLE_SSL_SNI=${ENABLE_SSL_SNI:-n} + - SKIP_IP_CHECK=${SKIP_IP_CHECK:-n} + - SKIP_HTTP_VERIFICATION=${SKIP_HTTP_VERIFICATION:-n} + - ONLY_MAILCOW_HOSTNAME=${ONLY_MAILCOW_HOSTNAME:-n} + - LE_STAGING=${LE_STAGING:-n} + - TZ=${TZ} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} + - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} + volumes: + - ./data/web/.well-known/acme-challenge:/var/www/acme:rw + - ./data/assets/ssl:/var/lib/acme/:rw + - ./data/assets/ssl-example:/var/lib/ssl-example/:ro + - mysql-socket-vol-1:/var/run/mysqld/ + restart: always + networks: + mailcow-network: + aliases: + - acme + + netfilter-mailcow: + image: mailcow/netfilter:1.36 + stop_grace_period: 30s + depends_on: + - dovecot-mailcow + - postfix-mailcow + - sogo-mailcow + - php-fpm-mailcow + - redis-mailcow + restart: always + privileged: true + environment: + - TZ=${TZ} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} + - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} + - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + network_mode: "host" + volumes: + - /lib/modules:/lib/modules:ro + + watchdog-mailcow: + image: mailcow/watchdog:1.77 + # Debug + #command: /watchdog.sh + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + volumes: + - rspamd-vol-1:/var/lib/rspamd + - mysql-socket-vol-1:/var/run/mysqld/ + - ./data/assets/ssl:/etc/ssl/mail/:ro + restart: always + environment: + - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} + - LOG_LINES=${LOG_LINES:-9999} + - TZ=${TZ} + - DBNAME=${DBNAME} + - DBUSER=${DBUSER} + - DBPASS=${DBPASS} + - DBROOT=${DBROOT} + - USE_WATCHDOG=${USE_WATCHDOG:-n} + - WATCHDOG_NOTIFY_EMAIL=${WATCHDOG_NOTIFY_EMAIL} + - WATCHDOG_NOTIFY_BAN=${WATCHDOG_NOTIFY_BAN:-y} + - WATCHDOG_EXTERNAL_CHECKS=${WATCHDOG_EXTERNAL_CHECKS:-n} + - WATCHDOG_MYSQL_REPLICATION_CHECKS=${WATCHDOG_MYSQL_REPLICATION_CHECKS:-n} + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - IP_BY_DOCKER_API=${IP_BY_DOCKER_API:-0} + - CHECK_UNBOUND=${CHECK_UNBOUND:-1} + - SKIP_CLAMD=${SKIP_CLAMD:-n} + - SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n} + - SKIP_SOGO=${SKIP_SOGO:-n} + - HTTPS_PORT=${HTTPS_PORT:-443} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - EXTERNAL_CHECKS_THRESHOLD=1 + - NGINX_THRESHOLD=5 + - UNBOUND_THRESHOLD=5 + - REDIS_THRESHOLD=5 + - MYSQL_THRESHOLD=5 + - MYSQL_REPLICATION_THRESHOLD=1 + - SOGO_THRESHOLD=3 + - POSTFIX_THRESHOLD=8 + - CLAMD_THRESHOLD=15 + - DOVECOT_THRESHOLD=12 + - DOVECOT_REPL_THRESHOLD=2 + - PHPFPM_THRESHOLD=5 + - RATELIMIT_THRESHOLD=1 + - FAIL2BAN_THRESHOLD=1 + - ACME_THRESHOLD=1 + - IPV6NAT_THRESHOLD=1 + - RSPAMD_THRESHOLD=5 + - OLEFY_THRESHOLD=5 + networks: + mailcow-network: + aliases: + - watchdog + + dockerapi-mailcow: + image: mailcow/dockerapi:1.37 + restart: always + oom_kill_disable: true + dns: + - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - DBROOT=${DBROOT} + - TZ=${TZ} + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + mailcow-network: + aliases: + - dockerapi + + solr-mailcow: + image: mailcow/solr:1.7 + restart: always + volumes: + - solr-vol-1:/opt/solr/server/solr/dovecot-fts/data + ports: + - "${SOLR_PORT:-127.0.0.1:18983}:8983" + environment: + - TZ=${TZ} + - SOLR_HEAP=${SOLR_HEAP:-1024} + - SKIP_SOLR=${SKIP_SOLR:-y} + networks: + mailcow-network: + aliases: + - solr + + olefy-mailcow: + image: mailcow/olefy:1.3 + restart: always + environment: + - TZ=${TZ} + - OLEFY_BINDADDRESS=0.0.0.0 + - OLEFY_BINDPORT=10055 + - OLEFY_TMPDIR=/tmp + - OLEFY_PYTHON_PATH=/usr/bin/python3 + - OLEFY_OLEVBA_PATH=/usr/bin/olevba3 + - OLEFY_LOGLVL=20 + - OLEFY_MINLENGTH=500 + - OLEFY_DEL_TMP=1 + networks: + mailcow-network: + aliases: + - olefy + + ipv6nat-mailcow: + depends_on: + - unbound-mailcow + - mysql-mailcow + - redis-mailcow + - clamd-mailcow + - rspamd-mailcow + - php-fpm-mailcow + - sogo-mailcow + - dovecot-mailcow + - postfix-mailcow + - memcached-mailcow + - nginx-mailcow + - acme-mailcow + - netfilter-mailcow + - watchdog-mailcow + - dockerapi-mailcow + - solr-mailcow + environment: + - TZ=${TZ} + image: robbertkl/ipv6nat + restart: always + privileged: true + network_mode: "host" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro + + + gitlab: + image: 'gitlab/gitlab-ce:latest' + container_name: 'gitlab' + restart: always + hostname: 'localhost' + environment: + GITLAB_OMNIBUS_CONFIG: | + external_url 'https://aitrainer.app:8929' + gitlab_rails['smtp_enable'] = true + gitlab_rails['smtp_address'] = "email-smtp.eu-west-1.amazonaws.com" + gitlab_rails['smtp_port'] = 587 + gitlab_rails['smtp_user_name'] = "AKIAIWHHQDMPADT7ETHQ" + gitlab_rails['smtp_password'] = "AjCB8NA+61i/URp09gik0HHtbEuy48e4JXhuPaqGacFs" + gitlab_rails['smtp_domain'] = "aitrainer.app" + gitlab_rails['smtp_authentication'] = "login" + gitlab_rails['smtp_enable_starttls_auto'] = true + gitlab_rails['smtp_openssl_verify_mode'] = 'peer' + # Add any other gitlab.rb configuration here, each on its own line + gitlab_rails['gitlab_shell_ssh_port'] = 6622 + ports: + - '8929:8929' + - '443:443' + - '6622:22' + - '587:587' + volumes: + - '/srv/gitlab/config:/etc/gitlab' + - '/srv/gitlab/logs:/var/log/gitlab' + - '/srv/gitlab/data:/var/opt/gitlab' + + mysql: + image: mysql:latest + volumes: + - mysql-vol-0:/var/lib/mysql0 + restart: always + ports: + - 33061:33061 + environment: + MYSQL_ROOT_PASSWORD: /run/secrets/mysql_root_pwd + MYSQL_DATABASE: aitrainer + MYSQL_USER: aitrainer + MYSQL_PASSWORD: /run/secrets/mysql_user_pwd + networks: + - bosi_default + + phpmyadmin: + depends_on: + - mysql + image: phpmyadmin/phpmyadmin + restart: always + ports: + - '80:80' + environment: + PMA_HOST: mysql + MYSQL_ROOT_PASSWORD: andio2009 + networks: + - bosi_default + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /srv/gitlab-runner/config:/etc/gitlab-runner + + +networks: + bosi_default: + mailcow-network: + driver: bridge + driver_opts: + com.docker.network.bridge.name: br-mailcow + enable_ipv6: true + ipam: + driver: default + config: + - subnet: ${IPV4_NETWORK:-172.22.1}.0/24 + - subnet: ${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} + +volumes: + # Storage for email files + vmail-vol-1: + # Storage for attachments (deduplicated) + vmail-attachments-vol-1: + mysql-vol-1: + mysql-vol-0: + mysql-socket-vol-1: + redis-vol-1: + rspamd-vol-1: + solr-vol-1: + postfix-vol-1: + crypt-vol-1: + sogo-web-vol-1: + sogo-userdata-backup-vol-1: + php: + +secrets: + mysql_root_pwd: + file: /.sec/mysql_root_pwd + mysql_user_pwd: + file: /.sec/mysql_user_pwd + + diff --git a/data/config/docker-compose_gitlab.yml b/data/config/docker-compose_gitlab.yml new file mode 100644 index 0000000..c79b49d --- /dev/null +++ b/data/config/docker-compose_gitlab.yml @@ -0,0 +1,143 @@ +version: '3.8' +services: + + demo: + image: ehazlett/docker-demo + deploy: + replicas: 1 + labels: + com.docker.lb.hosts: aitrainer.app + com.docker.lb.network: bosi-network + com.docker.lb.port: 8080 + com.docker.lb.ssl_cert: demo_app.example.org.cert + com.docker.lb.ssl_key: demo_app.example.org.key + environment: + METADATA: proxy-handles-tls + networks: + - demo-network + + gitlab: + image: 'gitlab/gitlab-ce:latest' + container_name: 'gitlab' + restart: always + hostname: 'localhost' + environment: + GITLAB_OMNIBUS_CONFIG: | + external_url 'https://aitrainer.app' + gitlab_rails['smtp_enable'] = true + gitlab_rails['smtp_address'] = "email-smtp.eu-west-1.amazonaws.com" + gitlab_rails['smtp_port'] = 587 + gitlab_rails['smtp_user_name'] = "AKIAIWHHQDMPADT7ETHQ" + gitlab_rails['smtp_password'] = "AjCB8NA+61i/URp09gik0HHtbEuy48e4JXhuPaqGacFs" + gitlab_rails['smtp_domain'] = "aitrainer.app" + gitlab_rails['smtp_authentication'] = "login" + gitlab_rails['smtp_enable_starttls_auto'] = true + gitlab_rails['smtp_openssl_verify_mode'] = 'peer' + # Add any other gitlab.rb configuration here, each on its own line + gitlab_rails['gitlab_shell_ssh_port'] = 6622 + ports: + - '80:80' + - '443:443' + - '6622:22' + - '587:587' + volumes: + - '/srv/gitlab/config:/etc/gitlab' + - '/srv/gitlab/logs:/var/log/gitlab' + - '/srv/gitlab/data:/var/opt/gitlab' + mysql: + image: mysql:latest + volumes: + - db_data:/var/lib/mysql_aitrainer + restart: always + ports: + - 33061:33061 + environment: + MYSQL_ROOT_PASSWORD: /run/secrets/mysql_root_pwd + MYSQL_DATABASE: aitrainer + MYSQL_USER: aitrainer + MYSQL_PASSWORD: /run/secrets/mysql_user_pwd + networks: + - bosi_default + + phpmyadmin: + depends_on: + - mysql + image: phpmyadmin/phpmyadmin + restart: always + ports: + - '8081:80' + environment: + PMA_HOST: mysql + MYSQL_ROOT_PASSWORD: andio2009 + networks: + - bosi_default + php: + image: php:7.2-fpm + volumes: + - php:/var/www/html + - ./php/php.ini:/usr/local/etc/php/php.ini + depends_on: + - mysql + gitlab-runner: + image: gitlab/gitlab-runner:latest + container_name: gitlab-runner + restart: always + networks: + - bosi_default + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /srv/gitlab-runner/config:/etc/gitlab-runner +secrets: + mysql_root_pwd: + file: /.sec/mysql_root_pwd + mysql_user_pwd: + file: /.sec/mysql_user_pwd +networks: + bosi_default: +volumes: + db_data: + php: + + + + openssl req \ + -new \ + -newkey rsa:4096 \ + -days 3650 \ + -nodes \ + -x509 \ + -subj "/C=US/ST=CA/L=SF/O=Docker-demo/CN=aitrainer.app" \ + -keyout aitrainer.app.key \ + -out aitrainer.app.cert + +version: "3.2" + +services: + demo: + image: proxy + command: --tls-cert=/run/secrets/cert.pem --tls-key=/run/secrets/key.pem + deploy: + replicas: 1 + labels: + com.docker.lb.hosts: aitrainer.app + com.docker.lb.network: proxy-network + com.docker.lb.port: 8029 + com.docker.lb.ssl_passthrough: "true" + environment: + METADATA: end-to-end-TLS + networks: + - proxy-network + secrets: + - source: aitrainer.app.cert + target: /run/secrets/cert.pem + - source: aitrainer.app.org.key + target: /run/secrets/key.pem + +networks: + demo-network: + driver: overlay +secrets: + aitrainer.app.cert: + file: ./aitrainer.app.cert + aitrainer.app.key: + file: ./aitrainer.app.key \ No newline at end of file diff --git a/data/config/gitlab-nginx-ssl.conf b/data/config/gitlab-nginx-ssl.conf new file mode 100644 index 0000000..ece3736 --- /dev/null +++ b/data/config/gitlab-nginx-ssl.conf @@ -0,0 +1,113 @@ +## GitLab +## +## Modified from nginx http version +## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/ +## Modified from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html +## +## Lines starting with two hashes (##) are comments with information. +## Lines starting with one hash (#) are configuration parameters that can be uncommented. +## +################################## +## CONTRIBUTING ## +################################## +## +## If you change this file in a Merge Request, please also create +## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +## +################################### +## configuration ## +################################### +## +## See installation.md#using-https for additional HTTPS configuration details. + +upstream gitlab-workhorse { + server unix:/srv/gitlab/gitlab-workhorse/socket fail_timeout=0; +} + +## Redirects all HTTP traffic to the HTTPS host +server { + ## Either remove "default_server" from the listen line below, + ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab + ## to be served if you visit any address that your server responds to, eg. + ## the ip address of the server (http://x.x.x.x/) + listen 0.0.0.0:80; + listen [::]:80 ipv6only=on default_server; + server_name git.aitrainer.app ; ## Replace this with something like gitlab.example.com + server_tokens off; ## Don't show the nginx version number, a security best practice + return 301 https://$http_host$request_uri; + access_log /var/log/nginx/gitlab_access.log; + error_log /var/log/nginx/gitlab_error.log; +} + +## HTTPS host +server { + listen 0.0.0.0:443 ssl; + listen [::]:443 ipv6only=on ssl default_server; + server_name git.aitrainer.app ; ## Replace this with something like gitlab.example.com + server_tokens off; ## Don't show the nginx version number, a security best practice + root /opt/gitlab/embedded/service/gitlab-rails/public; + + ## Strong SSL Security + ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ + ssl on; + #ssl_certificate /etc/nginx/ssl/gitlab.crt; + #ssl_certificate_key /etc/nginx/ssl/gitlab.key; + ssl_certificate /etc/letsencrypt/live/git.aitrainer.app/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/git.aitrainer.app/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot + + # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs + ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 5m; + + ## See app/controllers/application_controller.rb for headers set + + ## [Optional] Enable HTTP Strict Transport Security + ## HSTS is a feature improving protection against MITM attacks + ## For more information see: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ + # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + + ## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL. + ## Replace with your ssl_trusted_certificate. For more info see: + ## - https://medium.com/devops-programming/4445f4862461 + ## - https://www.ruby-forum.com/topic/4419319 + ## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx + # ssl_stapling on; + # ssl_stapling_verify on; + # ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt; + # resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired + # resolver_timeout 5s; + + ## [Optional] Generate a stronger DHE parameter: + ## sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096 + ## + # ssl_dhparam /etc/ssl/certs/dhparam.pem; + + ## Individual nginx logs for this GitLab vhost + access_log /var/log/nginx/gitlab_access.log; + error_log /var/log/nginx/gitlab_error.log; + + location / { + client_max_body_size 0; + gzip off; + + ## https://github.com/gitlabhq/gitlabhq/issues/694 + ## Some requests take more than 30 seconds. + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + + proxy_http_version 1.1; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://gitlab-workhorse; + } +} diff --git a/data/db/install.sql b/data/db/install.sql new file mode 100644 index 0000000..b64bcff --- /dev/null +++ b/data/db/install.sql @@ -0,0 +1,138 @@ +-- -------------------------------------------------------- +-- Host: 127.0.0.1 +-- Szerver verzió: 10.4.11-MariaDB - mariadb.org binary distribution +-- Szerver OS: Win64 +-- HeidiSQL Verzió: 11.0.0.5919 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + +use aitrainer; + +-- Struktúra mentése tábla aitrainer. customer +CREATE TABLE IF NOT EXISTS `customer` ( + `customer_id` int(11) NOT NULL AUTO_INCREMENT, + `name` char(100) COLLATE utf8_hungarian_ci NOT NULL, + `firstname` char(100) COLLATE utf8_hungarian_ci NOT NULL, + `email` char(100) COLLATE utf8_hungarian_ci DEFAULT NULL, + `password` char(100) COLLATE utf8_hungarian_ci DEFAULT NULL, + `sex` enum('m','w') COLLATE utf8_hungarian_ci DEFAULT 'm', + `age` tinyint(4) DEFAULT NULL, + `active` enum('Y','N','D','S') COLLATE utf8_hungarian_ci DEFAULT 'N', + `date_add` datetime DEFAULT NULL, + `date_change` datetime DEFAULT NULL, + `data_policy_allowed` tinyint(4) DEFAULT 1, + `admin` tinyint(4) DEFAULT 0, + PRIMARY KEY (`customer_id`) +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.customer: ~13 rows (hozzávetőleg) +/*!40000 ALTER TABLE `customer` DISABLE KEYS */; +INSERT INTO `customer` (`customer_id`, `name`, `firstname`, `email`, `password`, `sex`, `age`, `active`, `date_add`, `date_change`, `data_policy_allowed`, `admin`) VALUES + (1, 'Átlag 13 éves fiú', '', NULL, NULL, 'm', 13, 'N', NULL, NULL, 1, 0), + (2, 'Átlag 14 éves fiú', '', NULL, NULL, 'm', 14, 'N', NULL, NULL, 1, 0), + (3, 'Átlag 15 éves fiú', '', NULL, NULL, 'm', 15, 'N', NULL, NULL, 1, 0), + (4, 'Átlag 15 éves fiú', '', NULL, NULL, 'm', 15, 'N', NULL, NULL, 1, 0), + (5, 'Átlag 16 éves fiú', '', NULL, NULL, 'm', 16, 'N', NULL, NULL, 1, 0), + (6, 'Átlag 17 éves fiú', '', NULL, NULL, 'm', 17, 'N', NULL, NULL, 1, 0), + (7, 'Átlag 18 éves fiú', '', NULL, NULL, 'm', 18, 'N', NULL, NULL, 1, 0), + (8, 'Átlag 13 éves lány', '', NULL, NULL, 'w', 13, 'N', NULL, NULL, 1, 0), + (9, 'Átlag 14 éves lány', '', NULL, NULL, 'w', 14, 'N', NULL, NULL, 1, 0), + (10, 'Átlag 15 éves lány', '', NULL, NULL, 'w', 15, 'N', NULL, NULL, 1, 0), + (11, 'Átlag 16 éves lány', '', NULL, NULL, 'w', 16, 'N', NULL, NULL, 1, 0), + (12, 'Átlag 17 éves lány', '', NULL, NULL, 'w', 17, 'N', NULL, NULL, 1, 0), + (13, 'Átlag 18 éves lány', '', NULL, NULL, 'w', 18, 'N', NULL, NULL, 1, 0); +/*!40000 ALTER TABLE `customer` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. customer_information +CREATE TABLE IF NOT EXISTS `customer_information` ( + `customer_information_id` int(11) NOT NULL AUTO_INCREMENT, + `title` char(50) COLLATE utf8_hungarian_ci DEFAULT '', + `description` mediumtext COLLATE utf8_hungarian_ci DEFAULT NULL, + `date_add` datetime DEFAULT NULL, + `display_begin` datetime DEFAULT NULL, + `display_end` datetime DEFAULT NULL, + PRIMARY KEY (`customer_information_id`) USING BTREE, + KEY `title` (`title`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.customer_information: ~0 rows (hozzávetőleg) +/*!40000 ALTER TABLE `customer_information` DISABLE KEYS */; +/*!40000 ALTER TABLE `customer_information` ENABLE KEYS */; + +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (1, 'Fekvőtámasz világcsúcs', 'Világcsúcs fekvőtámasz: KJ Joseph 1 perc alatt 82 szabályos fekvőtámaszt végzett', '2020-06-01 08:00:00', '2020-06-01 08:00:00', '2023-07-01 08:00:00'); +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (2, 'Húzódszkodás csúcs', '24 órás csúcstartója Joonas Mäkipelto 5050 gyakorlattal', '2020-06-01 08:00:00', '2020-06-01 08:00:00', '2023-07-01 08:00:00'); +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (3, 'Fekvenyomás', '2015-ben a fekvenyomó világbajnokságot Smulter Fredrik finn súlyemelő 401 Kg-al nyerte', '2020-06-01 08:00:00', '2020-05-01 00:00:00', '2020-06-01 08:00:01'); + + +-- Struktúra mentése tábla aitrainer. exercises +CREATE TABLE IF NOT EXISTS `exercises` ( + `exercise_id` int(11) NOT NULL AUTO_INCREMENT, + `exercise_type_id` int(11) NOT NULL, + `customer_id` int(11) NOT NULL, + `date_add` datetime NOT NULL, + `quantity` float DEFAULT NULL, + `unit` enum('kg','meter','repeat','minute') COLLATE utf8_hungarian_ci DEFAULT 'repeat', + `rest_time` int(11) DEFAULT NULL COMMENT 'in sec', + PRIMARY KEY (`exercise_id`), + KEY `exercise_type_id` (`exercise_type_id`), + KEY `customer_id` (`customer_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercises: ~0 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercises` DISABLE KEYS */; +INSERT INTO `exercises` (`exercise_id`, `exercise_type_id`, `customer_id`, `date_add`, `quantity`, `unit`, `rest_time`) VALUES + (1, 1, 1, '2020-05-01 00:00:00', 12, 'repeat', NULL); +/*!40000 ALTER TABLE `exercises` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. exercise_evaluation +CREATE TABLE IF NOT EXISTS `exercise_evaluation` ( + `evaluation_id` int(11) NOT NULL AUTO_INCREMENT, + `age_min` int(11) DEFAULT 0, + `age_max` int(11) DEFAULT 0, + `value_min` int(11) DEFAULT 0, + `value_max` int(11) DEFAULT 0, + `sex` enum('m','w') COLLATE utf8_hungarian_ci NOT NULL DEFAULT 'm', + `evaluation` enum('excellent','very good','good','average','weak','poor') COLLATE utf8_hungarian_ci NOT NULL DEFAULT 'average', + `description` mediumtext COLLATE utf8_hungarian_ci DEFAULT NULL, + PRIMARY KEY (`evaluation_id`) USING BTREE, + KEY `value_min_value_max` (`value_min`,`value_max`) USING BTREE, + KEY `age_min_age_max` (`age_min`,`age_max`) USING BTREE, + KEY `age_min_age_max_value_min_value_max` (`age_min`,`age_max`,`value_min`,`value_max`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercise_evaluation: ~0 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercise_evaluation` DISABLE KEYS */; +/*!40000 ALTER TABLE `exercise_evaluation` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. exercise_type +CREATE TABLE IF NOT EXISTS `exercise_type` ( + `exercise_type_id` int(11) NOT NULL AUTO_INCREMENT, + `name` char(100) COLLATE utf8_hungarian_ci NOT NULL, + `description` varchar(1000) COLLATE utf8_hungarian_ci DEFAULT NULL, + `video` mediumblob DEFAULT NULL, + PRIMARY KEY (`exercise_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercise_type: ~11 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercise_type` DISABLE KEYS */; +INSERT INTO `exercise_type` (`exercise_type_id`, `name`, `description`, `video`) VALUES + (1, 'Melső fekvőtámasz 1 perc', 'Ezt igazolja a 2016 márciusában beállított guinness rekord is,\r\namelyben KJ Joseph 1 perc alatt 82 szabályos karhajlítás-nyújtást\r\nvégzett.', NULL), + (2, 'Húzódzkodás', 'Ennek a gyakorlatnak a 24 órás csúcstartója Joonas Mäkipelto 5050\r\ngyakorlattal.', NULL), + (3, 'Melső fekvőtámasz 30mp', 'A gyakorlatot 30 másodperc alatt olyan sokszor kell végrehajtani, ahányszor a\r\nfelvételiző képes rá. Azonban törekedni kell a szabályos végrehajtásra, ugyanis csak azokat\r\nszámolják. A nők esetében 20, a férfiak esetében 35 gyakorlatot kell végrehajtani a maximális\r\npont megszerzéséért. A gyakorlat akkor sikeres, ha a női legalább 1, a férfi felvételiző\r\nlegalább 11 gyakorlatot képes végrehajtani.', NULL), + (4, 'Melső fekvőtámasz 2perc', 'Magyar Honvédség: A gyakorlat végrehajtására 2 perc áll rendelkezésre. Ennek során csak a szabályosan a\r\nfentiekben leírt módon végrehajtott gyakorlat értékelhető. Férfiaknál 70 karhajlítás nyújtást\r\nkell végrehajtani a maximális pontért.', NULL), + (5, 'Hajlított karú függés', 'A gyakorlat addig tart, amíg a végrehajtó szemmagassága a kiinduló helyzettől\r\nsüllyedve a keresztvas alá nem kerül. Az értékeléshez stopperórát alkalmaznak, és az\r\neredmény másodperc pontossággal kerül megállapításra. Nők esetében 45, férfiak\r\ntekintetében 73 másodperctől jár a maximális pontszám. A gyakorlat sikeres végrehajtásához\r\nlegalább 8, 10 másodpercig kell megtartaniuk a kiinduló helyzetet a női és a férfi\r\nfelvételizőknek.\r\n\r\nA NKE-RTK-án lévő hallgatók egyéni rekordjai Iván Viktor 90s, Kiss Regina 74s', NULL), + (6, 'Fekvenyomás', '2015-ben a fekvenyomó világbajnokságot Smulter Fredrik finn súlyemelő 401 Kg-al nyerte.\r\nA súlyzó tömege nők esetében 25 kg, a férfiak esetében 60 kg a rúddal együtt. Az\r\nértékelésénél a szabályosan végrehajtott gyakorlatokat értékelik csak.\r\nA legtöbb pontért 25 gyakorlatot kell végezni mind a nőknek, mind a férfiaknak. A minimum:\r\negy gyakorlat mindkét nem esetében.', NULL), + (7, '4x10m-es ingafutás', 'A legjobb pontszámért 9,4 s illetve 8,8s alatt kell teljesíteni a nőknek, férfiaknak. A\r\ngyakorlat sikertelen 11,8 s illetve 11,2 s-on túl.', NULL), + (8, 'Helyből távolugrás', 'Byron Jones 2015-ben a 3,73 méteres ugrásával érte el a világcsúcsot.\r\nA nőknek 220 cm-re, a férfiak 250 cm-re kell ugraniuk a maximális pontért. A\r\nminimális távolság 172cm illetve 198 cm.', NULL), + (9, 'Felülés hanyattfekvésből', 'Az NKE-RTK hallgatói közül Papp Zsófia 66 db-ot, Gál Valentin 80\r\ndb-ot csinált 1 perc leforgása alatt.\r\nElső ütemre megtörténik a felülés, ami akkor szabályos, ha valamelyik könyök érinti a\r\ntérdet. Második ütemre vissza kell térni a kiinduló helyzetbe. A maximális pont eléréséhez 1\r\nperc alatt 45 illetve 55 ismétlést kell végezni a nőknek illetve a férfiaknak. A minimumhoz 7\r\nés 25 ismétlés szükséges.', NULL), + (10, 'Felülés hajlított térddel', 'Magyar Honvédség: A kiinduló helyzet hajlított ülés, ennek során a sarkak a talajon, a térd 90 fokban meghajlítva\r\nvan.\r\nA gyakorlat végrehajtására két perc áll rendelkezésre. Ennek során csak a szabályosan,\r\na fentiekben leírt módon végrehajtott gyakorlat értékelhető. Férfiaknál, nőknél egyaránt 90\r\ngyakorlatot kell végrehajtani a maximális pontért. Amennyiben a megadott időkeret alatt nem\r\nsikerül legalább 25 szabályos gyakorlatot végrehajtani, úgy az sikertelennek minősül.', NULL), + (11, 'Síkfutás 2000m', 'A maximálisan megszerezhető pontot az a felvételiző gyűjtheti be, aki a távot nők\r\nesetében 10:00 perc alatt, férfiak esetében 7:35 perc alatt teljesíti. A gyakorlat sikeres\r\nteljesítésére nők esetében maximum 16:00 perc, férfiak esetében 13:30 perc áll rendelkezésre.', NULL); +/*!40000 ALTER TABLE `exercise_type` ENABLE KEYS */; + +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/data/db/install_0_0_1.sql b/data/db/install_0_0_1.sql new file mode 100644 index 0000000..36f5ee2 --- /dev/null +++ b/data/db/install_0_0_1.sql @@ -0,0 +1,115 @@ +-- -------------------------------------------------------- +-- Host: 127.0.0.1 +-- Szerver verzió: 8.0.20 - MySQL Community Server - GPL +-- Szerver OS: Win64 +-- HeidiSQL Verzió: 11.0.0.5919 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + +use aitrainer; + +-- Struktúra mentése tábla aitrainer. customer +CREATE TABLE IF NOT EXISTS `customer` ( + `customer_id` int NOT NULL AUTO_INCREMENT, + `name` char(100) NOT NULL, + `firstname` char(100) NOT NULL, + `email` char(100) DEFAULT NULL, + `sex` enum('m','w') DEFAULT 'm', + `age` tinyint DEFAULT NULL, + `active` enum('Y','N','D','S') DEFAULT 'N', + PRIMARY KEY (`customer_id`) +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.customer: ~15 rows (hozzávetőleg) +/*!40000 ALTER TABLE `customer` DISABLE KEYS */; +INSERT INTO `customer` (`customer_id`, `name`, `firstname`, `email`, `sex`, `age`) VALUES + (1, 'Átlag 13 éves fiú', '', NULL, 'm', 13), + (2, 'Átlag 14 éves fiú', '', NULL, 'm', 14), + (3, 'Átlag 15 éves fiú', '', NULL, 'm', 15), + (4, 'Átlag 15 éves fiú', '', NULL, 'm', 15), + (5, 'Átlag 16 éves fiú', '', NULL, 'm', 16), + (6, 'Átlag 17 éves fiú', '', NULL, 'm', 17), + (7, 'Átlag 18 éves fiú', '', NULL, 'm', 18), + (8, 'Átlag 13 éves lány', '', NULL, 'w', 13), + (9, 'Átlag 14 éves lány', '', NULL, 'w', 14), + (10, 'Átlag 15 éves lány', '', NULL, 'w', 15), + (11, 'Átlag 16 éves lány', '', NULL, 'w', 16), + (12, 'Átlag 17 éves lány', '', NULL, 'w', 17), + (13, 'Átlag 18 éves lány', '', NULL, 'w', 18); +/*!40000 ALTER TABLE `customer` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. exercises +CREATE TABLE IF NOT EXISTS `exercises` ( + `exercise_id` int NOT NULL AUTO_INCREMENT, + `exercise_type_id` int NOT NULL, + `customer_id` int NOT NULL, + `datetime_exercise` datetime NOT NULL, + `quantity` float DEFAULT NULL, + `rest_time` int DEFAULT NULL COMMENT 'in sec', + PRIMARY KEY (`exercise_id`), + KEY `exercise_type_id` (`exercise_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercises: ~1 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercises` DISABLE KEYS */; +INSERT INTO `exercises` (`exercise_id`, `exercise_type_id`, `customer_id`, `datetime_exercise`, `quantity`, `rest_time`) VALUES + (1, 1, 1, '2020-05-01 00:00:00', 12, NULL); +/*!40000 ALTER TABLE `exercises` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. exercise_ages +CREATE TABLE IF NOT EXISTS `exercise_ages` ( + `exercise_age_id` int NOT NULL AUTO_INCREMENT, + `exercise_type_id` int NOT NULL, + `name` char(100) NOT NULL, + `sex` enum('m','w') DEFAULT 'm', + `age` tinyint DEFAULT NULL, + `min_exercises` int DEFAULT NULL, + `avg_exercises` int DEFAULT NULL, + `max_exercises` int DEFAULT NULL, + PRIMARY KEY (`exercise_age_id`), + UNIQUE KEY `exercise_type_id_2` (`exercise_type_id`,`sex`,`age`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercise_ages: ~6 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercise_ages` DISABLE KEYS */; +INSERT INTO `exercise_ages` (`exercise_age_id`, `exercise_type_id`, `name`, `sex`, `age`, `min_exercises`, `avg_exercises`, `max_exercises`) VALUES + (1, 1, '', 'm', 13, 12, NULL, NULL), + (2, 1, '', 'm', 14, 14, NULL, NULL), + (3, 1, '', 'm', 15, 16, NULL, NULL), + (4, 1, '', 'm', 16, 18, NULL, NULL), + (7, 1, '', 'm', 17, 18, NULL, NULL), + (8, 1, '', 'm', 18, 18, NULL, NULL); +/*!40000 ALTER TABLE `exercise_ages` ENABLE KEYS */; + +-- Struktúra mentése tábla aitrainer. exercise_types +CREATE TABLE IF NOT EXISTS `exercise_type` ( + `exercise_type_id` int NOT NULL AUTO_INCREMENT, + `name` char(100) NOT NULL, + `description` varchar(1000) DEFAULT NULL, + `video` mediumblob, + PRIMARY KEY (`exercise_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci; + +-- Tábla adatainak mentése aitrainer.exercise_types: ~11 rows (hozzávetőleg) +/*!40000 ALTER TABLE `exercise_type` DISABLE KEYS */; +INSERT INTO `exercise_type` (`exercise_type_id`, `name`, `description`, `video`) VALUES + (1, 'Melső fekvőtámasz 1 perc', 'Ezt igazolja a 2016 márciusában beállított guinness rekord is,\r\namelyben KJ Joseph 1 perc alatt 82 szabályos karhajlítás-nyújtást\r\nvégzett.', NULL), + (2, 'Húzódzkodás', 'Ennek a gyakorlatnak a 24 órás csúcstartója Joonas Mäkipelto 5050\r\ngyakorlattal.', NULL), + (3, 'Melső fekvőtámasz 30mp', 'A gyakorlatot 30 másodperc alatt olyan sokszor kell végrehajtani, ahányszor a\r\nfelvételiző képes rá. Azonban törekedni kell a szabályos végrehajtásra, ugyanis csak azokat\r\nszámolják. A nők esetében 20, a férfiak esetében 35 gyakorlatot kell végrehajtani a maximális\r\npont megszerzéséért. A gyakorlat akkor sikeres, ha a női legalább 1, a férfi felvételiző\r\nlegalább 11 gyakorlatot képes végrehajtani.', NULL), + (4, 'Melső fekvőtámasz 2perc', 'Magyar Honvédség: A gyakorlat végrehajtására 2 perc áll rendelkezésre. Ennek során csak a szabályosan a\r\nfentiekben leírt módon végrehajtott gyakorlat értékelhető. Férfiaknál 70 karhajlítás nyújtást\r\nkell végrehajtani a maximális pontért.', NULL), + (5, 'Hajlított karú függés', 'A gyakorlat addig tart, amíg a végrehajtó szemmagassága a kiinduló helyzettől\r\nsüllyedve a keresztvas alá nem kerül. Az értékeléshez stopperórát alkalmaznak, és az\r\neredmény másodperc pontossággal kerül megállapításra. Nők esetében 45, férfiak\r\ntekintetében 73 másodperctől jár a maximális pontszám. A gyakorlat sikeres végrehajtásához\r\nlegalább 8, 10 másodpercig kell megtartaniuk a kiinduló helyzetet a női és a férfi\r\nfelvételizőknek.\r\n\r\nA NKE-RTK-án lévő hallgatók egyéni rekordjai Iván Viktor 90s, Kiss Regina 74s', NULL), + (6, 'Fekvenyomás', '2015-ben a fekvenyomó világbajnokságot Smulter Fredrik finn súlyemelő 401 Kg-al nyerte.\r\nA súlyzó tömege nők esetében 25 kg, a férfiak esetében 60 kg a rúddal együtt. Az\r\nértékelésénél a szabályosan végrehajtott gyakorlatokat értékelik csak.\r\nA legtöbb pontért 25 gyakorlatot kell végezni mind a nőknek, mind a férfiaknak. A minimum:\r\negy gyakorlat mindkét nem esetében.', NULL), + (7, '4x10m-es ingafutás', 'A legjobb pontszámért 9,4 s illetve 8,8s alatt kell teljesíteni a nőknek, férfiaknak. A\r\ngyakorlat sikertelen 11,8 s illetve 11,2 s-on túl.', NULL), + (8, 'Helyből távolugrás', 'Byron Jones 2015-ben a 3,73 méteres ugrásával érte el a világcsúcsot.\r\nA nőknek 220 cm-re, a férfiak 250 cm-re kell ugraniuk a maximális pontért. A\r\nminimális távolság 172cm illetve 198 cm.', NULL), + (9, 'Felülés hanyattfekvésből', 'Az NKE-RTK hallgatói közül Papp Zsófia 66 db-ot, Gál Valentin 80\r\ndb-ot csinált 1 perc leforgása alatt.\r\nElső ütemre megtörténik a felülés, ami akkor szabályos, ha valamelyik könyök érinti a\r\ntérdet. Második ütemre vissza kell térni a kiinduló helyzetbe. A maximális pont eléréséhez 1\r\nperc alatt 45 illetve 55 ismétlést kell végezni a nőknek illetve a férfiaknak. A minimumhoz 7\r\nés 25 ismétlés szükséges.', NULL), + (10, 'Felülés hajlított térddel', 'Magyar Honvédség: A kiinduló helyzet hajlított ülés, ennek során a sarkak a talajon, a térd 90 fokban meghajlítva\r\nvan.\r\nA gyakorlat végrehajtására két perc áll rendelkezésre. Ennek során csak a szabályosan,\r\na fentiekben leírt módon végrehajtott gyakorlat értékelhető. Férfiaknál, nőknél egyaránt 90\r\ngyakorlatot kell végrehajtani a maximális pontért. Amennyiben a megadott időkeret alatt nem\r\nsikerül legalább 25 szabályos gyakorlatot végrehajtani, úgy az sikertelennek minősül.', NULL), + (11, 'Síkfutás 2000m', 'A maximálisan megszerezhető pontot az a felvételiző gyűjtheti be, aki a távot nők\r\nesetében 10:00 perc alatt, férfiak esetében 7:35 perc alatt teljesíti. A gyakorlat sikeres\r\nteljesítésére nők esetében maximum 16:00 perc, férfiak esetében 13:30 perc áll rendelkezésre.', NULL); +/*!40000 ALTER TABLE `exercise_type` ENABLE KEYS */; + +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/data/db/update_0_0_2.sql b/data/db/update_0_0_2.sql new file mode 100644 index 0000000..0f8d31d --- /dev/null +++ b/data/db/update_0_0_2.sql @@ -0,0 +1,55 @@ +ALTER TABLE `exercises` +ADD COLUMN `unit` ENUM('kg','meter','repeat','minute') NULL DEFAULT 'repeat' AFTER `quantity`, +CHANGE COLUMN `quantity` `quantity` FLOAT NULL DEFAULT NULL AFTER `datetime_exercise`; + +ALTER TABLE `exercises` +CHANGE COLUMN `datetime_exercise` `date_add` DATETIME NOT NULL AFTER `customer_id`, +ADD INDEX `customer_id` (`customer_id`); + +ALTER TABLE `customer` + ADD COLUMN `password` CHAR(100) NULL DEFAULT NULL AFTER `email`, + ADD COLUMN `date_add` DATETIME NULL AFTER `active`, + ADD COLUMN `date_change` DATETIME NULL AFTER `date_add`, + ADD COLUMN `data_policy_allowed` TINYINT NULL DEFAULT '1' AFTER `date_change`, + ADD COLUMN `admin` TINYINT NULL DEFAULT '0' AFTER `data_policy_allowed`; + +CREATE TABLE `exercise_evaluation` ( + `evaluation_id` INT(11) NOT NULL AUTO_INCREMENT, + `age_min` INT(11) NULL DEFAULT '0', + `age_max` INT(11) NULL DEFAULT '0', + `value_min` INT(11) NULL DEFAULT '0', + `value_max` INT(11) NULL DEFAULT '0', + `sex` ENUM('m','w') NOT NULL DEFAULT 'm' COLLATE 'utf8_hungarian_ci', + `evaluation` ENUM('excellent','very good','good','average','weak','poor') NOT NULL DEFAULT 'average' COLLATE 'utf8_hungarian_ci', + `description` TEXT(65535) NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + PRIMARY KEY (`evaluation_id`) USING BTREE, + INDEX `value_min_value_max` (`value_min`, `value_max`) USING BTREE, + INDEX `age_min_age_max` (`age_min`, `age_max`) USING BTREE, + INDEX `age_min_age_max_value_min_value_max` (`age_min`, `age_max`, `value_min`, `value_max`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + +CREATE TABLE `customer_information` ( + `customer_information_id` INT(11) NOT NULL AUTO_INCREMENT, + `title` CHAR(50) NULL DEFAULT '' COLLATE 'utf8_hungarian_ci', + `description` TEXT(65535) NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + `date_add` DATETIME NULL DEFAULT NULL, + `display_begin` DATETIME NULL DEFAULT NULL, + `display_end` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`customer_information_id`) USING BTREE, + INDEX `title` (`title`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (1, 'Fekvőtámasz világcsúcs', 'Világcsúcs fekvőtámasz: KJ Joseph 1 perc alatt 82 szabályos fekvőtámaszt végzett', '2020-06-01 08:00:00', '2020-06-01 08:00:00', '2023-07-01 08:00:00'); +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (2, 'Húzódszkodás csúcs', '24 órás csúcstartója Joonas Mäkipelto 5050 gyakorlattal', '2020-06-01 08:00:00', '2020-06-01 08:00:00', '2023-07-01 08:00:00'); +INSERT INTO `customer_information` (`customer_information_id`, `title`, `description`, `date_add`, `display_begin`, `display_end`) VALUES (3, 'Fekvenyomás', '2015-ben a fekvenyomó világbajnokságot Smulter Fredrik finn súlyemelő 401 Kg-al nyerte', '2020-06-01 08:00:00', '2020-05-01 00:00:00', '2020-06-01 08:00:01'); + +DROP TABLE exercise_ages; + +UPDATE configuration set config_value = "0.0.2" WHERE config_key = "db_version"; + diff --git a/data/db/update_0_0_3.sql b/data/db/update_0_0_3.sql new file mode 100644 index 0000000..4e4cbfb --- /dev/null +++ b/data/db/update_0_0_3.sql @@ -0,0 +1 @@ +INSERT INTO `customer` (`name`, `firstname`, `email`, `password`, `sex`, `age`, `active`, `date_add`, `date_change`, `data_policy_allowed`, `admin`) VALUES ('Dummy User', NULL, 'bosi', '$2a$10$thOc8jS750c7xe9U9Qq3GuSPs/H0Pt2Ads05yzUlyzQBIj.Rk9QCy', 'm', 40, 'N', NULL, NULL, 1, 1); diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c6b040f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8294cac --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,102 @@ +@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=. +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%" == "0" goto init + +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 init + +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 + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +: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 %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/readme.MD b/readme.MD new file mode 100644 index 0000000..451fa34 --- /dev/null +++ b/readme.MD @@ -0,0 +1,13 @@ +#aitrainer server API v0.0.5 + +connects the MYSQL Database +provide a RESTful API for the mobile app + +##finished API + +* customers +* exercise_type +* exercise +* customer_information + +with automatic database update diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c500055 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':src' +include ':data' \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/ApiApplication.kt b/src/main/kotlin/com/aitrainer/api/ApiApplication.kt new file mode 100644 index 0000000..00ffe92 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/ApiApplication.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api + +import org.slf4j.LoggerFactory +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.builder.SpringApplicationBuilder + +@SpringBootApplication +class ApiApplication + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Override + fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder { + return application.sources(ApiApplication::class.java) + } + + fun main(args: Array) { + logger.info(" ---- Start aitrainer API") + SpringApplication.run(ApiApplication::class.java, *args) + } + diff --git a/src/main/kotlin/com/aitrainer/api/controller/ApplicationProperties.kt b/src/main/kotlin/com/aitrainer/api/controller/ApplicationProperties.kt new file mode 100644 index 0000000..927c727 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ApplicationProperties.kt @@ -0,0 +1,45 @@ +package com.aitrainer.api.controller + +import org.springframework.beans.factory.annotation.Value +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping +class ApplicationProperties { + + @Value("\${application.version}") + private lateinit var version: String + + @Value("\${spring.datasource.url}") + private lateinit var datasourceUrl: String + + @Value("\${spring.datasource.username}") + private lateinit var datasourceUsername: String + + @Value("\${spring.datasource.password}") + private lateinit var datasourcePassword: String + + + + @GetMapping("/version") + fun getVersion(): String { + return this.version + } + + @GetMapping("/datasourceUrl") + fun getDatasourceUrl(): String { + return this.datasourceUrl + } + + @GetMapping("/datasourceUsername") + fun getDatasourceUsername(): String { + return this.datasourceUsername + } + + @GetMapping("/datasourcePassword") + fun getDatasourcePassword(): String { + return this.datasourcePassword + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/ConfigurationController.kt b/src/main/kotlin/com/aitrainer/api/controller/ConfigurationController.kt new file mode 100644 index 0000000..98e16fc --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ConfigurationController.kt @@ -0,0 +1,27 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.model.Configuration + +import com.aitrainer.api.repository.ConfigurationRepository +import org.springframework.web.bind.annotation.RestController +import java.time.LocalDateTime + +@RestController +class ConfigurationController ( private val configurationRepository: ConfigurationRepository) { + + fun getConfiguration(key: String): Configuration = + configurationRepository.findByConfigKey(key) + + fun updateConfiguration( newConfiguration: Configuration): Configuration { + val existingConfiguration: Configuration = configurationRepository.findByConfigKey(newConfiguration.configKey) + val updatedConfiguration: Configuration = existingConfiguration.copy( + configKey = newConfiguration.configKey, + configValue = newConfiguration.configValue, + //dateAdd = newConfiguration.dateAdd, + dateChange = LocalDateTime.now().toString()) + + configurationRepository.save(updatedConfiguration) + return existingConfiguration + } + +} diff --git a/src/main/kotlin/com/aitrainer/api/controller/CustomerController.kt b/src/main/kotlin/com/aitrainer/api/controller/CustomerController.kt new file mode 100644 index 0000000..93d068d --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/CustomerController.kt @@ -0,0 +1,116 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.model.Customer +import com.aitrainer.api.model.User +import com.aitrainer.api.service.ServiceBeans +import com.aitrainer.api.repository.CustomerRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.HttpHeaders +import org.springframework.http.ResponseEntity +import org.springframework.security.access.annotation.Secured +import org.springframework.web.bind.annotation.* +import javax.validation.Valid + + +@RestController +@RequestMapping("/api") +class CustomerController ( private val customerRepository: CustomerRepository ) { + + @Autowired + var serviceBeans: ServiceBeans? = null + + @Secured + @GetMapping("/customers") + fun getAllCustomers(@RequestHeader headers: HttpHeaders): List = + customerRepository.findAll() + + @Secured + @PostMapping("/customers") + fun createNewCustomer(@Valid @RequestBody customer: Customer, @RequestHeader headers: HttpHeaders): Customer = + customerRepository.save(customer) + + @Secured + @GetMapping("/customers/{id}") + fun getCustomerById(@PathVariable(value = "id") customerId: Long, @RequestHeader headers: HttpHeaders): ResponseEntity { + return customerRepository.findById(customerId).map { customer -> + ResponseEntity.ok(customer) + }.orElse(ResponseEntity.notFound().build()) + } + + + @Secured + @GetMapping("/customers/real") + fun getRealCustomers(active: String, @RequestHeader headers: HttpHeaders): List = + customerRepository.findByActive(active) + + @Secured + @PutMapping("/customers/{id}") + fun updateCustomerById(@PathVariable(value = "id") customerId: Long, + @Valid @RequestBody newCustomer: Customer, + @RequestHeader headers: HttpHeaders): ResponseEntity { + + return customerRepository.findById(customerId).map { existingCustomer -> + val updatedCustomer: Customer = existingCustomer + .copy(name = newCustomer.name, + firstname = newCustomer.firstname, + sex = newCustomer.sex, + age = newCustomer.age) + ResponseEntity.ok().body(customerRepository.save(updatedCustomer)) + }.orElse(ResponseEntity.notFound().build()) + } + + + @PostMapping("/registration") + fun registration(@Valid @RequestBody json: String): ResponseEntity<*> { + val customer = Customer() + + val newUser: User = User().fromJson(json) + with (customer) { + email = newUser.username + password = serviceBeans!!.passwordEncoder().encode(newUser.password) + } + + val returnCustomer: Customer? = customerRepository.findByEmail(newUser.username).let { + if ( it == null) { + customerRepository.save(customer) + } else { + null + } + } + + return if ( returnCustomer != null ) { + ResponseEntity.ok().body(returnCustomer) + } else { + ResponseEntity.badRequest().body("Customer exists") + } + + } + + @GetMapping("/login") + fun login(@Valid @RequestBody json: String): ResponseEntity<*> { + val customer = Customer() + + val newUser: User = User().fromJson(json) + with (customer) { + email = newUser.username + password = newUser.password + } + val returnCustomer: Customer? = customerRepository.findByEmail(newUser.username).let { + if ( it == null) { + null + } else { + if (serviceBeans!!.passwordEncoder().matches(newUser.password, it.password)) { + it + } else { + null + } + + } + } + return if ( returnCustomer != null ) { + ResponseEntity.ok().body(returnCustomer) + } else { + ResponseEntity.badRequest().body("Customer does not exist or the password is wrong") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/CustomerControllerAspect.kt b/src/main/kotlin/com/aitrainer/api/controller/CustomerControllerAspect.kt new file mode 100644 index 0000000..656fb28 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/CustomerControllerAspect.kt @@ -0,0 +1,30 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.repository.ConfigurationRepository +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Suppress("unused") +@Aspect +@Component +class CustomerControllerAspect { + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Before("execution(* com.aitrainer.api.controller.CustomerController.*(..))") + fun customerControllerAspect(joinPoint: JoinPoint) { + println("customer controller") + Singleton.checkDBUpdate(configurationRepository, properties) + } + +} + diff --git a/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationController.kt b/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationController.kt new file mode 100644 index 0000000..a5d9882 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationController.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.model.CustomerInformation +import com.aitrainer.api.repository.CustomerInformationRepository +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.time.LocalDateTime + +@RestController +@RequestMapping("/api") +class CustomerInformationController( private val customerInformationRepository: CustomerInformationRepository ) { + + @GetMapping("/customer_information") + fun getCustomerInformation(): List { + val dateTime: String = LocalDateTime.now().toString() + return customerInformationRepository.findByDisplayBeginLessThanAndDisplayEndGreaterThan(dateTime, dateTime ) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationControllerAspect.kt b/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationControllerAspect.kt new file mode 100644 index 0000000..93b3a09 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/CustomerInformationControllerAspect.kt @@ -0,0 +1,35 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.repository.ConfigurationRepository +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.aspectj.lang.annotation.Pointcut +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Aspect +@Component +@Suppress("unused") +class CustomerInformationControllerAspect { + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Suppress("unused") + @Pointcut("execution(* com.aitrainer.api.controller.CustomerInformationController.*())") + fun customerInformationAspect() { + } + + @Suppress("unused") + @Before("customerInformationAspect()") + fun dbCheckAop() { + Singleton.checkDBUpdate(configurationRepository, properties) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/DatabaseCheckSingleton.kt b/src/main/kotlin/com/aitrainer/api/controller/DatabaseCheckSingleton.kt new file mode 100644 index 0000000..6db8eda --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/DatabaseCheckSingleton.kt @@ -0,0 +1,102 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.model.Configuration +import com.aitrainer.api.repository.ConfigurationRepository +import org.slf4j.LoggerFactory +import org.springframework.web.bind.annotation.RestController +import java.io.File +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet +import java.util.Properties + +@RestController +object Singleton { + + private lateinit var connection: Connection + private lateinit var configurationRepository: ConfigurationRepository + private lateinit var properties: ApplicationProperties + + private var initialized: Boolean = false + + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + // to use the singleton functionality avoiding the db access before each request + private var dbVersion: String = "" + private var appVersion: String = "" + + fun checkDBUpdate(configurationRepository: ConfigurationRepository, properties: ApplicationProperties) { + + if ( dbVersion.isNotEmpty() && appVersion.isNotEmpty()) { + // no db access + //println("DB up-to-date, no DB access") + return + } + + this.configurationRepository = configurationRepository + this.properties = properties + + val dbConfig: Configuration = configurationRepository.findByConfigKey("db_version") + val applicationVersion: String = properties.getVersion() + + this.dbVersion = dbConfig.configValue + this.appVersion = applicationVersion + + if ( dbConfig.configValue != applicationVersion ) { + + try { + + + val versionNumber: String = applicationVersion.replace(".", "_") + val fileName = "update_$versionNumber.sql" + val path = "./data/db/" + val file2Update = path+fileName + + var sqlRows = "" + File(file2Update).forEachLine { + sqlRows += it + if ( sqlRows.contains(";") ) { + execSQL(sqlRows) + sqlRows = "" + } + } + this.connection.commit() + logger.info("Database has been updated to $applicationVersion" ) + } catch (exception: Exception) { + logger.info("Database exception of $applicationVersion: " + exception.message ) + this.connection.rollback() + } + } + + } + + private fun execSQL( sql: String ) { + if (! this.initialized ) { + this.getConnection() + } + + with(this.connection) { + createStatement().execute(sql) + //commit() + } + } + + fun execQuery( sql: String ): ResultSet { + if (! this.initialized ) { + this.getConnection() + } + + return connection.createStatement().executeQuery(sql) + } + + private fun getConnection(): Connection { + val connectionProps = Properties() + connectionProps["user"] = this.properties.getDatasourceUsername() + connectionProps["password"] = this.properties.getDatasourcePassword() + this.connection = DriverManager.getConnection(this.properties.getDatasourceUrl(), connectionProps) + this.connection.autoCommit = false + this.initialized = true + return this.connection + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/ExerciseController.kt b/src/main/kotlin/com/aitrainer/api/controller/ExerciseController.kt new file mode 100644 index 0000000..adda633 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ExerciseController.kt @@ -0,0 +1,43 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.model.Exercises +import com.aitrainer.api.repository.ExercisesRepository +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import javax.validation.Valid + +@RestController +@RequestMapping("/api") +class ExerciseController(private val exercisesRepository: ExercisesRepository) { + + @GetMapping("/exercises/{id}") + fun getExerciseById(@PathVariable(value = "id") exerciseId: Long): ResponseEntity { + return exercisesRepository.findById(exerciseId).map { exercise -> + ResponseEntity.ok(exercise) + }.orElse(ResponseEntity.notFound().build()) + } + + @GetMapping("/exercises/customer/{id}") + fun getAllExersicesByCustomerId(@PathVariable( value = "id" ) customerId: Long? ): List = + exercisesRepository.getAllByCustomerId(customerId) + + @PostMapping("/exercises") + fun createNewExercise(@Valid @RequestBody exercise: Exercises): Exercises { + return exercisesRepository.save(exercise) + } + + @PutMapping("/exercises/{id}") + fun updateExerciseById(@PathVariable(value = "id") exerciseId: Long, + @Valid @RequestBody newExercises: Exercises): ResponseEntity { + return exercisesRepository.findById(exerciseId).map { existingExercises -> + val updatedExercises: Exercises = existingExercises.copy( + exerciseTypeId = newExercises.exerciseTypeId, + customerId = newExercises.customerId, + dateAdd = newExercises.dateAdd, + quantity = newExercises.quantity, + restTime = newExercises.restTime + ) + ResponseEntity.ok().body(exercisesRepository.save(updatedExercises)) + }.orElse(ResponseEntity.notFound().build()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/ExerciseControllerAspect.kt b/src/main/kotlin/com/aitrainer/api/controller/ExerciseControllerAspect.kt new file mode 100644 index 0000000..02089ed --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ExerciseControllerAspect.kt @@ -0,0 +1,35 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.repository.ConfigurationRepository +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.aspectj.lang.annotation.Pointcut +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Component +@Suppress("unused") +@Aspect +class ExerciseControllerAspect { + + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Suppress("unused") + @Pointcut("execution(* com.aitrainer.api.controller.ExerciseController.*())") + fun exerciseControllerAspect() { + } + + @Before("exerciseControllerAspect()") + fun loggingAop() { + Singleton.checkDBUpdate(configurationRepository, properties) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeController.kt b/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeController.kt new file mode 100644 index 0000000..85319e1 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeController.kt @@ -0,0 +1,60 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.model.ExerciseType +import com.aitrainer.api.repository.ExerciseTypeRepository +import org.slf4j.LoggerFactory + +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import javax.validation.Valid + +@RestController +@RequestMapping("/api") +class ExerciseTypeController ( private val exerciseTypeRepository: ExerciseTypeRepository ) { + private val logger = LoggerFactory.getLogger(javaClass) + + @GetMapping("/exercise_type") + fun getAllExerciseType(): List { + val list: List = exerciseTypeRepository.findAll() + logger.info(" -- Get All exercise types..") + return list + } + + + + @PostMapping("/exercise_type") + fun createNewExerciseType(@Valid @RequestBody exerciseType: ExerciseType): ExerciseType { + logger.info("Create new exercise type: $exerciseType") + return exerciseTypeRepository.save(exerciseType) + } + + + @GetMapping("/exercise_type/{id}") + fun getExerciseTypeById(@PathVariable(value = "id") exerciseTypeId: Long): ResponseEntity { + logger.info("Get exercise type by id: $exerciseTypeId") + return exerciseTypeRepository.findById(exerciseTypeId).map { exerciseType -> + ResponseEntity.ok(exerciseType) + }.orElse(ResponseEntity.notFound().build()) + } +/* + @GetMapping("/exercise_type/name/{name}") + fun getExerciseTypeByName(@PathVariable(value = "name") name: String): ResponseEntity { + return exerciseTypeRepository.findByName(name).map { exerciseType -> + ResponseEntity.ok(exerciseType) + }.orElse(ResponseEntity.notFound().build()) + }*/ + + @PostMapping("/exercise_type/{id}") + fun updateExerciseTypesById(@PathVariable(value = "id") exerciseTypeId: Long, + @Valid @RequestBody newExerciseType: ExerciseType): ResponseEntity { + logger.info("Update exercise type by id: $exerciseTypeId with object $newExerciseType") + return exerciseTypeRepository.findById(exerciseTypeId).map { existingExerciseType -> + val updatedExerciseType: ExerciseType = existingExerciseType + .copy(name = newExerciseType.name, + description = newExerciseType.description, + video = newExerciseType.video) + ResponseEntity.ok().body(exerciseTypeRepository.save(updatedExerciseType)) + }.orElse(ResponseEntity.notFound().build()) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeControllerAspect.kt b/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeControllerAspect.kt new file mode 100644 index 0000000..acbb9ca --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/controller/ExerciseTypeControllerAspect.kt @@ -0,0 +1,33 @@ +package com.aitrainer.api.controller + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.repository.ConfigurationRepository +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.aspectj.lang.annotation.Pointcut +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Suppress("unused") +@Aspect +@Component +class ExerciseTypeControllerAspect { + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Suppress("unused") + @Pointcut("execution(* com.aitrainer.api.controller.ExerciseTypeController.*())") + fun exerciseTypeControllerAspect() { + } + + @Before("exerciseTypeControllerAspect()") + fun loggingAop() { + Singleton.checkDBUpdate(configurationRepository, properties) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/model/Configuration.kt b/src/main/kotlin/com/aitrainer/api/model/Configuration.kt new file mode 100644 index 0000000..de7b2b1 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/Configuration.kt @@ -0,0 +1,20 @@ +package com.aitrainer.api.model + +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.validation.constraints.NotBlank + +@Entity +data class Configuration ( + + + @get: NotBlank var configKey: String = "", + @get: NotBlank var configValue: String = "", + var dateAdd: String, + var dateChange: String = "", + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val configuration_id: Long = 0 +) diff --git a/src/main/kotlin/com/aitrainer/api/model/Customer.kt b/src/main/kotlin/com/aitrainer/api/model/Customer.kt new file mode 100644 index 0000000..9e3014a --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/Customer.kt @@ -0,0 +1,24 @@ +package com.aitrainer.api.model + +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id + +@Entity +data class Customer ( + var name: String = "", + var firstname: String = "", + var email: String = "", + var age: Int = 0, + var sex: String = "m", + var active: String = "N", + var dateAdd: String? = null, + var dateChange: String? = null, + var dataPolicyAllowed: Int = 0, + var admin: Int = 0, + var password: String = "", + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val customer_id: Long = 0 +) diff --git a/src/main/kotlin/com/aitrainer/api/model/CustomerInformation.kt b/src/main/kotlin/com/aitrainer/api/model/CustomerInformation.kt new file mode 100644 index 0000000..493ada5 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/CustomerInformation.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api.model + +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.validation.constraints.NotBlank + +@Entity +class CustomerInformation ( + @get: NotBlank var title: String = "", + var description: String = "", + + var dateAdd: String? = null, + var displayBegin: String? = null, + var displayEnd: String? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val customerInformationId: Long = 0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/model/ExerciseType.kt b/src/main/kotlin/com/aitrainer/api/model/ExerciseType.kt new file mode 100644 index 0000000..1d846d2 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/ExerciseType.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api.model + +import org.hibernate.type.BinaryType +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.validation.constraints.NotBlank +import javax.validation.constraints.Null + +@Entity +data class ExerciseType ( + + @get: NotBlank var name: String = "", + @get: NotBlank var description: String = "", + @get: Null var video: BinaryType?, + + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val exerciseTypeId: Long = 0 +) diff --git a/src/main/kotlin/com/aitrainer/api/model/Exercises.kt b/src/main/kotlin/com/aitrainer/api/model/Exercises.kt new file mode 100644 index 0000000..d8a1d88 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/Exercises.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api.model + +import org.springframework.lang.NonNull +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.validation.constraints.Null + +@Entity +data class Exercises ( + @get: NonNull var exerciseTypeId: Long = 0, + @get: NonNull var customerId: Long = 0, + @get: NonNull var dateAdd: String? = null, + @get: NonNull var quantity: Int = 0, + @get: Null var restTime: Int?, // in seconds + @get: NonNull var unit: String? = null, + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val exerciseId: Long = 0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/model/User.kt b/src/main/kotlin/com/aitrainer/api/model/User.kt new file mode 100644 index 0000000..f1f4ce3 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/User.kt @@ -0,0 +1,15 @@ +package com.aitrainer.api.model + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +@Serializable +data class User ( + var username: String = "", + var password: String = "" +) { + @OptIn(UnstableDefault::class) + fun fromJson(json: String): User { + return Json.parse(serializer(), json) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/repository/ConfigurationRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/ConfigurationRepository.kt new file mode 100644 index 0000000..30383c5 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/ConfigurationRepository.kt @@ -0,0 +1,10 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.Configuration +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface ConfigurationRepository : JpaRepository { + fun findByConfigKey( key: String ):Configuration +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/repository/CustomerInformationRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/CustomerInformationRepository.kt new file mode 100644 index 0000000..3e3f87f --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/CustomerInformationRepository.kt @@ -0,0 +1,11 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.CustomerInformation +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface CustomerInformationRepository: JpaRepository { + fun findByDisplayBeginLessThanAndDisplayEndGreaterThan( dateTimeBegin: String, dateTimeEnd: String ): + List +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/repository/CustomerRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/CustomerRepository.kt new file mode 100644 index 0000000..4e6c10b --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/CustomerRepository.kt @@ -0,0 +1,12 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.Customer +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface CustomerRepository : JpaRepository { + fun findByActive( active: String? ): List + + fun findByEmail(email: String?): Customer? +} diff --git a/src/main/kotlin/com/aitrainer/api/repository/ExerciseTypeRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/ExerciseTypeRepository.kt new file mode 100644 index 0000000..c5cdc1e --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/ExerciseTypeRepository.kt @@ -0,0 +1,12 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.ExerciseType +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface ExerciseTypeRepository : JpaRepository{ + + fun findByName(name: String): ExerciseType + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/repository/ExercisesRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/ExercisesRepository.kt new file mode 100644 index 0000000..05f7426 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/ExercisesRepository.kt @@ -0,0 +1,10 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.Exercises +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface ExercisesRepository : JpaRepository { + fun getAllByCustomerId( customerId: Long? ):List +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/security/AuthenticationControllerAspect.kt b/src/main/kotlin/com/aitrainer/api/security/AuthenticationControllerAspect.kt new file mode 100644 index 0000000..c106785 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/AuthenticationControllerAspect.kt @@ -0,0 +1,31 @@ +package com.aitrainer.api.security + +import com.aitrainer.api.ApiApplication +import com.aitrainer.api.controller.ApplicationProperties +import com.aitrainer.api.controller.Singleton +import com.aitrainer.api.repository.ConfigurationRepository +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Suppress("unused") +@Aspect +@Component +class AuthenticationControllerAspect { + private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Before("execution(* com.aitrainer.api.security.JwtAuthenticationController.*(..))") + fun customerControllerAspect(joinPoint: JoinPoint) { + println("auth controller join") + Singleton.checkDBUpdate(configurationRepository, properties) + } + +} diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationController.kt b/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationController.kt new file mode 100644 index 0000000..9064b5a --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationController.kt @@ -0,0 +1,49 @@ +package com.aitrainer.api.security + +import com.aitrainer.api.service.UserDetailsServiceImpl +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.BadCredentialsException +import org.springframework.security.authentication.DisabledException +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.web.bind.annotation.* +import org.springframework.stereotype.Component + + + +@Component +@RestController +//@CrossOrigin +@RequestMapping("/api") +class JwtAuthenticationController { + @Autowired + private val authenticationManager: AuthenticationManager? = null + + @Autowired + private val jwtTokenUtil: JwtTokenUtil? = null + + @Autowired + private val jwtUserDetailsService: UserDetailsServiceImpl? = null + + @PostMapping("/authenticate") + fun generateAuthenticationToken(@RequestBody authenticationRequest: JwtRequest): ResponseEntity<*> { + + authenticate(authenticationRequest.username!!, authenticationRequest.password!!) + + val userDetails = jwtUserDetailsService + ?.loadUserByUsername(authenticationRequest.username) + val token: String = jwtTokenUtil!!.generateToken(userDetails!!) + return ResponseEntity.ok(JwtResponse(token)) + } + + private fun authenticate(username: String, password: String) { + try { + authenticationManager!!.authenticate(UsernamePasswordAuthenticationToken(username, password)) + } catch (e: DisabledException) { + throw Exception("USER_DISABLED", e) + } catch (e: BadCredentialsException) { + throw Exception("INVALID_CREDENTIALS", e) + } + } +} diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationEntryPoint.kt b/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationEntryPoint.kt new file mode 100644 index 0000000..2433af8 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtAuthenticationEntryPoint.kt @@ -0,0 +1,23 @@ +package com.aitrainer.api.security + +import org.springframework.security.web.AuthenticationEntryPoint +import org.springframework.stereotype.Component +import java.io.IOException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse +import java.io.Serializable +import org.springframework.security.core.AuthenticationException + + +@Component +class JwtAuthenticationEntryPoint : AuthenticationEntryPoint, Serializable { + @Throws(IOException::class) + override fun commence(request: HttpServletRequest?, response: HttpServletResponse, + authException: AuthenticationException?) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized") + } + + companion object { + private const val serialVersionUID = -7858869558953243875L + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtRequest.kt b/src/main/kotlin/com/aitrainer/api/security/JwtRequest.kt new file mode 100644 index 0000000..58cbb19 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtRequest.kt @@ -0,0 +1,19 @@ +package com.aitrainer.api.security + +import java.io.Serializable + + +class JwtRequest : Serializable { + var username: String? = null + var password: String? = null + + //default constructor for JSON Parsing + constructor(username: String?, password: String?) { + this.username = username + this.password = password + } + + companion object { + private const val serialVersionUID = 5926468583005150707L + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtRequestFilter.kt b/src/main/kotlin/com/aitrainer/api/security/JwtRequestFilter.kt new file mode 100644 index 0000000..35cdd56 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtRequestFilter.kt @@ -0,0 +1,77 @@ +package com.aitrainer.api.security + +import com.aitrainer.api.service.UserDetailsServiceImpl +import io.jsonwebtoken.ExpiredJwtException +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import java.io.IOException +import javax.servlet.FilterChain +import javax.servlet.ServletException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + + +@Component +class JwtRequestFilter : OncePerRequestFilter() { + @Autowired + private val jwtUserDetailsService: UserDetailsServiceImpl? = null + + @Autowired + private val jwtTokenUtil: JwtTokenUtil? = null + + //@Autowired + //private lateinit var authenticationController: JwtAuthenticationController + + @Throws(ServletException::class, IOException::class) + override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) { + val requestTokenHeader = request.getHeader("Authorization") + var username: String? = null + var jwtToken: String? = null + // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token + if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer")) { + jwtToken = requestTokenHeader.substring(7) + try { + username = jwtTokenUtil!!.getUsernameFromToken(jwtToken) + } catch (e: IllegalArgumentException) { + println("Unable to get JWT Token") + } catch (e: ExpiredJwtException) { + println("JWT Token has expired") + } + } else if (requestTokenHeader != null && requestTokenHeader.equals("1") ) { + logger.warn("Authenticate") + //val credentials: User = ObjectMapper().readValue(request.inputStream, User::class.java) + + } else { + logger.warn("JWT Token does not begin with Bearer String") + } + + //Once we get the token validate it. + if (username != null && SecurityContextHolder.getContext().authentication == null) { + val userDetails: UserDetails = jwtUserDetailsService!!.loadUserByUsername(username) + + // if token is valid configure Spring Security to manually set authentication + if (jwtTokenUtil!!.validateToken(jwtToken!!, userDetails)) { + val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.authorities) + usernamePasswordAuthenticationToken.details = WebAuthenticationDetailsSource().buildDetails(request) + // After setting the Authentication in the context, we specify + // that the current user is authenticated. So it passes the Spring Security Configurations successfully. + SecurityContextHolder.getContext().authentication = usernamePasswordAuthenticationToken + } + } + chain.doFilter(request, response) + } + + /*private fun readUserCredentials(request: HttpServletRequest): UserCredentials? { + return try { + ObjectMapper().readValue(request.inputStream, UserCredentials::class.java) + } catch (ioe: IOException) { + throw BadCredentialsException("Invalid request", ioe) + } + }*/ +} diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtResponse.kt b/src/main/kotlin/com/aitrainer/api/security/JwtResponse.kt new file mode 100644 index 0000000..d7227fa --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtResponse.kt @@ -0,0 +1,12 @@ +package com.aitrainer.api.security + +import java.io.Serializable + + +class JwtResponse(val token: String) : Serializable { + + companion object { + private const val serialVersionUID = -8091879091924046844L + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtSecurityConfig.kt b/src/main/kotlin/com/aitrainer/api/security/JwtSecurityConfig.kt new file mode 100644 index 0000000..b299268 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtSecurityConfig.kt @@ -0,0 +1,65 @@ +package com.aitrainer.api.security + +import com.aitrainer.api.service.ServiceBeans +import com.aitrainer.api.service.UserDetailsServiceImpl +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter + + +@Configuration +@EnableWebSecurity +class JwtSecurityConfig : WebSecurityConfigurerAdapter() { + @Autowired + private val jwtAuthenticationEntryPoint: JwtAuthenticationEntryPoint? = null + + @Autowired + private val jwtUserDetailsService: UserDetailsServiceImpl? = null + + @Autowired + private val jwtRequestFilter: JwtRequestFilter? = null + + @Autowired + private val serviceBeans: ServiceBeans? = null + + override fun configure(auth: AuthenticationManagerBuilder?) { + auth!!.userDetailsService(jwtUserDetailsService).passwordEncoder(serviceBeans!!.passwordEncoder()) + } + + @Bean + @Throws(Exception::class) + override fun authenticationManagerBean(): AuthenticationManager { + return super.authenticationManagerBean() + } + + @Throws(Exception::class) + override fun configure(httpSecurity: HttpSecurity) { + + // We don't need CSRF for this example + httpSecurity. + csrf().disable(). + // dont authenticate this particular request + authorizeRequests().antMatchers("/api/authenticate").permitAll(). + // all other requests need to be authenticated + anyRequest().authenticated().and(). + // make sure we use stateless session; session won't be used to + // store user's state. + exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and(). + // Add a filter to validate the tokens with every request + //addFilterAt(JwtAuthenticationFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter::class.java). + addFilterAfter(jwtRequestFilter, UsernamePasswordAuthenticationFilter::class.java). + sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + + + + } + + +} diff --git a/src/main/kotlin/com/aitrainer/api/security/JwtTokenUtil.kt b/src/main/kotlin/com/aitrainer/api/security/JwtTokenUtil.kt new file mode 100644 index 0000000..8581cf3 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/security/JwtTokenUtil.kt @@ -0,0 +1,67 @@ +package com.aitrainer.api.security + +import org.springframework.beans.factory.annotation.Value +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Component +import java.util.* +import java.io.Serializable + +import io.jsonwebtoken.Claims +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm + + +@Component +class JwtTokenUtil : Serializable { + @Value("\${jwt.secret}") + private val secret: String? = null + fun getUsernameFromToken(token: String?): String { + return getClaimFromToken(token, Claims::getSubject) + } + + fun getIssuedAtDateFromToken(token: String?): Date { + return getClaimFromToken(token, Claims::getIssuedAt) + } + + fun getExpirationDateFromToken(token: String?): Date { + return getClaimFromToken(token, Claims::getExpiration) + } + + fun getClaimFromToken( token: String?, claimsResolver: ( Claims.()-> T ) ): T { + val claims: Claims = getAllClaimsFromToken(token) + return claims.claimsResolver() + } + + private fun getAllClaimsFromToken(token: String?): Claims { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).body + } + + private fun isTokenExpired(token: String): Boolean { + val expiration: Date = getExpirationDateFromToken(token) + return expiration.before(Date()) + } + + fun generateToken(userDetails: UserDetails): String { + val claims: Map = HashMap() + return doGenerateToken(claims, userDetails.username) + } + + private fun doGenerateToken(claims: Map, subject: String): String { + return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(Date(System.currentTimeMillis())) + .setExpiration(Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact() + } + + fun canTokenBeRefreshed(token: String): Boolean { + return !isTokenExpired(token) + } + + fun validateToken(token: String, userDetails: UserDetails): Boolean { + val username = getUsernameFromToken(token) + return username == userDetails.username && !isTokenExpired(token) + } + + companion object { + private const val serialVersionUID = -2550185165626007488L + const val JWT_TOKEN_VALIDITY = 5 * 60 * 60.toLong() + } +} diff --git a/src/main/kotlin/com/aitrainer/api/service/CustomerService.kt b/src/main/kotlin/com/aitrainer/api/service/CustomerService.kt new file mode 100644 index 0000000..bfebd01 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/service/CustomerService.kt @@ -0,0 +1,10 @@ +package com.aitrainer.api.service + +import com.aitrainer.api.model.Customer +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param + +interface CustomerService { + @Query("FROM customer WHERE active = :active ") + fun findByActive(@Param("active") active: String? ): List +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/service/ExerciseService.kt b/src/main/kotlin/com/aitrainer/api/service/ExerciseService.kt new file mode 100644 index 0000000..e505bc3 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/service/ExerciseService.kt @@ -0,0 +1,10 @@ +package com.aitrainer.api.service + +import com.aitrainer.api.model.Exercises +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param + +interface ExerciseService { + @Query("FROM exercises WHERE customer_id = :customerId") + fun findAllByCustomerId( @Param("customerId") customerId: Long? ): List +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/service/ServiceBeans.kt b/src/main/kotlin/com/aitrainer/api/service/ServiceBeans.kt new file mode 100644 index 0000000..8880030 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/service/ServiceBeans.kt @@ -0,0 +1,19 @@ +package com.aitrainer.api.service + +import org.springframework.context.annotation.Bean +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Component + +/* + Commonly used Beans + */ + +@Component +class ServiceBeans { + + @Bean + fun passwordEncoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/service/UserDetailsServiceImpl.kt b/src/main/kotlin/com/aitrainer/api/service/UserDetailsServiceImpl.kt new file mode 100644 index 0000000..a3482e4 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/service/UserDetailsServiceImpl.kt @@ -0,0 +1,34 @@ +package com.aitrainer.api.service + +import com.aitrainer.api.model.Customer +import com.aitrainer.api.repository.CustomerRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.userdetails.User +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import kotlin.collections.HashSet + +@Service +class UserDetailsServiceImpl: UserDetailsService { + + @Autowired + private lateinit var customerRepository: CustomerRepository + + @Override + @Transactional(readOnly = true) + override fun loadUserByUsername(username: String?): UserDetails { + val customer: Customer? = customerRepository.findByEmail(username) + + val grantedAuthorities = HashSet() + grantedAuthorities.add(SimpleGrantedAuthority("user")) + + if (customer != null) { + return User(customer.email, customer.password, grantedAuthorities) + } else {throw Exception("User does not exist")} + + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..2097769 --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,13 @@ +spring.profiles.active=dev +## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) +#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false +spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true +spring.datasource.username = aitrainer +spring.datasource.password = andio2009 + + +## Hibernate Properties + + +# The SQL dialect makes Hibernate generate better SQL for the chosen database +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect \ No newline at end of file diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..a7b7f9e --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,13 @@ +spring.profiles.active=prod +## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) +#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false +spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&allowMultiQueries=true +spring.datasource.username = aitrainer +spring.datasource.password = andio2009 + + +## Hibernate Properties + + +# The SQL dialect makes Hibernate generate better SQL for the chosen database +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect \ No newline at end of file diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties new file mode 100644 index 0000000..def8592 --- /dev/null +++ b/src/main/resources/application-test.properties @@ -0,0 +1,13 @@ +spring.profiles.active=test +## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) +#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false +spring.datasource.url = jdbc:mysql://mysql:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&allowMultiQueries=true +spring.datasource.username = root +spring.datasource.password = andio2009 + + +## Hibernate Properties + + +# The SQL dialect makes Hibernate generate better SQL for the chosen database +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..fa36823 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,21 @@ +spring.profiles.active=dev,test,prod +## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) +#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false +spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true +spring.datasource.username = aitrainer +spring.datasource.password = andio2009 + + +## Hibernate Properties + + +# The SQL dialect makes Hibernate generate better SQL for the chosen database +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect + +logging.config=classpath:logback-spring.xml +logging.file=logs + +# if the database structure has been changed, increment this version number +application.version=0.0.3 + +jwt.secret=aitrainer \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..23f9249 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,41 @@ + + + + + + ${logging.file}/aitrainer.log + + ${logging.file}/aitrainer.%d{yyyy-MM-dd}.%i.log.gz + + 50MB + + 30 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [%logger:%line] %msg%n + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [%logger:%line] %msg%n + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/ApplicationStartTest.kt b/src/test/kotlin/com/aitrainer/api/test/ApplicationStartTest.kt new file mode 100644 index 0000000..3beb06c --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/ApplicationStartTest.kt @@ -0,0 +1,34 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.controller.ApplicationProperties +import com.aitrainer.api.controller.Singleton +import com.aitrainer.api.repository.ConfigurationRepository +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import java.sql.ResultSet +import kotlin.test.assertEquals + +@SpringBootTest +class ApplicationStartTest { + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + @Autowired + private lateinit var properties: ApplicationProperties + + @Test + fun testDBCheck() { + Singleton.checkDBUpdate(this.configurationRepository, this.properties) + + var foundTable = false + val rs: ResultSet = Singleton.execQuery("Show tables;") + while ( rs.next() ) { + if ( rs.getString("tables_in_aitrainer") == "customer_information" ) { + foundTable = true + } + } + + assertEquals(foundTable, true) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/AuthenticationTest.kt b/src/test/kotlin/com/aitrainer/api/test/AuthenticationTest.kt new file mode 100644 index 0000000..83c0b35 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/AuthenticationTest.kt @@ -0,0 +1,25 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.security.JwtAuthenticationController +import com.aitrainer.api.security.JwtRequest +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import kotlin.test.assertEquals + +@SpringBootTest +class AuthenticationTest { + + @Autowired + private lateinit var authController: JwtAuthenticationController + @Test + fun testAuthentication() { + val response: ResponseEntity<*> + val jwtRequest = JwtRequest("bosi", "andio2009") + response = authController.generateAuthenticationToken(jwtRequest) + assertEquals(response.statusCode, HttpStatus.OK) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/ConfigurationTest.kt b/src/test/kotlin/com/aitrainer/api/test/ConfigurationTest.kt new file mode 100644 index 0000000..d7540b5 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/ConfigurationTest.kt @@ -0,0 +1,31 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.controller.ConfigurationController +import com.aitrainer.api.model.Configuration +import com.aitrainer.api.repository.ConfigurationRepository +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import kotlin.test.assertEquals + +@SpringBootTest +class ConfigurationTest { + + @Autowired + private lateinit var configurationRepository: ConfigurationRepository + private lateinit var configurationController: ConfigurationController + + @Test + fun testUpdateConfig() { + val config: Configuration = configurationRepository.findByConfigKey("db_version") + config.configValue = "0.0.2" + + configurationController = ConfigurationController(configurationRepository) + val updatedConfig: Configuration = configurationController.updateConfiguration(config) + assertEquals(updatedConfig.configValue, "0.0.2") + + val foundConfig: Configuration = configurationRepository.findByConfigKey("db_version") + assertEquals(foundConfig.configValue, "0.0.2") + + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/CustomerInformationTest.kt b/src/test/kotlin/com/aitrainer/api/test/CustomerInformationTest.kt new file mode 100644 index 0000000..0666d40 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/CustomerInformationTest.kt @@ -0,0 +1,24 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.model.CustomerInformation +import com.aitrainer.api.repository.CustomerInformationRepository +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import kotlin.test.assertEquals + +@SpringBootTest +class CustomerInformationTest { + + @Autowired + private lateinit var customerInformationRepository: CustomerInformationRepository + + @Test + fun getActiveCustomerInformation() { + val dateTime = "2020-06-01 09:00:00" + val info: List = this.customerInformationRepository. + findByDisplayBeginLessThanAndDisplayEndGreaterThan(dateTime, dateTime ) + assertEquals(info.size, 2) + assertEquals(info.first().title, "Fekvőtámasz világcsúcs") + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/CustomerTests.kt b/src/test/kotlin/com/aitrainer/api/test/CustomerTests.kt new file mode 100644 index 0000000..86c6996 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/CustomerTests.kt @@ -0,0 +1,122 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.controller.CustomerController +import com.aitrainer.api.model.Customer +import com.aitrainer.api.model.User +import com.aitrainer.api.repository.CustomerRepository +import com.aitrainer.api.service.ServiceBeans +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +@SpringBootTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class CustomerTests { + + @Autowired + private var serviceBean: ServiceBeans? = null + + @BeforeAll + fun init() { + if ( serviceBean == null ) { serviceBean = ServiceBeans() } + } + + @Autowired + private lateinit var customerRepository: CustomerRepository + private var insertedId: Long? = null + + @Test + fun testGet() { + val id: Long = 7 + val customer: Customer = customerRepository.findById( id ).orElse(null) + assertEquals( customer.name, "Átlag 18 éves fiú") + } + + @Test + fun testInsert() { + val newCustomer = Customer("Bossanyi", "Tibor", "", 48, "m") + val savedCustomer: Customer = customerRepository.save(newCustomer) + assertEquals(savedCustomer.age, 48) + + this.insertedId = savedCustomer.customer_id + + val customer: Customer = customerRepository.findById( savedCustomer.customer_id ).orElse(null) + assertEquals( customer.firstname, "Tibor") + + this.testUpdate(savedCustomer.customer_id) + } + + fun testUpdate( customerId: Long ) { + val id: Long? = customerId + assertNotNull(id) + + val updatedCustomer: Customer = customerRepository.findById( id ).orElse(null) + + assertNotNull(updatedCustomer) + updatedCustomer.firstname ="Tiborka" + + val customer: Customer = customerRepository.save( updatedCustomer ) + assertEquals( customer.firstname, "Tiborka") + + customerRepository.delete(updatedCustomer) + } + + @Test + fun testRegistration() { + val json = "{\"username\":\"bosi@example.com\",\"password\":\"94385\"}" + val user: User = User().fromJson(json) + assertEquals(user.username, "bosi@example.com") + val customer = Customer() + with(customer) { + email = user.username + password = user.password + } + val customerController = CustomerController(customerRepository) + customerController.serviceBeans = serviceBean + var response: ResponseEntity<*> = customerController.registration(json) + val newCustomer: Customer? = response.body as Customer + assertEquals(response.statusCode, HttpStatus.OK) + + val json2 = "{\"username\":\"bosi@example.com\",\"password\":\"934345\"}" + response = customerController.registration(json2) + assertEquals(response.statusCode, HttpStatus.BAD_REQUEST) + + if ( newCustomer != null) { + customerRepository.delete(newCustomer) + } + } + + @Test fun testLogin() { + val json = "{\"username\":\"bosi2@example.com\",\"password\":\"94333385\"}" + val user: User = User().fromJson(json) + val customer = Customer() + with(customer) { + email = user.username + password = user.password + } + val customerController = CustomerController(customerRepository) + customerController.serviceBeans = serviceBean + var response: ResponseEntity<*> = customerController.registration(json) + val newCustomer: Customer? = response.body as Customer + assertEquals(response.statusCode, HttpStatus.OK) + + response = customerController.login(json) + val loginedCustomer: Customer? = response.body as Customer + assertEquals(response.statusCode, HttpStatus.OK) + if ( loginedCustomer != null ) { + assertEquals(loginedCustomer.email, ("bosi2@example.com") ) + } else { + assert(true) + } + + if ( newCustomer != null) { + customerRepository.delete(newCustomer) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/ExerciseTest.kt b/src/test/kotlin/com/aitrainer/api/test/ExerciseTest.kt new file mode 100644 index 0000000..bbc2a55 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/ExerciseTest.kt @@ -0,0 +1,44 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.model.Exercises +import com.aitrainer.api.repository.ExercisesRepository +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@SpringBootTest +class ExerciseTest { + + @Autowired + private lateinit var exerciseRepository: ExercisesRepository + + @Test + fun testGet() { + var id: Long = 1 + + val exercises: List = exerciseRepository.getAllByCustomerId( id ) + assertEquals( exercises[0].quantity, 12) + + id = 100000 + val exercises2: List = exerciseRepository.getAllByCustomerId( id ) + + assertEquals( exercises2.size, 0) + } + + @Test + fun testInsert() { + val exercise = Exercises( + exerciseTypeId = 3, + customerId = 11, + quantity = 100, + dateAdd = "2020-05-13 04:32:00", + unit = "repeat", + restTime = null + ) + val exerciseNew = exerciseRepository.save(exercise) + assertTrue(exerciseNew.exerciseId > 2) + exerciseRepository.delete(exercise) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/ExerciseTypeTest.kt b/src/test/kotlin/com/aitrainer/api/test/ExerciseTypeTest.kt new file mode 100644 index 0000000..f119762 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/ExerciseTypeTest.kt @@ -0,0 +1,49 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.model.ExerciseType +import com.aitrainer.api.repository.ExerciseTypeRepository +import org.junit.jupiter.api.Test +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import kotlin.test.assertEquals + +@SpringBootTest +class ExerciseTypeTest { + private val logger = LoggerFactory.getLogger(javaClass) + + @Autowired + private lateinit var exerciseTypeRepository: ExerciseTypeRepository + private var insertedId: Long = 0 + + @Test + fun testGet() { + val id: Long = 7 + val extype: ExerciseType = exerciseTypeRepository.findById(id).orElse(null) + assertEquals(extype.name, "4x10m-es ingafutás") + } + + @Test + fun testGetAll() { + val exerciseTypes: List = exerciseTypeRepository.findAll() + assertEquals(exerciseTypes[0].exerciseTypeId, 1) + assertEquals(exerciseTypes[0].name, "Melső fekvőtámasz 1 perc") + } + + @Test + fun testInsert(){ + logger.info("Add 'Húzodzkodás 2") + val newEx = ExerciseType( "Húzodzkodás 2", " A legtöbb húzodszkodást 24 óra alatt John Ort érte el 7600-al 2016-ban. ", null ) + val savedEx: ExerciseType = exerciseTypeRepository.save(newEx) + assertEquals(savedEx.name, "Húzodzkodás 2") + + this.insertedId = savedEx.exerciseTypeId + + logger.info("Find 'Húzodzkodás 2") + val extype: ExerciseType = exerciseTypeRepository.findById( savedEx.exerciseTypeId ).orElse(null) + assertEquals( extype.name, "Húzodzkodás 2") + + logger.info("Delete 'Húzodzkodás 2") + exerciseTypeRepository.delete(extype) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/PropertiesTest.kt b/src/test/kotlin/com/aitrainer/api/test/PropertiesTest.kt new file mode 100644 index 0000000..26130b1 --- /dev/null +++ b/src/test/kotlin/com/aitrainer/api/test/PropertiesTest.kt @@ -0,0 +1,27 @@ +package com.aitrainer.api.test + +import com.aitrainer.api.controller.ApplicationProperties +import org.springframework.boot.test.context.SpringBootTest +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import kotlin.test.assertEquals + +@SpringBootTest +class PropertiesTest { + + @Autowired + private lateinit var properties: ApplicationProperties + + @Test + fun testProperties() { + val version: String = properties.getVersion() + assertEquals(version, "0.0.2") + } + + @Test + fun testDatasourceUrl() { + val url: String = properties.getDatasourceUrl() + assertEquals(url, "jdbc:mysql://localhost:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true") + } + +} \ No newline at end of file