Compare commits

..

No commits in common. "dev" and "beta" have entirely different histories.
dev ... beta

112 changed files with 128 additions and 17775 deletions

66
.vscode/launch.json vendored
View File

@ -1,66 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "PHP 8.0",
"type": "php",
"request": "launch",
"pathMappings": {
"/web/backend": "${workspaceFolder}\\src\\backend"
},
"skipFiles": [
"${workspaceFolder}/src/backend/vendor/",
"/web/backend/vendor/"
],
"port": 7780,
"hostname": "127.0.0.1",
"xdebugSettings": {
"max_children": 100,
}
},
{
"name": "PHP 8.0 with vendor",
"type": "php",
"request": "launch",
"pathMappings": {
"/web/backend": "${workspaceFolder}\\src\\backend"
},
"port": 7780,
"hostname": "127.0.0.1",
"xdebugSettings": {
"max_children": 100,
}
},
{
"name": "PHP 8.0 CLI",
"type": "php",
"request": "launch",
"pathMappings": {
"/web/backend": "${workspaceFolder}\\src\\backend"
},
"skipFiles": [
"${workspaceFolder}/src/backend/vendor/",
"/web/backend/vendor/"
],
"port": 7781,
"hostname": "127.0.0.1",
"xdebugSettings": {
"max_children": 100,
}
},
{
"name": "PHP 8.0 CLI with vendor",
"type": "php",
"request": "launch",
"pathMappings": {
"/web/backend": "${workspaceFolder}\\src\\backend"
},
"port": 7781,
"hostname": "127.0.0.1",
"xdebugSettings": {
"max_children": 100,
}
}
]
}

198
Jenkinsfile vendored
View File

@ -1,198 +0,0 @@
pipeline {
agent any
environment {
DatabaseUrl = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh ${BRANCH_NAME} DatabaseUrl').trim()
ProjectPath = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh ${BRANCH_NAME} ProjectPath').trim()
DatabaseUrlTesting = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh master DatabaseUrl').trim()
}
stages {
stage('Pull') {
steps {
git(url: 'http://git.fufle.net/Community/CureNet.git', branch: '${BRANCH_NAME}', credentialsId: '799188a2-c281-4e4c-b78f-c82132ea792b', poll: true)
}
}
stage('Approve') {
steps {
echo "http://git.fufle.net/Community/CureNet/commit/${env.GIT_COMMIT}"
script {
if (env.BRANCH_NAME == "prod") {
echo "http://git.fufle.net/Community/CureNet/compare/prod...staging"
input(message: 'Deploy to production?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
if (env.BRANCH_NAME == "staging") {
echo "http://git.fufle.net/Community/CureNet/compare/staging...beta"
input(message: 'Deploy to staging instance?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
if (env.BRANCH_NAME == "beta") {
echo "http://git.fufle.net/Community/CureNet/compare/beta...dev"
input(message: 'Deploy to beta instance?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
}
}
}
stage('Install') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'sed -i "s/-1001/${BUILD_ID}/g" src/environments/*.ts'
sh 'Timestamp=`php8.0 -r "echo time();"` && sed -i "s/-1002/${Timestamp}/g" src/environments/*.ts'
sh 'npm ci'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'php8.0 /usr/local/bin/composer install'
}
}
}
}
}
stage('Configure for tests') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'echo skip'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'APP_SECRET=`php8.0 -r "echo md5(\\"branch=${BRANCH_NAME};\\");"` && sed -i "s/APP_SECRET=.*/APP_SECRET=${APP_SECRET}/g" .env'
sh '''sed -i 's/DATABASE_URL=.*/DATABASE_URL=${DatabaseUrlTesting}/g' .env.test'''
sh 'php8.0 /usr/local/bin/composer dump-env test'
sh 'php8.0 bin/console doctrine:schema:update --force';
}
}
}
}
}
stage('Test') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ng lint'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'php8.0 vendor/bin/phpunit'
}
}
}
}
}
stage('Configure') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'echo skip'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh '''APP_SECRET=`php8.0 -r "echo md5(\\"branch=${BRANCH_NAME};\\");"` && sed -i 's/APP_SECRET=.*/APP_SECRET=${APP_SECRET}/g' .env'''
sh '''sed -i 's/DATABASE_URL=.*/DATABASE_URL=${DatabaseUrl}/g' .env.dev'''
sh 'php8.0 /usr/local/bin/composer dump-env dev'
}
}
}
}
}
stage('Build') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ng b -c production'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'echo skip'
}
}
}
}
}
stage('Deploy') {
when {
anyOf {
branch 'master'
branch 'staging'
branch 'beta'
branch 'dev'
}
}
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ssh web@fufle.net touch ${ProjectPath}/frontend/REMOVEME'
sh 'ssh web@fufle.net rm -rf ${ProjectPath}/frontend/*'
sh 'scp -r dist/* web@fufle.net:${ProjectPath}/frontend/'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'ssh web@fufle.net touch ${ProjectPath}/backend/REMOVEME'
sh 'ssh web@fufle.net rm -rf ${ProjectPath}/backend/*'
sh 'scp -r ./* web@fufle.net:${ProjectPath}/backend/'
}
}
}
}
}
}
}

View File

@ -12,8 +12,6 @@ We also encourage doctors and specialists to join the community, they will have
### Disease analysis
We want users to have access to the log of their disease where they can enter how it is progressing. On this basis, together with specialists, we will be able to find some common features for new diseases and predict as many negative symptoms as possible in order to be able to prevent them as much as possible.
[Technical documentation](./src/#technical-documentation)
# Polski
## Czym jest CureNet?
### Opis
@ -26,7 +24,4 @@ Każdy może założyć profil oraz dodać jedną lub więcej chorób. Dzięki t
### Społeczność specjalistów
Zachęcamy także lekarzy i specjalistów, aby dołączyli do społeczności, będą oni mieli wgląd w listę swoich pacjentów ale także do listy osób, które mają podobne schorzenia jak ich podopieczni, dzięki czemu będą mieli dostęp do szerszego grona osób z daną chorobą. Będą mogli także przedyskutować swoje obserwacje z innymi lekarzami, którzy także mają doświadczenie z daną chorobą. Jest to naprawdę ważne przy bardzo unikalnych i nie dokońca poznanych jeszcze schorzeniach.
### Analiza chorób
Chcemy, aby użytkownicy mieli dostęp do dziennika swojej choroby, gdzie będą mogli wprowadzać jak ona przebiega. Na tej podstawie wraz ze specjalistami będziemy w stanie znaleźć pewne wsólne cechy dla nowych chorób i przewidzieć jak najwięcej negatywnych symptomów, aby móc im jak najlepiej zapobiec.
[Dokumentacja techniczna](./src/#technical-documentation)
Chcemy, aby użytkownicy mieli dostęp do dzinnika swojej choroby, gdzie będą mogli wprowadzać jak ona przebiega. Na tej podstawie wraz ze specjalistami będziemy w stanie znaleźć pewne wsólne cechy dla nowych chorób i przewidzieć jak najwięcej negatywnych symptomów, aby móc im jak najlepiej zapobiec.

4
docker/.gitignore vendored
View File

@ -1,4 +0,0 @@
logs/*
!logs/.gitkeep
cache/*/*
!cache/*/.gitkeep

View File

@ -1,50 +0,0 @@
FROM debian:10
RUN apt update \
&& echo "Binary::apt::APT::Keep-Downloaded-Packages \"true\";" | tee /etc/apt/apt.conf.d/01keep-debs \
&& apt install -y wget lsb-release ca-certificates apt-transport-https software-properties-common gnupg2 gcc g++ make \
&& echo "configure repos" \
&& echo "deb https://packages.sury.org/php/ buster main" | tee /etc/apt/sources.list.d/php.list \
&& echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" | tee /etc/apt/sources.list.d/postgresql.list \
&& echo "deb https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/buster pgadmin4 main" | tee /etc/apt/sources.list.d/pgdg.list \
&& echo "download keys" \
&& (wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add - ) \
&& (wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - ) \
&& (wget -qO - https://www.pgadmin.org/static/packages_pgadmin_org.pub | apt-key add - ) \
&& echo "install nodejs" \
&& wget -qO - https://deb.nodesource.com/setup_16.x | bash - \
&& echo "update repos" \
&& apt update \
&& echo "install software" \
&& apt install -y aptitude unzip \
&& apt install -y postgresql pgadmin4 \
&& apt install -y php8.0 php8.0-ctype php8.0-iconv php8.0-simplexml php8.0-tokenizer php8.0-fpm php8.0-pdo php8.0-pdo-pgsql php8.0-pgsql php8.0-mbstring php8.0-zip php8.0-xdebug \
&& apt install -y nginx \
&& apt install -y nodejs \
&& echo "configure tools" \
&& echo "# angular" \
&& npm install -g @angular/cli \
&& echo "# composer" \
&& php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php --install-dir=/usr/local/bin/ --filename=composer \
&& php -r "unlink('composer-setup.php');" \
&& echo "# nginx" \
&& ln -s /web/config/nginx/app.conf /etc/nginx/conf.d/app.conf \
&& ln -s /web/config/nginx/php80.conf /etc/nginx/php80.conf \
&& echo "# postgresql" \
&& echo "postgres:WeryStronkPaz2" | chpasswd \
&& echo "# pgadmin4" \
&& bash -c 'PGADMIN_SETUP_EMAIL="admin@pgadmin.localhost" PGADMIN_SETUP_PASSWORD="WeryStronkPaz2" /usr/pgadmin4/bin/setup-web.sh --yes' \
&& echo "" > /etc/nginx/sites-enabled/default \
&& echo "!!! DONE !!!"
RUN apt install -y nano openssh-server telnet
VOLUME /web/backend/vendor
VOLUME /web/backend/var
VOLUME /web/frontend/node_modules
EXPOSE 7700 7780 7781 49153
STOPSIGNAL SIGQUIT
CMD ["bash", "/web/config/startup.sh"]

View File

View File

View File

View File

@ -1,25 +0,0 @@
version: "3.9"
services:
app:
build: .
ports:
- 7700:7700
- 49153:49153
expose:
- 7780:7780
- 7781:7781
volumes:
- .:/web/config
- ../src/backend:/web/backend:cached
- type: bind
target: /web/frontend
source: ../src/frontend
volume:
nocopy: true
- ./logs:/var/log
- vendor:/web/backend/vendor
- var:/web/backend/var
volumes:
nodemodules:
vendor:
var:

View File

View File

@ -1,27 +0,0 @@
server {
server_name localhost;
listen 7700;
root /web/backend/public;
index index.htm index.html index.php;
location / {
proxy_bind 127.0.0.1;
proxy_pass http://localhost:4200;
}
location /api/ {
try_files $uri $uri/ /index.php/$uri?$query_string;
}
location /coverage/ {
alias /web/coverage/html-coverage/;
index index.html;
}
location /pgadmin4/ {
include proxy_params;
proxy_bind 127.0.0.1;
proxy_pass http://localhost:80;
proxy_set_header X-Script-Name /pgadmin4;
}
proxy_intercept_errors on;
fastcgi_intercept_errors on;
fastcgi_hide_header X-Powered-By;
include "php80.conf";
}

View File

@ -1,14 +0,0 @@
index index.php index.html index.htm;
include snippets/fastcgi-php.conf;
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
fastcgi_param HTTP_PROXY "";
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffering off;
}

View File

@ -1,12 +0,0 @@
zend_extension=xdebug.so
[xdebug]
xdebug.mode=coverage
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=7781
xdebug.show_exception_trace = 1
xdebug.show_error_trace = 1
xdebug.log_level = 0

View File

@ -1,12 +0,0 @@
zend_extension=xdebug.so
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=7780
xdebug.show_exception_trace = 1
xdebug.show_error_trace = 1
xdebug.log_level = 0

View File

@ -1,4 +0,0 @@
ALTER USER postgres WITH PASSWORD 'WeryStronkPaz2';
CREATE USER curenet WITH PASSWORD 'WeryStronkPaz2';
CREATE DATABASE curenet;
GRANT ALL PRIVILEGES ON DATABASE curenet to curenet;

View File

@ -1 +0,0 @@
docker.exe compose -p curenet up

View File

@ -1 +0,0 @@
docker.exe compose -p curenet exec app bash -c 'cd /web && bash'

View File

@ -1,58 +0,0 @@
#!/bin/bash
echo "> Configure cache"
mkdir -p /tmp/app/vendor
ln -s /tmp/app/vendor /web/backend/vendor
echo "> Configure user"
useradd developer
chown developer:developer /web/frontend -R
chown developer:developer /web/backend -R
echo "> Configure logs"
mkdir -p /var/log/{apache2,nginx,pgadmin,postgresql}
chown postgres:adm /var/log/postgresql -R
chown root:postgres /var/log/postgresql
echo "> Configure volumes"
[ -e /web/frontend/node_modules ] || ln -s /web/node /web/frontend/node_modules
[ -e /web/backend/var ] || ln -s /web/var /web/backend/var
[ -e /web/backend/vendor ] || ln -s /web/vendor /web/backend/vendor
echo "> Starting php8.0-fpm server"
[ -e /etc/php/8.0/fpm/conf.d/20-xdebug.ini ] && rm /etc/php/8.0/fpm/conf.d/20-xdebug.ini || echo OK
[ -e /etc/php/8.0/cli/conf.d/20-xdebug.ini ] && rm /etc/php/8.0/cli/conf.d/20-xdebug.ini || echo OK
cp /web/config/php8.0/xdebug-fpm.ini /etc/php/8.0/fpm/conf.d/20-xdebug.ini
cp /web/config/php8.0/xdebug-cli.ini /etc/php/8.0/cli/conf.d/20-xdebug.ini
service php8.0-fpm start
echo "> Starting nginx server"
service nginx start
echo "> Starting apache2 server for pgAdmin4"
service apache2 start
echo "> Starting postgresql database"
service postgresql start
echo "> Starting ssh server"
service ssh start
echo "> Configure database"
su postgres -c psql postgres < /web/config/postgres/init-data.sql
echo "> Configure symfony"
cd /web/backend
composer install
php bin/console doctrine:schema:update --force
php php bin/console lexik:jwt:generate-keypair --skip-if-exists
echo "> Configure angular"
cd /web/frontend
time su developer -c 'npm ci'
echo "> Starting angular"
su developer -c 'ng serve --disable-host-check --host 0.0.0.0 --poll'
echo "> Waiting"
sleep Infinity

View File

@ -1,74 +0,0 @@
# Technical documentation
## Frontend
1. [HTML 5](https://developer.mozilla.org/en-US/docs/Glossary/HTML5)
1. [CSS 3](https://developer.mozilla.org/pl/docs/Web/CSS)
1. [JavaScript](https://developer.mozilla.org/pl/docs/orphaned/Web/JavaScript)
1. [TypeScript](https://www.typescriptlang.org/docs/ "Documentation")
1. [Angular 12](https://angular.io/docs "Documentation") (Frontend framework)
## Backend
1. [PHP 8.0](https://www.php.net/docs.php "Documentation")
1. [Symfony 5](https://symfony.com/doc/current/index.html "Documentation") (PHP Framework)
1. [Postgresql 13](https://www.postgresql.org/docs/13/index.html "Documentation") (Database)
1. [Docker](https://docs.docker.com/) (Local development environment)
## Tools
### Neccessary
1. [Docker](https://docs.docker.com/) - to run local environment
### Optional
1. [Firefox Developer Edition](https://www.mozilla.org/pl/firefox/developer/)
2. [VS Code](https://code.visualstudio.com/) - [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment)
- Plugins for PHP
- [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug)
- [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client)
- Plugins for Angular
- [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template)
- Plugins for database
- [SQLTools](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools)
- [SQLTools PostgreSQL/Redshift Driver](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools-driver-pg)
- Plugins for git:
- [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
## How to run app?
1. Download project from repository using standalone git or built in any IDE
2. Install Docker Desktop
3. run command line and go to project directory
4. Go to docker directory
5. Execute `docker compose -p curenet up`
6. Wait until docker building and lauching the app.
At first attempt it can take a few minutes
## Application links
1. Main app: `http://localhost:7700`
2. PgAdmin4 `http://localhost:7700/pgadmin4`
3. PHP Xdebug for fpm: `localhost:7780`
3. PHP Xdebug for cli: `localhost:7781`
## Credentials
### PgAdmin4
- Login: `admin@pgadmin.localhost`
- Password: `WeryStronkPaz2`
- Configuration:
1. Click on Server in left panel
2. Go to Create > Server
3. Enter name (you can name it as you want)
4. Go to Connection tab
5. Enter host name: `localhost`
6. Enter username: `postgres`
7. Enter password: `WeryStronkPaz2`
8. Save it
9. Go to Servers > [your name] > Databases > curenet > Schemas > public > Tables
10. You can see all tables
### Database
- Server: `localhost`
- Port: `5432`
- Username
- For application: `curenet`
- For admin: `postgres`
- Database
- For application: `curenet`
- For admin: `postgres`
- Password: `WeryStronkPaz2`
### Known issues
1. If the docker build the app but can't run it, check the docker/startup.sh file, it should be saved with LF as end of line sequence. If it's CRLF try to change it to LF, save the file and try to run app again

View File

@ -30,9 +30,3 @@ APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
###< doctrine/doctrine-bundle ###
###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=8270442e040a48fd42967bf1690d5dba
###< lexik/jwt-authentication-bundle ###

View File

@ -1,3 +0,0 @@
APP_ENV=dev
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"

View File

@ -1,3 +0,0 @@
APP_ENV=dev
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
DATABASE_URL="postgresql://curenet:WeryStronkPaz2@127.0.0.1:5432/curenet?serverVersion=13&charset=utf8"

View File

@ -1,3 +0,0 @@
APP_ENV=dev
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"

View File

@ -4,4 +4,3 @@ APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"

View File

@ -18,7 +18,3 @@
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
###> lexik/jwt-authentication-bundle ###
/config/jwt/*.pem
###< lexik/jwt-authentication-bundle ###

View File

@ -12,10 +12,8 @@
"doctrine/doctrine-bundle": "^2.4",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "^2.9",
"lexik/jwt-authentication-bundle": "^2.12",
"phpdocumentor/reflection-docblock": "^5.2",
"sensio/framework-extra-bundle": "^6.1",
"symfony-bundles/json-request-bundle": "^4.0",
"symfony/asset": "5.3.*",
"symfony/console": "5.3.*",
"symfony/dotenv": "5.3.*",
@ -50,7 +48,7 @@
"symfony/browser-kit": "^5.3",
"symfony/css-selector": "^5.3",
"symfony/debug-bundle": "^5.3",
"symfony/maker-bundle": "^1.33",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^5.3",
"symfony/stopwatch": "^5.3",
"symfony/var-dumper": "^5.3",

View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7f30e61383bffabaf547a9160e26d9c1",
"content-hash": "05166d01e6bf577938523c038634225a",
"packages": [
{
"name": "composer/package-versions-deprecated",
@ -1653,252 +1653,6 @@
],
"time": "2021-07-09T11:58:40+00:00"
},
{
"name": "lcobucci/clock",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/clock.git",
"reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3",
"reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"require-dev": {
"infection/infection": "^0.17",
"lcobucci/coding-standard": "^6.0",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/php-code-coverage": "9.1.4",
"phpunit/phpunit": "9.3.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Lcobucci\\Clock\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com"
}
],
"description": "Yet another clock abstraction",
"support": {
"issues": "https://github.com/lcobucci/clock/issues",
"source": "https://github.com/lcobucci/clock/tree/2.0.x"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"time": "2020-08-27T18:56:02+00:00"
},
{
"name": "lcobucci/jwt",
"version": "4.1.4",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "71cf170102c8371ccd933fa4df6252086d144de6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/71cf170102c8371ccd933fa4df6252086d144de6",
"reference": "71cf170102c8371ccd933fa4df6252086d144de6",
"shasum": ""
},
"require": {
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-sodium": "*",
"lcobucci/clock": "^2.0",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"infection/infection": "^0.21",
"lcobucci/coding-standard": "^6.0",
"mikey179/vfsstream": "^1.6.7",
"phpbench/phpbench": "^1.0@alpha",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/php-invoker": "^3.1",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/4.1.4"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"time": "2021-03-23T23:53:08+00:00"
},
{
"name": "lexik/jwt-authentication-bundle",
"version": "v2.12.3",
"source": {
"type": "git",
"url": "https://github.com/lexik/LexikJWTAuthenticationBundle.git",
"reference": "7fb85afb1a63bb7e518a369baa355599f822ff43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lexik/LexikJWTAuthenticationBundle/zipball/7fb85afb1a63bb7e518a369baa355599f822ff43",
"reference": "7fb85afb1a63bb7e518a369baa355599f822ff43",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"lcobucci/jwt": "^3.4|^4.0",
"namshi/jose": "^7.2",
"php": ">=7.1",
"symfony/deprecation-contracts": "^2.4",
"symfony/framework-bundle": "^4.4|^5.1",
"symfony/security-bundle": "^4.4|^5.1"
},
"require-dev": {
"symfony/browser-kit": "^4.4|^5.1",
"symfony/console": "^4.4|^5.1",
"symfony/dom-crawler": "^4.4|^5.1",
"symfony/phpunit-bridge": "^4.4|^5.1",
"symfony/var-dumper": "^4.4|^5.1",
"symfony/yaml": "^4.4|^5.1"
},
"suggest": {
"gesdinet/jwt-refresh-token-bundle": "Implements a refresh token system over Json Web Tokens in Symfony",
"spomky-labs/lexik-jose-bridge": "Provides a JWT Token encoder with encryption support"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"Lexik\\Bundle\\JWTAuthenticationBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jeremy Barthe",
"email": "j.barthe@lexik.fr",
"homepage": "https://github.com/jeremyb"
},
{
"name": "Nicolas Cabot",
"email": "n.cabot@lexik.fr",
"homepage": "https://github.com/slashfan"
},
{
"name": "Cedric Girard",
"email": "c.girard@lexik.fr",
"homepage": "https://github.com/cedric-g"
},
{
"name": "Dev Lexik",
"email": "dev@lexik.fr",
"homepage": "https://github.com/lexik"
},
{
"name": "Robin Chalas",
"email": "robin.chalas@gmail.com",
"homepage": "https://github.com/chalasr"
},
{
"name": "Lexik Community",
"homepage": "https://github.com/lexik/LexikJWTAuthenticationBundle/graphs/contributors"
}
],
"description": "This bundle provides JWT authentication for your Symfony REST API",
"homepage": "https://github.com/lexik/LexikJWTAuthenticationBundle",
"keywords": [
"Authentication",
"JWS",
"api",
"bundle",
"jwt",
"rest",
"symfony"
],
"support": {
"issues": "https://github.com/lexik/LexikJWTAuthenticationBundle/issues",
"source": "https://github.com/lexik/LexikJWTAuthenticationBundle/tree/v2.12.3"
},
"funding": [
{
"url": "https://github.com/chalasr",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/lexik/jwt-authentication-bundle",
"type": "tidelift"
}
],
"time": "2021-07-07T09:06:05+00:00"
},
{
"name": "monolog/monolog",
"version": "2.3.1",
@ -1995,73 +1749,6 @@
],
"time": "2021-07-14T11:56:39+00:00"
},
{
"name": "namshi/jose",
"version": "7.2.3",
"source": {
"type": "git",
"url": "https://github.com/namshi/jose.git",
"reference": "89a24d7eb3040e285dd5925fcad992378b82bcff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/namshi/jose/zipball/89a24d7eb3040e285dd5925fcad992378b82bcff",
"reference": "89a24d7eb3040e285dd5925fcad992378b82bcff",
"shasum": ""
},
"require": {
"ext-date": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-spl": "*",
"php": ">=5.5",
"symfony/polyfill-php56": "^1.0"
},
"require-dev": {
"phpseclib/phpseclib": "^2.0",
"phpunit/phpunit": "^4.5|^5.0",
"satooshi/php-coveralls": "^1.0"
},
"suggest": {
"ext-openssl": "Allows to use OpenSSL as crypto engine.",
"phpseclib/phpseclib": "Allows to use Phpseclib as crypto engine, use version ^2.0."
},
"type": "library",
"autoload": {
"psr-4": {
"Namshi\\JOSE\\": "src/Namshi/JOSE/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alessandro Nadalin",
"email": "alessandro.nadalin@gmail.com"
},
{
"name": "Alessandro Cinelli (cirpo)",
"email": "alessandro.cinelli@gmail.com"
}
],
"description": "JSON Object Signing and Encryption library for PHP.",
"keywords": [
"JSON Web Signature",
"JSON Web Token",
"JWS",
"json",
"jwt",
"token"
],
"support": {
"issues": "https://github.com/namshi/jose/issues",
"source": "https://github.com/namshi/jose/tree/master"
},
"time": "2016-12-05T07:27:31+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
@ -2548,68 +2235,6 @@
},
"time": "2021-05-31T10:40:46+00:00"
},
{
"name": "symfony-bundles/json-request-bundle",
"version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/symfony-bundles/json-request-bundle.git",
"reference": "53af1ff1df5457ea16d18cebab1309e667ea86c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony-bundles/json-request-bundle/zipball/53af1ff1df5457ea16d18cebab1309e667ea86c4",
"reference": "53af1ff1df5457ea16d18cebab1309e667ea86c4",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=7.4 || ^8.0",
"symfony/framework-bundle": "^5.0"
},
"require-dev": {
"doctrine/annotations": "^1.0",
"phpstan/phpstan": "^0.12",
"phpunit/php-code-coverage": "^9.2",
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "^5.2",
"symfony/yaml": "^5.2"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"SymfonyBundles\\JsonRequestBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Dmitry Khaperets",
"email": "khaperets@gmail.com"
}
],
"description": "Symfony JsonRequest Bundle",
"homepage": "https://github.com/symfony-bundles/json-request-bundle",
"keywords": [
"angular",
"bundle",
"json",
"symfony"
],
"support": {
"issues": "https://github.com/symfony-bundles/json-request-bundle/issues",
"source": "https://github.com/symfony-bundles/json-request-bundle/tree/4.0.5"
},
"time": "2021-02-16T11:16:20+00:00"
},
{
"name": "symfony/asset",
"version": "v5.3.2",
@ -5561,74 +5186,6 @@
],
"time": "2021-05-27T09:27:20+00:00"
},
{
"name": "symfony/polyfill-php56",
"version": "v1.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php56.git",
"reference": "54b8cd7e6c1643d78d011f3be89f3ef1f9f4c675"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/54b8cd7e6c1643d78d011f3be89f3ef1f9f4c675",
"reference": "54b8cd7e6c1643d78d011f3be89f3ef1f9f4c675",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "metapackage",
"extra": {
"branch-alias": {
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php56/tree/v1.20.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-23T14:02:19+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.23.0",
@ -10385,5 +9942,5 @@
"ext-iconv": "*"
},
"platform-dev": [],
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.0.0"
}

View File

@ -12,6 +12,4 @@ return [
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
SymfonyBundles\JsonRequestBundle\JsonRequestBundle::class => ['all' => true],
];

View File

@ -1,4 +0,0 @@
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'

View File

@ -1,41 +1,17 @@
security:
# https://symfony.com/doc/current/security/experimental_authenticators.html
enable_authenticator_manager: true
password_hashers:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
# used to reload user from session & other features (e.g. switch_user)
# used to reload user from session & other features (e.g. switch_user)
users_in_memory: { memory: null }
firewalls:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
provider: users_in_memory
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
@ -47,6 +23,3 @@ security:
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
# - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210723170329 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SEQUENCE "user_id_seq" INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE "user" (id INT NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('DROP SEQUENCE "user_id_seq" CASCADE');
$this->addSql('DROP TABLE "user"');
}
}

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210723171757 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE "user" ADD email VARCHAR(255) NOT NULL');
$this->addSql('ALTER TABLE "user" ADD name VARCHAR(255) NOT NULL');
$this->addSql('ALTER TABLE "user" ADD surname VARCHAR(255) NOT NULL');
$this->addSql('ALTER TABLE "user" ADD avatar VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE "user" ADD country VARCHAR(3) DEFAULT NULL');
$this->addSql('ALTER TABLE "user" ADD state VARCHAR(10) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE "user" DROP email');
$this->addSql('ALTER TABLE "user" DROP name');
$this->addSql('ALTER TABLE "user" DROP surname');
$this->addSql('ALTER TABLE "user" DROP avatar');
$this->addSql('ALTER TABLE "user" DROP country');
$this->addSql('ALTER TABLE "user" DROP state');
}
}

View File

@ -1,3 +0,0 @@
cd ../../docker
docker.exe compose -p curenet exec app bash -c 'cd /web/backend && bash'
cd src/backend

View File

@ -1,66 +0,0 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Traits\JsonResponseTrait;
use App\Repository\UserRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
class AuthController extends AbstractController
{
use JsonResponseTrait;
public function __construct(private UserRepository $userRepository) {
}
#[Route('/api/register', methods: ["POST"], name: 'register')]
public function register(Request $request, UserPasswordHasherInterface $encoder)
{
$em = $this->getDoctrine()->getManager();
$name = $request->get('name');
$surname = $request->get('surname');
$password = $request->get('password');
$email = $request->get('email');
if (empty($name) || empty($password) || empty($email)){
return $this->notAcceptable("Invalid Username or Password or Email");
}
$existing = $this->userRepository->findByEmail($email);
if ($existing) {
return $this->notAcceptable("User email exists");
}
$user = new User();
$user->setPassword($encoder->hashPassword($user, $password));
$user->setEmail($email);
$user->setName($name);
$user->setSurname($surname);
$em->persist($user);
$em->flush();
return $this->created($user);
}
#[Route('/api/login', methods: ['POST'], name: 'login')]
public function login(JWTTokenManagerInterface $JWTManager)
{
$user = new User();
return $this->ok(['token' => $JWTManager->create($user)]);
}
#[Route('/api/user-check', methods: ['POST'])]
public function checkUser() {
$user = $this->getUser();
if (!$user) {
return $this->unauthorized([]);
}
$user->generateAvatar();
return $this->ok($user);
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Entity\Abstraction;
use ReflectionClass;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class BaseEntity {
protected array $hidden = [];
protected array $map = [];
private array $systemParams = ['hidden', 'map', 'systemParams'];
public function toArray() {
$output = [];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers);
$array = $serializer->normalize($this, null);
$hidden = array_merge($this->hidden, $this->systemParams);
foreach ($array as $key => $value) {
if (in_array($key, $hidden)) {
continue;
}
if (isset($this->map[$key])) {
$key = $this->map[$key];
}
$output[$key] = $value;
}
return $output;
}
}

View File

@ -1,235 +0,0 @@
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\UserRepository;
use App\Entity\Abstraction\BaseEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
/**
* @ORM\Entity(repositoryClass=UserRepository::class)
* @ORM\Table(name="`user`")
*/
class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUserInterface, JWTUserInterface
{
protected array $hidden = ['password', 'salt', 'userIdentifier', 'username'];
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
/**
* @ORM\Column(type="string", length=255)
*/
private $email;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="string", length=255)
*/
private $surname;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $avatar;
/**
* @ORM\Column(type="string", length=3, nullable=true)
*/
private $country;
/**
* @ORM\Column(type="string", length=10, nullable=true)
*/
private $state;
public static function createFromPayload($username, array $payload)
{
$user = new User();
return $user;
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): self
{
$this->id = $id;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* @deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* @see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getSurname(): ?string
{
return $this->surname;
}
public function setSurname(string $surname): self
{
$this->surname = $surname;
return $this;
}
public function getAvatar(): ?string
{
return $this->avatar;
}
public function setAvatar(?string $avatar): self
{
$this->avatar = $avatar;
return $this;
}
public function getCountry(): ?string
{
return $this->country;
}
public function setCountry(?string $country): self
{
$this->country = $country;
return $this;
}
public function getState(): ?string
{
return $this->state;
}
public function setState(?string $state): self
{
$this->state = $state;
return $this;
}
public function generateAvatar() {
if (!$this->avatar) {
$this->avatar = 'https://www.gravatar.com/avatar/' . md5($this->email) . '?s=514&d=robohash';
}
}
}

View File

@ -1,81 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
}
$user->setPassword($newHashedPassword);
$this->_em->persist($user);
$this->_em->flush();
}
/**
* @return User[] Returns an array of User objects
*/
public function findByEmail($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.email = :val')
->setParameter('val', $value)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult()
;
}
// /**
// * @return User[] Returns an array of User objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?User
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\Traits;
use App\Entity\Abstraction\BaseEntity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
trait JsonResponseTrait {
protected function ok($data, $code = Response::HTTP_OK, $headers = []) {
return $this->response($data, $code, $headers);
}
protected function created($data, $code = Response::HTTP_CREATED, $headers = []) {
return $this->response($data, $code, $headers);
}
protected function notAcceptable($data, $code = Response::HTTP_NOT_ACCEPTABLE, $headers = []) {
return $this->response([ 'error' => 'Not acceptable', 'data' => $data], $code, $headers);
}
protected function unauthorized($data, $code = Response::HTTP_UNAUTHORIZED, $headers = []) {
return $this->response([ 'error' => 'Unauthorized', 'data' => $data], $code, $headers);
}
protected function response($data, $code = Response::HTTP_OK, $headers = []) {
if ($data instanceof BaseEntity) {
$data = $data->toArray();
}
return new JsonResponse($data, $code, $headers);
}
}

View File

@ -91,33 +91,12 @@
"laminas/laminas-code": {
"version": "4.4.2"
},
"lcobucci/clock": {
"version": "2.0.0"
},
"lcobucci/jwt": {
"version": "4.1.4"
},
"lexik/jwt-authentication-bundle": {
"version": "2.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.5",
"ref": "5b2157bcd5778166a5696e42f552ad36529a07a6"
},
"files": [
"config/packages/lexik_jwt_authentication.yaml"
]
},
"monolog/monolog": {
"version": "2.3.1"
},
"myclabs/deep-copy": {
"version": "1.10.2"
},
"namshi/jose": {
"version": "7.2.3"
},
"nikic/php-parser": {
"version": "v4.12.0"
},
@ -243,9 +222,6 @@
"config/packages/sensio_framework_extra.yaml"
]
},
"symfony-bundles/json-request-bundle": {
"version": "4.0.5"
},
"symfony/asset": {
"version": "v5.3.2"
},
@ -466,9 +442,6 @@
"symfony/polyfill-mbstring": {
"version": "v1.23.0"
},
"symfony/polyfill-php56": {
"version": "v1.20.0"
},
"symfony/polyfill-php73": {
"version": "v1.23.0"
},

View File

@ -1,18 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

View File

@ -1,16 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -1,46 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

View File

@ -1,27 +0,0 @@
# Frontend
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.8.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -1,164 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"frontend": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss",
"skipTests": true
},
"@schematics/angular:class": {
"skipTests": true
},
"@schematics/angular:directive": {
"skipTests": true
},
"@schematics/angular:guard": {
"skipTests": true
},
"@schematics/angular:interceptor": {
"skipTests": true
},
"@schematics/angular:module": {
},
"@schematics/angular:pipe": {
"skipTests": true
},
"@schematics/angular:service": {
"skipTests": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
{
"input": "src/app/styles/light.scss",
"bundleName": "light",
"inject": false
},
{
"input": "src/app/styles/dark.scss",
"bundleName": "dark",
"inject": false
}
],
"scripts": [],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
},
"defaultConfiguration": ""
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "frontend:build"
},
"configurations": {
"production": {
"browserTarget": "frontend:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "frontend:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "frontend:serve"
},
"configurations": {
"production": {
"devServerTarget": "frontend:serve:production"
}
}
}
}
}},
"defaultProject": "frontend"
}

View File

@ -1,36 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('frontend app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}

View File

@ -1,14 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/frontend'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +0,0 @@
{
"name": "frontend",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~12.1.3",
"@angular/cdk": "^12.1.3",
"@angular/common": "~12.1.3",
"@angular/compiler": "~12.1.3",
"@angular/core": "~12.1.3",
"@angular/forms": "~12.1.3",
"@angular/material": "^12.1.3",
"@angular/platform-browser": "~12.1.3",
"@angular/platform-browser-dynamic": "~12.1.3",
"@angular/router": "~12.1.3",
"@ng-stack/forms": "^2.4.0",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"bootstrap": "^5.0.2",
"font-awesome": "^4.7.0",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.1.3",
"@angular/cli": "~12.1.3",
"@angular/compiler-cli": "~12.1.3",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.3.5"
}
}

View File

@ -1,3 +0,0 @@
cd ../../docker
docker.exe compose -p curenet exec app bash -c 'cd /web/frontend && bash'
cd ../src/frontend

View File

@ -1,134 +0,0 @@
import { NgModule } from '@angular/core';
import { Route, RouterModule } from '@angular/router';
import { AuthGuard } from './modules/auth/services/auth/auth.guard';
import { NotFoundComponent } from './modules/shared/components/not-found/not-found.component';
interface AppMenuEntry {
label: string;
icon: string;
path?: string;
matchExact?: boolean;
level?: number;
}
interface AppRoute extends Route {
menuEntries?: AppMenuEntry[];
}
export const appRoutes: AppRoute[] = [
{
path: 'auth',
loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule),
},
{
path: '',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.HOME',
icon: 'fa fa-home',
matchExact: true,
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'profile',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.PROFILE',
icon: 'fa fa-user',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'community',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.COMMUNITY',
icon: 'fa fa-users',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'questions',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.QUESTIONS',
icon: 'fa fa-question',
level: 1,
}
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'messages',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.MESSAGES',
icon: 'fa fa-comments',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'tests',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.TESTS',
icon: 'fa fa-heartbeat',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'research',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.RESEARCH',
icon: 'fa fa-flask',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'admin',
loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule),
menuEntries: [
{
label: 'MENU.ADMIN',
icon: 'fa fa-id-card',
matchExact: true,
},
{
path: 'admin/users',
label: 'MENU.USERS',
icon: 'fa fa-users',
level: 1,
}
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -1,79 +0,0 @@
<ng-template #toolbar>
<mat-toolbar class="toolbar-background">
<button mat-icon-button (click)="toggleSidebar()" *ngIf="!isSidebarHidden">
<i class="fa fa-bars"></i>
</button>
<span>{{ 'APP.NAME' | translate }}</span>
<span class="flex-grow-1"></span>
<div>
<div *ngFor="let component of dynamicToolbarComponents">
<ng-container [ngComponentOutlet]="component"></ng-container>
</div>
</div>
</mat-toolbar>
</ng-template>
<ng-container *ngIf="!isMobile">
<ng-container *ngTemplateOutlet="toolbar"></ng-container>
</ng-container>
<mat-drawer-container class="flex-grow-1">
<mat-drawer [mode]="sidebarMode" [opened]="isSidebarOpen" (openedChange)="onSidebarOpenedChange($event)" *ngIf="!isSidebarHidden">
<div class="menu">
<div>
<ng-container *ngFor="let route of appRoutes">
<ng-container *ngIf="route.menuEntries">
<div
*ngFor="let menu of route.menuEntries"
class="menu-item"
[attr.style]="menu.level ? 'margin-left: '+(menu.level * 20)+'px' : ''"
[routerLink]="menu.path ? menu.path : route.path"
routerLinkActive="active"
[routerLinkActiveOptions]="{exact: menu.matchExact}">
<i [class]="menu.icon" [title]="menu.label | translate"></i>
<span>{{ menu.label | translate }}</span>
</div>
</ng-container>
</ng-container>
</div>
<div>
<div class="d-flex user-box" *ngIf="user">
<div class="avatar" [style.background-image]="url(user.avatar)"></div>
<div class="d-flex align-items-center flex-grow-1 p-2">{{ user.name }} {{ user.surname }}</div>
<div class="user-menu-container">
<div class="user-menu">
<button mat-icon-button [matMenuTriggerFor]="usermenu">
<i class="fa fa-cog"></i>
</button>
</div>
<mat-menu #usermenu="matMenu" yPosition="above" xPosition="before" class="w-220">
<mat-form-field class="w-100 px-3">
<mat-label>{{ 'APP.LANGUAGE' | translate }}</mat-label>
<mat-select [formControl]="langControl">
<mat-option *ngFor="let lang of langs" [value]="lang">{{ 'LANGUAGE.'+lang.toUpperCase() | translate }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="w-100 px-3">
<mat-label>{{ 'APP.THEME' | translate }}</mat-label>
<mat-select [formControl]="themeControl">
<mat-option *ngFor="let theme of themes" [value]="theme">{{ 'THEME.'+theme.toUpperCase() | translate }}</mat-option>
</mat-select>
</mat-form-field>
<button mat-menu-item (click)="logout()">
Wyloguj
</button>
</mat-menu>
</div>
</div>
</div>
</div>
</mat-drawer>
<ng-container *ngIf="isMobile">
<ng-container *ngTemplateOutlet="toolbar"></ng-container>
</ng-container>
<div class="example-sidenav-content">
<router-outlet></router-outlet>
</div>
</mat-drawer-container>

View File

@ -1,91 +0,0 @@
$sidebar-width: 250px;
:host{
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.menu{
width: $sidebar-width;
flex-grow: 1;
display: flex;
flex-direction: column;
height: 100%;
> :first-child{
flex-grow: 1;
}
> * {
width: $sidebar-width;
}
}
.avatar{
width: 48px;
height: 48px;
border-radius: 10px;
background-color: rgba(0,0,0, 0.25);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.menu-item{
display: flex;
position: relative;
cursor: pointer;
padding: 10px;
transition-duration: 400ms !important;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !important;
transition-property: all !important;
&:after{
display: block;
content: "";
position: absolute;
top: 50%;
right: 0px;
width: 3px;
height: 0%;
background: var(--primary-color);
transition-duration: 400ms !important;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !important;
transition-property: all !important;
}
&.active{
&:after{
top: 0%;
height: 100%;
}
}
i {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
transition: all 0.4s ease;
}
span{
display: flex;
align-items: center;
flex-grow: 1;
opacity: 0.75;
}
}
.user-box{
padding: 6px;
}
.user-menu-container{
width: 46px;
.user-menu{
position: absolute;
bottom: 6px;
right: 6px;
height: 48px;
width: 46px;
display: flex;
align-items: center;
justify-content: center;
}
}

View File

@ -1,162 +0,0 @@
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { appRoutes } from './app-routing.module';
import { AppService, WindowSize } from './modules/shared/services/app/app.service';
import { BrowserStorageService } from './modules/shared/services/browser-storage/browser-storage.service';
import { ThemeService } from './modules/shared/services/theme/theme.service';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { AuthService } from './modules/auth/services/auth/auth.service';
import { Router } from '@angular/router';
import { UserModel } from './modules/auth/models/user.model';
enum SidebarOpenEnum {
Default,
Open,
Closed,
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
isMobile = true;
title = 'CureNet';
sidebarOpen = SidebarOpenEnum.Default;
sidebarMode = 'over';
defaultSidebarOpen = false;
lang: string;
theme: string;
langs: string[] = [];
themes: string[] = [];
themeControl = new FormControl();
langControl = new FormControl();
appRoutes = appRoutes;
user: UserModel;
isSidebarHidden = false;
dynamicToolbarComponents = [];
get isSidebarOpen(): boolean {
switch (this.sidebarOpen) {
case SidebarOpenEnum.Open:
return true;
case SidebarOpenEnum.Closed:
return false;
default:
return this.defaultSidebarOpen;
}
}
constructor(
private appService: AppService,
private themeService: ThemeService,
private browserStorageService: BrowserStorageService,
private sanitizer: DomSanitizer,
private authService: AuthService,
private router: Router,
) {
this.configureSidebarEvents();
this.configureThemeEvents();
this.configureLanguageEvents();
this.configureResizeEvents();
this.configureUserEvents();
}
configureUserEvents(): void {
this.user = this.authService.getUser();
this.authService.userChange.subscribe(user => {
this.user = user;
});
}
configureSidebarEvents(): void {
this.isSidebarHidden = this.appService.getSidebarHidden();
this.appService.sidebarHiddenChange.subscribe(hidden => {
this.isSidebarHidden = hidden;
});
const sidebarUserPrefference = this.browserStorageService.getItem('sidebar.open');
if (sidebarUserPrefference !== null) {
this.sidebarOpen = sidebarUserPrefference;
}
this.appService.dynamicToolbarComponentsChange.subscribe(components => {
this.dynamicToolbarComponents = components;
});
}
configureThemeEvents(): void {
this.themes = this.themeService.getThemes();
this.themeControl.valueChanges.subscribe(theme => {
this.themeService.setTheme(theme);
});
this.onThemeChanged(this.themeService.getTheme());
this.themeService.themeChange.subscribe(theme => {
this.onThemeChanged(theme);
});
}
configureLanguageEvents(): void {
this.langs = this.appService.getLangs();
this.lang = this.appService.getLang();
this.appService.changeLang(this.lang);
this.langControl.valueChanges.subscribe(lang => {
this.appService.changeLang(lang);
});
this.onLangChanged(this.appService.getLang());
this.appService.langChange.subscribe(lang => {
this.onLangChanged(lang);
});
}
configureResizeEvents(): void {
this.onResize(this.appService.size);
this.appService.sizeChange.subscribe(size => {
this.onResize(size);
});
}
onThemeChanged(theme: string): void {
if (theme === undefined) {
return;
}
this.theme = theme;
this.themeControl.setValue(theme);
}
onResize(size: WindowSize): void {
if (size === undefined) {
return;
}
this.isMobile = size.isMobile;
this.defaultSidebarOpen = !size.isMobile;
this.sidebarMode = this.isMobile ? 'over' : 'side';
}
onLangChanged(lang: string): void {
if (lang === undefined) {
return;
}
this.lang = lang;
this.langControl.setValue(lang);
}
toggleSidebar(): void {
this.sidebarOpen = this.isSidebarOpen ? SidebarOpenEnum.Closed : SidebarOpenEnum.Open;
this.browserStorageService.setItem('sidebar.open', this.sidebarOpen);
}
onSidebarOpenedChange(opened: boolean): void {
this.sidebarOpen = opened ? SidebarOpenEnum.Open : SidebarOpenEnum.Closed;
}
url(url: string): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url('${url}')`);
}
logout(): void {
this.authService.logout();
this.router.navigateByUrl('/');
}
}

View File

@ -1,53 +0,0 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialModule } from './modules/material/material.module';
import { AppService } from './modules/shared/services/app/app.service';
import { ThemeService } from './modules/shared/services/theme/theme.service';
import { BrowserStorageService } from './modules/shared/services/browser-storage/browser-storage.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TokenInterceptor } from './modules/shared/interceptors/token/token.interceptor';
import { AuthTokenService } from './modules/shared/services/auth/auth-token.service';
import { AuthService } from './modules/auth/services/auth/auth.service';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, '/assets/lang/', '.json');
}
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
AppService,
ThemeService,
BrowserStorageService,
AuthTokenService,
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
import { UsersComponent } from './components/users/users.component';
const routes: Route[] = [
{
path: '',
component: NotFoundComponent,
},
{
path: 'users',
component: UsersComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule { }

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { SharedModule } from '../shared/shared.module';
import { UsersComponent } from './components/users/users.component';
import { AdminUserService } from './services/admin-user/admin-user.service';
@NgModule({
declarations: [
UsersComponent
],
imports: [
CommonModule,
AdminRoutingModule,
SharedModule,
],
providers: [
AdminUserService,
],
})
export class AdminModule { }

View File

@ -1 +0,0 @@
<p>users works!</p>

View File

@ -1,18 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { AdminUserService } from '../../services/admin-user/admin-user.service';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
export class UsersComponent implements OnInit {
constructor(private adminUserService: AdminUserService) { }
ngOnInit(): void {
this.adminUserService.getUsers().subscribe(() => {
});
}
}

View File

@ -1,14 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { UserModel } from 'src/app/modules/auth/models/user.model';
@Injectable()
export class AdminUserService {
constructor(private http: HttpClient) { }
getUsers(): Observable<UserModel[]> {
return this.http.get<UserModel[]>('/api/admin/users');
}
}

View File

@ -1,31 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
import { AuthComponent } from './components/auth/auth.component';
import { AuthTabEnum } from './enums/auth-tab.enum';
const routes: Route[] = [
{
path: AuthTabEnum.Login,
component: AuthComponent,
},
{
path: AuthTabEnum.Register,
component: AuthComponent,
},
{
path: AuthTabEnum.RestorePassword,
component: AuthComponent,
},
{
path: '**',
redirectTo: 'login',
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthRoutingModule { }

View File

@ -1,29 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthRoutingModule } from './auth-routing.module';
import { AuthComponent } from './components/auth/auth.component';
import { ThemeSwitcherComponent } from './components/theme-switcher/theme-switcher.component';
import { MaterialModule } from '../material/material.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from './services/auth/auth.service';
import { NgStackFormsModule } from '@ng-stack/forms';
@NgModule({
declarations: [
AuthComponent,
ThemeSwitcherComponent,
],
imports: [
CommonModule,
AuthRoutingModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
NgStackFormsModule,
],
providers: [
AuthService,
],
})
export class AuthModule { }

View File

@ -1,66 +0,0 @@
<div class="primary-background toolbar-background"></div>
<div class="col-11 col-lg-7 col-xl-5 p-3 content">
<mat-card class="px-0">
<mat-tab-group mat-align-tabs="center" [selectedIndex]="selectedIndex" (selectedIndexChange)="selectedIndexChange($event)">
<mat-tab label="Logowanie">
<div [formGroup]="loginForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.RestorePassword))">Odzyskaj hasło</button>
<button mat-flat-button color="primary" (click)="login()">Zaloguj</button>
</div>
</div>
</mat-tab>
<mat-tab label="Rejestracja">
<div [formGroup]="registerForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Imię</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Imię</mat-label>
<input formControlName="name" matInput>
</mat-form-field>
<div class="col-12 col-md-4">Nazwisko</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Nazwisko</mat-label>
<input formControlName="surname" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-flat-button color="primary" (click)="register()">Zarejestruj</button>
</div>
</div>
</mat-tab>
<mat-tab label="Odzyskiwanie hasła" *ngIf="selectedIndex === 2">
<div [formGroup]="restoreForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.Login))">Anuluj</button>
<button mat-flat-button color="primary">Odzyskaj hasło</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>

View File

@ -1,9 +0,0 @@
.primary-background{
background-color: var(--toolbar-background);
height: 200px;
}
.content{
margin: 0px auto;
margin-top: -150px;
height: 200px;
}

View File

@ -1,118 +0,0 @@
import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router, UrlSegment } from '@angular/router';
import { FormBuilder, FormGroup } from '@ng-stack/forms';
import { AppService } from 'src/app/modules/shared/services/app/app.service';
import { AuthTabEnum } from '../../enums/auth-tab.enum';
import { LoginModel } from '../../models/login.model';
import { RegisterModel } from '../../models/register.model';
import { RestoreModel } from '../../models/restore.model';
import { AuthService } from '../../services/auth/auth.service';
import { ThemeSwitcherComponent } from '../theme-switcher/theme-switcher.component';
@Component({
selector: 'app-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss']
})
export class AuthComponent implements OnInit, OnDestroy {
registerForm: FormGroup<RegisterModel>;
loginForm: FormGroup<LoginModel>;
restoreForm: FormGroup<RestoreModel>;
tab: AuthTabEnum;
selectedIndex: number;
defaultTab = AuthTabEnum.Login;
tabIndexes = [
AuthTabEnum.Login,
AuthTabEnum.Register,
AuthTabEnum.RestorePassword,
];
showRestore = false;
AuthTabEnum = AuthTabEnum;
constructor(
private appService: AppService,
private authService: AuthService,
private activatedRoute: ActivatedRoute,
private router: Router,
private location: Location,
formBuilder: FormBuilder,
) {
this.registerForm = formBuilder.group<RegisterModel>({
name: 'Name',
surname: 'Surname',
email: 'email@test.com',
password: 'password',
});
this.loginForm = formBuilder.group<LoginModel>({
email: 'email@test.com',
password: 'password',
});
this.restoreForm = formBuilder.group<RestoreModel>({
email: 'email@test.com',
});
this.handleUrl(activatedRoute.snapshot.url);
activatedRoute.url.subscribe(url => {
this.handleUrl(url);
});
}
handleUrl(url: UrlSegment[]): void {
const path = url[0].path;
this.tab = path as AuthTabEnum;
this.selectedIndex = this.getSelectedIndex();
}
getSelectedIndex(tab: AuthTabEnum = null): number {
if (tab === null) {
tab = this.tab;
}
let index = this.tabIndexes.findIndex(i => i === tab);
if (index === -1) {
index = this.tabIndexes.findIndex(i => i === this.defaultTab);
}
return index;
}
getIndexTab(): AuthTabEnum {
return this.tabIndexes[this.selectedIndex];
}
selectedIndexChange(index: number): void {
this.selectedIndex = index;
const tab = this.getIndexTab();
const url = this.router.createUrlTree(['..', tab], {
relativeTo: this.activatedRoute,
}).toString();
this.location.go(url);
}
ngOnInit(): void {
this.appService.setSidebarHidden(true);
this.appService.addDynamicToolbarComponent(ThemeSwitcherComponent);
}
ngOnDestroy(): void {
this.appService.setSidebarHidden(false);
this.appService.removeDynamicToolbarComponent(ThemeSwitcherComponent);
}
register(): void {
this.authService.createAccount(this.registerForm.getRawValue()).subscribe(data => {
this.loginForm.patchValue({
email: this.registerForm.controls.email.value,
password: this.registerForm.controls.password.value,
});
this.selectedIndexChange(this.getSelectedIndex(AuthTabEnum.Login));
});
}
login(): void {
this.authService.login(this.loginForm.getRawValue()).subscribe(data => {
const afterUrl = '/';
this.router.navigateByUrl(afterUrl);
});
}
}

View File

@ -1,3 +0,0 @@
<button mat-icon-button (click)="toggleTheme()">
<i class="fa" [class.fa-sun-o]="isDark" [class.fa-moon-o]="!isDark"></i>
</button>

View File

@ -1,32 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ThemeService } from 'src/app/modules/shared/services/theme/theme.service';
@Component({
selector: 'app-theme-switcher',
templateUrl: './theme-switcher.component.html',
styleUrls: ['./theme-switcher.component.scss']
})
export class ThemeSwitcherComponent implements OnInit {
isDark = false;
darkTheme = 'dark';
lightTheme = 'light';
theme = 'light';
constructor(
private themeService: ThemeService,
) {
this.theme = themeService.getTheme();
themeService.themeChange.subscribe(theme => {
this.theme = theme;
});
}
ngOnInit(): void {
}
toggleTheme(): void {
this.themeService.setTheme(this.theme === this.darkTheme ? this.lightTheme : this.darkTheme);
this.isDark = this.theme === this.darkTheme;
}
}

View File

@ -1,5 +0,0 @@
export enum AuthTabEnum {
Login = 'login',
Register = 'register',
RestorePassword = 'restore-password',
}

View File

@ -1,4 +0,0 @@
export interface LoginModel {
email: string;
password: string;
}

View File

@ -1,6 +0,0 @@
export interface RegisterModel {
name: string;
surname: string;
email: string;
password: string;
}

View File

@ -1,3 +0,0 @@
export interface RestoreModel {
email: string;
}

View File

@ -1,3 +0,0 @@
export interface TokenResponse {
token: string;
}

View File

@ -1,8 +0,0 @@
export interface UserModel {
id: number;
name: string;
surname: string;
avatar: string | null;
country: string | null;
state: string | null;
}

View File

@ -1,34 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthTokenService } from 'src/app/modules/shared/services/auth/auth-token.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authTokenService: AuthTokenService, private router: Router) {
}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const can = this.authTokenService.userValidate();
if (!can){
this.router.navigate(['/auth']);
}
return can;
}
canActivateChild(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const can = this.authTokenService.userValidate();
if (!can){
this.router.navigate(['/auth']);
}
return can;
}
}

View File

@ -1,68 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { LoginModel } from '../../models/login.model';
import { RegisterModel } from '../../models/register.model';
import { TokenResponse } from '../../models/token.response';
import { UserModel } from '../../models/user.model';
import { AuthTokenService } from '../../../shared/services/auth/auth-token.service';
import { Router } from '@angular/router';
@Injectable()
export class AuthService {
private static updateAgent: any;
private user: UserModel;
public userChange = new Subject<UserModel>();
constructor(
private http: HttpClient,
private authTokenService: AuthTokenService,
private router: Router,
) {
this.checkUser();
this.userChange.subscribe(user => {
this.user = user;
});
}
getUser(): UserModel {
return this.user;
}
checkUser(): void {
this.http.post<UserModel>('/api/user-check', {}).subscribe(user => {
this.userChange.next(user);
if (AuthService.updateAgent) {
clearTimeout(AuthService.updateAgent);
}
AuthService.updateAgent = setTimeout(() => {
this.checkUser();
}, 2000) as any;
}, () => {
this.logout();
});
}
createAccount(data: RegisterModel): Observable<UserModel> {
return this.http.post<UserModel>('/api/register', data);
}
login(data: LoginModel): Observable<TokenResponse> {
const out = new Subject<TokenResponse>();
this.http.post<TokenResponse>('/api/login', {
username: data.email,
password: data.password,
}).subscribe(response => {
this.authTokenService.setToken(response.token);
this.checkUser();
out.next(response);
});
return out;
}
logout(): void {
this.authTokenService.removeToken();
this.router.navigate(['/auth']);
}
}

View File

@ -1,33 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {MatToolbarModule} from '@angular/material/toolbar';
import {MatButtonModule} from '@angular/material/button';
import {MatSidenavModule} from '@angular/material/sidenav';
import {MatSelectModule} from '@angular/material/select';
import {MatMenuModule} from '@angular/material/menu';
import {MatCardModule} from '@angular/material/card';
import {MatTabsModule} from '@angular/material/tabs';
import {MatInputModule} from '@angular/material/input';
const itemsToExport = [
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatSelectModule,
MatMenuModule,
MatCardModule,
MatTabsModule,
MatInputModule,
];
@NgModule({
declarations: [],
imports: [
CommonModule,
...itemsToExport,
],
exports: [
...itemsToExport,
]
})
export class MaterialModule { }

View File

@ -1,10 +0,0 @@
<div class="primary-background toolbar-background"></div>
<div class="content align-items-center justify-content-center">
<div class="col-11 col-lg-7 col-xl-5">
<mat-card>
<div class="m-3 p-3">
{{ 'ERROR.PAGE_NOT_FOUND' | translate }}
</div>
</mat-card>
</div>
</div>

View File

@ -1,10 +0,0 @@
.primary-background{
background-color: var(--toolbar-background);
height: 100px;
}
.content{
margin: 0px auto;
margin-top: -100px;
height: 200px;
display: flex;
}

View File

@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss']
})
export class NotFoundComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -1,20 +0,0 @@
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthTokenService } from 'src/app/modules/shared/services/auth/auth-token.service';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private authTokenService: AuthTokenService) {
}
intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authTokenService.hasToken()) {
const Authorization = `Bearer ${this.authTokenService.getToken()}`;
return next.handle(httpRequest.clone({ setHeaders: { Authorization } }));
}
return next.handle(httpRequest);
}
}

View File

@ -1,9 +0,0 @@
export class User {
id: number;
email: string;
name: string;
surname: string;
avatar: string;
country: string;
state: string;
}

View File

@ -1,110 +0,0 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { fromEvent, Subject } from 'rxjs';
import { BrowserStorageService } from '../browser-storage/browser-storage.service';
export class WindowSize {
isMobile: boolean;
constructor(
public width: number,
public height: number,
) {
this.isMobile = this.width <= 700;
}
static generate(): WindowSize {
return new WindowSize(window.innerWidth, window.innerHeight);
}
update(): WindowSize {
this.width = window.innerWidth;
this.height = window.innerHeight;
return this;
}
}
@Injectable()
export class AppService {
private sidebarHidden = false;
private lang = 'en';
private defaultLang = 'en';
private dynamicToolbarComponents = [];
langChange = new Subject<string>();
size: WindowSize;
sizeChange = new Subject<WindowSize>();
sidebarHiddenChange = new Subject<boolean>();
dynamicToolbarComponentsChange = new Subject<any[]>();
constructor(
private browserStorageService: BrowserStorageService,
private translate: TranslateService,
) {
this.configureResizeEvents();
this.configureLanguageEvents();
this.configureSidebarEvents();
}
configureSidebarEvents(): void {
this.sidebarHiddenChange.subscribe(hidden => {
this.sidebarHidden = hidden;
});
}
configureResizeEvents(): void {
this.sizeChange.subscribe(size => this.size = size);
this.sizeChange.next(WindowSize.generate());
fromEvent(window, 'resize').subscribe(e => {
this.sizeChange.next(WindowSize.generate());
});
}
configureLanguageEvents(): void {
this.langChange.subscribe(lang => {
this.lang = lang;
this.browserStorageService.setItem('language', lang);
});
let language = this.browserStorageService.getItem('language');
if (!language) {
language = this.defaultLang;
}
this.translate.setDefaultLang(this.defaultLang);
this.changeLang(language);
}
addDynamicToolbarComponent(component): void {
this.dynamicToolbarComponents.push(component);
this.dynamicToolbarComponentsChange.next(this.dynamicToolbarComponents);
}
removeDynamicToolbarComponent(component): void {
this.dynamicToolbarComponents = this.dynamicToolbarComponents.filter(c => c !== component);
this.dynamicToolbarComponentsChange.next(this.dynamicToolbarComponents);
}
setSidebarHidden(hidden: boolean): void {
this.sidebarHiddenChange.next(hidden);
}
getSidebarHidden(): boolean {
return this.sidebarHidden;
}
changeLang(lang: string): void {
if (lang !== this.lang) {
this.translate.use(lang);
this.langChange.next(lang);
}
}
getLang(): string {
return this.lang;
}
getLangs(): string[] {
return [
'pl',
'en',
];
}
}

View File

@ -1,34 +0,0 @@
import { Injectable } from '@angular/core';
import { BrowserStorageService } from 'src/app/modules/shared/services/browser-storage/browser-storage.service';
@Injectable()
export class AuthTokenService {
private TokenKey = 'jwt.token';
constructor( private browserStorageService: BrowserStorageService) {
}
setToken(token: string): void {
this.browserStorageService.setItem(this.TokenKey, token);
}
hasToken(): boolean {
const token = this.browserStorageService.getItemOrDefault(this.TokenKey, null);
return token !== null;
}
getToken(): string {
return this.browserStorageService.getItem(this.TokenKey);
}
removeToken(): void {
this.browserStorageService.setItem(this.TokenKey, null);
}
userValidate(): boolean {
if (!this.hasToken()) {
return false;
}
return true;
}
}

View File

@ -1,38 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable()
export class BrowserStorageService {
private storage: Storage;
private namespace: string;
constructor() {
this.storage = localStorage;
this.namespace = 'default';
}
setNamespace(namespace: string): void {
this.namespace = namespace;
}
getNamespace(namespace: string | null = null): string {
return namespace ?? this.namespace;
}
setItem(key: string, value: any, namespace: string | null = null): void {
namespace = this.getNamespace(namespace);
const path = `${namespace}.${key}`;
this.storage.setItem(path, JSON.stringify(value));
}
getItem(key: string, namespace: string | null = null): any {
namespace = this.getNamespace(namespace);
const path = `${namespace}.${key}`;
return JSON.parse(this.storage.getItem(path) ?? 'null');
}
getItemOrDefault(key: string, defaultValue: any, namespace: string | null = null): any {
return this.getItem(key, namespace) ?? defaultValue;
}
}

View File

@ -1,41 +0,0 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { BrowserStorageService } from '../browser-storage/browser-storage.service';
@Injectable()
export class ThemeService {
private theme: string;
themeChange = new Subject<string>();
constructor(
browserStorageService: BrowserStorageService
) {
this.themeChange.subscribe(theme => {
this.theme = theme;
browserStorageService.setItem('current-theme', theme);
});
let userTheme = browserStorageService.getItem('current-theme');
if (!userTheme) {
userTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
this.setTheme(userTheme);
}
getTheme(): string {
return this.theme;
}
setTheme(theme: string): void {
if (theme !== this.theme) {
this.themeChange.next(theme);
document.getElementById('current-theme').setAttribute('href', `/${theme}.css`);
}
}
getThemes(): string[] {
return [
'light',
'dark',
];
}
}

View File

@ -1,26 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module';
import { HttpClient } from '@angular/common/http';
import { MaterialModule } from '../material/material.module';
@NgModule({
declarations: [
NotFoundComponent
],
imports: [
CommonModule,
MaterialModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
})
export class SharedModule { }

View File

@ -1,38 +0,0 @@
@use 'sass:map';
$primary-color: map.get($theme, color, primary, 500);
$toolbar-background: map.get($theme, color, background, app-bar);
$toolbar-color: map.get($theme, color, primary, contrast, 500) !default;
$main-background: map.get($theme, color, background, background) !default;
$warn-color: map.get($theme, warn, 500) !default;
body {
--primary-color: #{$primary-color};
--toolbar-background: #{$toolbar-background};
--toolbar-color: #{$toolbar-color};
--main-background: #{$main-background};
--warn-color: #{$warn-color};
.mat-toolbar{
background-color: var(--toolbar-background);
color: var(--toolbar-color);
}
.toolbar-background{
position: relative;
& > *{
position: relative;
z-index: 2;
}
&:before{
position: absolute;
z-index: 0;
top: 0px;
left: 0px;
width:100%;
height: 100%;
content: "";
opacity: 0.125;
background-image: url('/assets/background/items.png');
background-position: top;
background-attachment: fixed;
}
}
}

View File

@ -1,398 +0,0 @@
$theme: (
color: (
primary: (
50: #e8eaf6,
100: #c5cae9,
200: #9fa8da,
300: #7986cb,
400: #5c6bc0,
500: #3f51b5,
600: #3949ab,
700: #303f9f,
800: #283593,
900: #1a237e,
A100: #8c9eff,
A200: #536dfe,
A400: #3d5afe,
A700: #304ffe,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: white,
400: white,
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #3f51b5,
lighter: #c5cae9,
darker: #303f9f,
text: #3f51b5,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": white,
"400-contrast": white,
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
accent: (
50: #fce4ec,
100: #f8bbd0,
200: #f48fb1,
300: #f06292,
400: #ec407a,
500: #e91e63,
600: #d81b60,
700: #c2185b,
800: #ad1457,
900: #880e4f,
A100: #ff80ab,
A200: #ff4081,
A400: #f50057,
A700: #c51162,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: rgba(0, 0, 0, 0.87),
400: rgba(0, 0, 0, 0.87),
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #ff4081,
lighter: #ff80ab,
darker: #f50057,
text: #ff4081,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": rgba(0, 0, 0, 0.87),
"400-contrast": rgba(0, 0, 0, 0.87),
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
warn: (
50: #ffebee,
100: #ffcdd2,
200: #ef9a9a,
300: #e57373,
400: #ef5350,
500: #f44336,
600: #e53935,
700: #d32f2f,
800: #c62828,
900: #b71c1c,
A100: #ff8a80,
A200: #ff5252,
A400: #ff1744,
A700: #d50000,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: rgba(0, 0, 0, 0.87),
400: rgba(0, 0, 0, 0.87),
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #f44336,
lighter: #ffcdd2,
darker: #d32f2f,
text: #f44336,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": rgba(0, 0, 0, 0.87),
"400-contrast": rgba(0, 0, 0, 0.87),
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
is-dark: true,
foreground: (
base: white,
divider: rgba(255, 255, 255, 0.12),
dividers: rgba(255, 255, 255, 0.12),
disabled: rgba(255, 255, 255, 0.5),
disabled-button: rgba(255, 255, 255, 0.3),
disabled-text: rgba(255, 255, 255, 0.5),
elevation: black,
hint-text: rgba(255, 255, 255, 0.5),
secondary-text: rgba(255, 255, 255, 0.7),
icon: white,
icons: white,
text: white,
slider-min: white,
slider-off: rgba(255, 255, 255, 0.3),
slider-off-active: rgba(255, 255, 255, 0.3),
),
background: (
status-bar: black,
app-bar: #212121,
background: #303030,
hover: rgba(255, 255, 255, 0.04),
card: #424242,
dialog: #424242,
disabled-button: rgba(255, 255, 255, 0.12),
raised-button: #424242,
focused-button: rgba(255, 255, 255, 0.12),
selected-button: #212121,
selected-disabled-button: #424242,
disabled-button-toggle: black,
unselected-chip: #616161,
disabled-list-option: black,
tooltip: #616161,
),
),
primary: (
50: #e8eaf6,
100: #c5cae9,
200: #9fa8da,
300: #7986cb,
400: #5c6bc0,
500: #3f51b5,
600: #3949ab,
700: #303f9f,
800: #283593,
900: #1a237e,
A100: #8c9eff,
A200: #536dfe,
A400: #3d5afe,
A700: #304ffe,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: white,
400: white,
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #3f51b5,
lighter: #c5cae9,
darker: #303f9f,
text: #3f51b5,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": white,
"400-contrast": white,
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
accent: (
50: #fce4ec,
100: #f8bbd0,
200: #f48fb1,
300: #f06292,
400: #ec407a,
500: #e91e63,
600: #d81b60,
700: #c2185b,
800: #ad1457,
900: #880e4f,
A100: #ff80ab,
A200: #ff4081,
A400: #f50057,
A700: #c51162,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: rgba(0, 0, 0, 0.87),
400: rgba(0, 0, 0, 0.87),
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #ff4081,
lighter: #ff80ab,
darker: #f50057,
text: #ff4081,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": rgba(0, 0, 0, 0.87),
"400-contrast": rgba(0, 0, 0, 0.87),
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
warn: (
50: #ffebee,
100: #ffcdd2,
200: #ef9a9a,
300: #e57373,
400: #ef5350,
500: #f44336,
600: #e53935,
700: #d32f2f,
800: #c62828,
900: #b71c1c,
A100: #ff8a80,
A200: #ff5252,
A400: #ff1744,
A700: #d50000,
contrast: (
50: rgba(0, 0, 0, 0.87),
100: rgba(0, 0, 0, 0.87),
200: rgba(0, 0, 0, 0.87),
300: rgba(0, 0, 0, 0.87),
400: rgba(0, 0, 0, 0.87),
500: white,
600: white,
700: white,
800: white,
900: white,
A100: rgba(0, 0, 0, 0.87),
A200: white,
A400: white,
A700: white,
),
default: #f44336,
lighter: #ffcdd2,
darker: #d32f2f,
text: #f44336,
default-contrast: white,
lighter-contrast: rgba(0, 0, 0, 0.87),
darker-contrast: white,
"50-contrast": rgba(0, 0, 0, 0.87),
"100-contrast": rgba(0, 0, 0, 0.87),
"200-contrast": rgba(0, 0, 0, 0.87),
"300-contrast": rgba(0, 0, 0, 0.87),
"400-contrast": rgba(0, 0, 0, 0.87),
"500-contrast": white,
"600-contrast": white,
"700-contrast": white,
"800-contrast": white,
"900-contrast": white,
"A100-contrast": rgba(0, 0, 0, 0.87),
"A200-contrast": white,
"A400-contrast": white,
"A700-contrast": white,
"contrast-contrast": null,
),
is-dark: true,
foreground: (
base: white,
divider: rgba(255, 255, 255, 0.12),
dividers: rgba(255, 255, 255, 0.12),
disabled: rgba(255, 255, 255, 0.5),
disabled-button: rgba(255, 255, 255, 0.3),
disabled-text: rgba(255, 255, 255, 0.5),
elevation: black,
hint-text: rgba(255, 255, 255, 0.5),
secondary-text: rgba(255, 255, 255, 0.7),
icon: white,
icons: white,
text: white,
slider-min: white,
slider-off: rgba(255, 255, 255, 0.3),
slider-off-active: rgba(255, 255, 255, 0.3),
),
background: (
status-bar: black,
app-bar: #212121,
background: #303030,
hover: rgba(255, 255, 255, 0.04),
card: #424242,
dialog: #424242,
disabled-button: rgba(255, 255, 255, 0.12),
raised-button: #424242,
focused-button: rgba(255, 255, 255, 0.12),
selected-button: #212121,
selected-disabled-button: #424242,
disabled-button-toggle: black,
unselected-chip: #616161,
disabled-list-option: black,
tooltip: #616161,
),
);

View File

@ -1,35 +0,0 @@
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@use '~@angular/material' as mat;
// Plus imports for other components in your app.
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat.core();
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-primary: mat.define-palette(mat.$indigo-palette);
$theme-accent: mat.define-palette(mat.$green-palette, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-warn: mat.define-palette(mat.$red-palette);
// Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography".
$theme: mat.define-dark-theme((
color: (
primary: $theme-primary,
accent: $theme-accent,
warn: $theme-warn,
)
));
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include mat.all-component-themes($theme);
@import "./base";

View File

@ -1,40 +0,0 @@
@use "sass:map";
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@use '~@angular/material' as mat;
// Plus imports for other components in your app.
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat.core();
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-primary: mat.define-palette(mat.$blue-palette);
$theme-accent: mat.define-palette(mat.$light-blue-palette, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-warn: mat.define-palette(mat.$red-palette);
// Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography".
$theme: mat.define-light-theme((
color: (
primary: $theme-primary,
accent: $theme-accent,
warn: $theme-warn,
)
));
$toolbar-background: map.get($theme, color, primary, 500);
$toolbar-text: map.get($theme, color, primary, contrast, 500);
$theme: map.set($theme, color, background, app-bar, $toolbar-background);
$theme: map.set($theme, color, foreground, app-bar, $toolbar-text);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include mat.all-component-themes($theme);
@import "./base";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1,30 +0,0 @@
{
"APP": {
"NAME": "CureNet",
"THEME": "Theme",
"LANGUAGE": "Language"
},
"ERROR": {
"PAGE_NOT_FOUND": "Page not found or not implemented yet!"
},
"THEME": {
"LIGHT": "Light",
"DARK": "Dark"
},
"LANGUAGE": {
"PL": "Polish",
"EN": "English"
},
"MENU": {
"HOME": "Dashboard",
"PROFILE": "Profile",
"COMMUNITY": "Community",
"GROUP": "My group",
"MESSAGES": "Messages",
"QUESTIONS": "Q&A",
"TESTS": "Laboratory tests",
"RESEARCH": "Research",
"ADMIN": "Admin",
"USERS": "Users"
}
}

View File

@ -1,30 +0,0 @@
{
"APP": {
"NAME": "CureNet",
"THEME": "Motyw",
"LANGUAGE": "Język"
},
"ERROR": {
"PAGE_NOT_FOUND": "Nie znaleziono strony lub nie została ona jeszcze zaimplementowana!"
},
"THEME": {
"LIGHT": "Jasny",
"DARK": "Ciemny"
},
"LANGUAGE": {
"PL": "Polski",
"EN": "Angielski"
},
"MENU": {
"HOME": "Kokpit",
"PROFILE": "Profil",
"COMMUNITY": "Społeczność",
"GROUP": "Moja grupa",
"MESSAGES": "Wiadomości",
"QUESTIONS": "Pytania i odpowiedzi",
"TESTS": "Wyniki badań",
"RESEARCH": "Badania naukowe",
"ADMIN": "Panel administratora",
"USERS": "Użytkownicy"
}
}

View File

@ -1,5 +0,0 @@
export const environment = {
production: true,
buildId: -1001,
buildTime: -1002,
};

View File

@ -1,18 +0,0 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
buildId: -1001,
buildTime: -1002,
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

Some files were not shown because too many files have changed in this diff Show More