Merge branch 'master' into stage2
@ -43,7 +43,7 @@ services:
|
|||||||
- community-server
|
- community-server
|
||||||
- login-server
|
- login-server
|
||||||
volumes:
|
volumes:
|
||||||
- ./logs:/var/log/nginx
|
- ./logs/nginx:/var/log/nginx
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
## PHPMYADMIN ###########################################
|
## PHPMYADMIN ###########################################
|
||||||
|
|||||||
@ -16,8 +16,6 @@ services:
|
|||||||
target: production
|
target: production
|
||||||
networks:
|
networks:
|
||||||
- external-net
|
- external-net
|
||||||
depends_on:
|
|
||||||
- nginx
|
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
environment:
|
environment:
|
||||||
@ -69,6 +67,7 @@ services:
|
|||||||
- 1201:1201
|
- 1201:1201
|
||||||
volumes:
|
volumes:
|
||||||
- ./configs/login_server:/etc/grd_login
|
- ./configs/login_server:/etc/grd_login
|
||||||
|
- login_build:/code/build_vol
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
## NGINX ################################################
|
## NGINX ################################################
|
||||||
@ -136,3 +135,4 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_vol:
|
db_vol:
|
||||||
|
login_build:
|
||||||
|
|||||||
48
docu/README.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Gradido Documentation
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
The API Documentation is separated by which server answers them. Either the community server or login server. Therefore the documentation is split into two parts accordingly.
|
||||||
|
|
||||||
|
- [login-server API](./login_server.api.md)
|
||||||
|
- [community-server API](./community-server.api.md)
|
||||||
|
|
||||||
|
## Graphics
|
||||||
|
|
||||||
|
The following Graphics try to show certain mechanics, principles and structures of the gradido project
|
||||||
|
|
||||||
|
### Cookie mechanic
|
||||||
|

|
||||||
|
|
||||||
|
### Software structure transition
|
||||||
|
|
||||||
|
An Idea of how to transform the Software structure step by step to keep it working, while transitioning to a more modern and strict structure.
|
||||||
|
The basic Idea is to create a unified API to communicate with the outside world, while the existing services are hidden behind it. Furthermore the community server is under consideration to be absorbed by this new unified API Interface. This would remove PHP as language from the project, unify the API and separate front from backend.
|
||||||
|
|
||||||
|
In the long run (shown as the last section in the graphic) it could be wise to fully decentralize the login server completely.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Old or outdated stuff
|
||||||
|
|
||||||
|
Graphics get outdated - this section is the graveyard for those.
|
||||||
|
|
||||||
|
#### Software release plan stage0-3
|
||||||
|
|
||||||
|
In the past a waterfall-like development method was used which resulted in multiple big branches not yet compatible. This graphic shows how and in which order those branches o existing code could be integrated.
|
||||||
|
|
||||||
|
We quickly managed to go from `stage0` to `stage1`. `stage2` and `stage3` will be one step now, if we can reuse this code.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Frontend authentication idea
|
||||||
|
|
||||||
|
Some ideas of regarding fixing the broken authentication
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Repo structure
|
||||||
|
|
||||||
|
One upon a time the repository was split into multiple parts. This compartimalisation resulted in some inefficiencies which were resolved by merging (most of) them into a mono repository. The following graphic was made to explain the idea and plan the "mono-repoisation"
|
||||||
|
|
||||||
|

|
||||||
76
docu/comment_dario.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
## Software structure transition
|
||||||
|
I would be happy if you label GraphQL more detailed for example "new community server with GraphQL-Interface"
|
||||||
|
Everytime I read only GraphQL its trigger me, because GraphQL is only a standard not a server.
|
||||||
|
I don't think it is wise to remove the login-server complete, because than user left only with two options sending gradidos securly:
|
||||||
|
- with a native mobile app (saving keys in secure storage)
|
||||||
|
- Managing there keys/passphrase themself and copy them into client everytime they want so send gradidos like with webwallets (https://www.myetherwallet.com/access-my-wallet)
|
||||||
|
|
||||||
|
My suggestion is to see the login-server as third option for inexperienced users to handle Gradidos as easy as paypal.
|
||||||
|
So the Login-Server can be optional in your graphic.
|
||||||
|
When the new community-server gets already signed transactions he can send it directly to the blockchain/blockchain-gate, if not signed he can send it to login-server for signing. We can change it so that the new community-server gets signed transactions back so login-server only sign transactions, not sending them anymore into the blockchain-network,
|
||||||
|
|
||||||
|
## Frontend authentication idea
|
||||||
|
Why the extra step with JWT?
|
||||||
|
The Login-Server save the session-id in Cookie GRADIDO_LOGIN and if vue is running from same domain it could access the cookie directly. But more important the browser is sending the cookie automaticly so I need only to change Login-Server Json API to not using session_id instead reading in the session_id from cookie.
|
||||||
|
|
||||||
|
## Repo structure
|
||||||
|
Yes, scripts are for deploying on bare metal, so they not used in docker setup.
|
||||||
|
|
||||||
|
## Community Server API
|
||||||
|
### Send transaction
|
||||||
|
"// TODO balance and at least last transaction must be returned with the call itself"
|
||||||
|
Do you mean last transaction before freshly sended transaction or this freshly sended transaction?
|
||||||
|
I can give you the new balance after the transaction was successfully written into blockchain.
|
||||||
|
The send transaction call is actually a asychronous call, it's return very fast but transaction will be processed after it some time. With Hedera up to 2 minutes.
|
||||||
|
|
||||||
|
"// TODO how is this more secure?"
|
||||||
|
For me posting the transaction data again in pure html with disabled js directly from login-server is more secure, because if
|
||||||
|
a corrupted js-client manipulate transaction without user knowing,
|
||||||
|
user has the chance to notice it and cancel the transaction. Or if js-client has an error, but that shouldn't happen with enough tests.
|
||||||
|
|
||||||
|
So the only way to manipulating transaction server-side is hacking the login-server, getting sudo password, change and recompile c++ code and restart login-server with new version. And even for that scenario I have a idea to prevent that.
|
||||||
|
If login-server ask for a password after starting which he will use additional for encrypt user privat keys a hacker which restart login-server without the right passwort cannot doing anything with that. Even if User login-in no transactions can be signed because private keys of users cannot decrypted. For protecting password in-memory I have also some ideas, which I haven't implemented yet, but it exist some possibilities.
|
||||||
|
|
||||||
|
Yes it is also possible to change the pure html page received from login-server with a proxy or with a manipulated browser but I think that is harder to archieve. And you must do it for every user, while hacking serverside can has a greater impact to more user. The cost-benefit ratio is much better.
|
||||||
|
|
||||||
|
"// TODO Is this in line with our usability goals? // TODO Should this not be handled client side?"
|
||||||
|
I don't see where it intercept with usability. Confirm transaction is normal in online-banking-software.
|
||||||
|
And with the same style user don't notice that they on a different server. Like on the production server or stage1 with old interface.
|
||||||
|
This concept I have from the start, but with my idea from yesterday serve client-js from login-server we can reduce the risk of corrupted js-client and js-client can handle it by themself.
|
||||||
|
Maybe we can make it optional so that user can choose between security and simpler use without confirming transaction.
|
||||||
|
|
||||||
|
## Login Server API
|
||||||
|
### Login by Email Verification Code
|
||||||
|
"// TODO why would I want to do this? "
|
||||||
|
Maybe the title is misleading.
|
||||||
|
In Old frontend:
|
||||||
|
If user clicked on link in verification email, he land on http://localhost/account/checkEmail/[code].
|
||||||
|
Than if code was found in db, check_email will be set to 1 and user will be logged in and redirected to his Dashboard.
|
||||||
|
On most pages I have used I have experienced it this way. But it isn't a full login. If user now wants to send gradidos in http://localhost/account/checkTransaction password will be requested.
|
||||||
|
But if a session from the user was still in memory this session will be used and is therefore a full login.
|
||||||
|
So this api call act mainly as replacement for this function for using with new client.
|
||||||
|
|
||||||
|
// TODO ??? (will be done automaticly if called with valid email verification code of type register or registerDirect)
|
||||||
|
Sorry, was a bit unclear (._.)
|
||||||
|
Also check_email will be set to 1 if called with valid email verification code of type register or registerDirect, not with email verification code of type resetPassword.
|
||||||
|
|
||||||
|
// TODO ??? Can be used for password reset (additional step required: call update user info with new password)
|
||||||
|
Yes maybe not clear, I reuse the email verification code function for password reset because it's mostly the same.
|
||||||
|
Login by code but with redirect to page for choosing new password.
|
||||||
|
So if someone likes to reset his passwords and use this in old frontend: http://localhost/account/resetPassword
|
||||||
|
a new email verification code of type resetPassword is created and a email with this code in the link http://localhost/account/checkEmail/[code] is sended to user. If he clicks on it he get logged in and redirect to http://localhost/account/updateUserPassword page to choose a new password. Then his private keys will reincrypted with the new password and he will redirected to Dashboard.
|
||||||
|
|
||||||
|
### Send E-Mails
|
||||||
|
// TODO this makes no sense, why two fields email_text & code_type?
|
||||||
|
I don't know which you prefer so I use both.
|
||||||
|
In C++ I use enums for that so I have a name and a number.
|
||||||
|
I have seen in vue-client code that you have created a enum like object:
|
||||||
|
```js
|
||||||
|
// control email-text sended with email verification code
|
||||||
|
const EMAIL_TYPE = {
|
||||||
|
DEFAULT: 2, // if user has registered directly
|
||||||
|
ADMIN: 5, // if user was registered by an admin
|
||||||
|
}
|
||||||
|
```
|
||||||
|
and wasn't sure if it is okay if I only offer the number variant.
|
||||||
|
So which way you prefer, number or string?
|
||||||
@ -1,31 +1,45 @@
|
|||||||
# community server api
|
# Community Server API
|
||||||
|
|
||||||
In this examples I assume that you use gradido with or docker-compose build on your local maschine
|
This document describes the community server API. The community server is written in PHP and mixes front with backend. Furthermore cakePHP and its auto-generated controller structure is used to access the API. This whole part of the Software will be subject to refactoring.
|
||||||
|
|
||||||
## Konto Overview
|
// TODO In this examples I assume that you use gradido with or docker-compose build on your local maschine
|
||||||
return current account balance
|
|
||||||
|
|
||||||
GET http://localhost/state-balances/ajaxGetBalance/-127182
|
## Error handling
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
## Account overview
|
||||||
|
Returns the current account balance
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`GET http://localhost/state-balances/ajaxGetBalance/-127182`
|
||||||
|
|
||||||
|
### Response
|
||||||
|
Assuming: session is valid
|
||||||
|
|
||||||
If session is valid, return:
|
|
||||||
```json
|
```json
|
||||||
{"state":"success","balance":174500}
|
{
|
||||||
|
"state":"success",
|
||||||
|
"balance":174500
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 174500 = 17,45 GDD
|
|
||||||
|
|
||||||
## List Transactions
|
- `balance`: balance describes gradido cents which are 4 digits behind the separator. A balance value of 174500 equals therefor 17,45 GDD
|
||||||
List all transactions from logged in user, currently without paging
|
|
||||||
Ajax:
|
## List transactions
|
||||||
GET http://localhost/state-balances/ajaxListTransactions/-127182/
|
List all transactions for logged in user
|
||||||
or
|
|
||||||
GET http://localhost/state-balances/ajaxListTransactions/-127182/DESC
|
### Request
|
||||||
to get transaction in descending order
|
`GET http://localhost/state-balances/ajaxListTransactions/-127182/[DESC]`
|
||||||
|
(The `DESC` part is optional symbolized by [])
|
||||||
|
|
||||||
|
### Response
|
||||||
|
Assuming: session is valid
|
||||||
|
|
||||||
Antwort:
|
|
||||||
Wenn alles okay:
|
|
||||||
```json
|
```json
|
||||||
{"state":"success", "transactions":
|
{
|
||||||
[
|
"state":"success",
|
||||||
|
"transactions": [
|
||||||
{
|
{
|
||||||
"name": "Max Mustermann",
|
"name": "Max Mustermann",
|
||||||
"email": "Maxim Mustermann",
|
"email": "Maxim Mustermann",
|
||||||
@ -44,37 +58,42 @@ Wenn alles okay:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- name: name of other involved party or empty if unknown (if other party don't belong to group)
|
- `transactionExecutingCount`: how many transaction for this user currently pending and waiting for signing
|
||||||
|
- `count`: sum of finished transactions user is involved
|
||||||
|
- `gdtSum`: sum of gdt of user in cent with 2 places (Nachkommastellen)
|
||||||
|
- `timeUsed`: time used for getting data from db in seconds, only for analyse backend performance
|
||||||
|
|
||||||
|
Transaction:
|
||||||
|
- `name`: name of other involved party or empty if unknown (if other party don't belong to group)
|
||||||
- if type is send, name is name of receiver
|
- if type is send, name is name of receiver
|
||||||
- if type is receive, name is name of sender
|
- if type is receive, name is name of sender
|
||||||
- if type is creation currently I use a static string ("Gradido Akademie)
|
- if type is creation currently I use a static string ("Gradido Akademie)
|
||||||
- email: optional, only if type is send or receive and other user is known
|
- `email`: optional, only if type is send or receive and other user is known
|
||||||
- pubkey: optional, only if type is send or receive and other user isn't known
|
- `pubkey`: optional, only if type is send or receive and other user isn't known, hexadecimal representation of 32 Byte public key of user [0-9a-f]
|
||||||
- type: type of transaction
|
- `type`: type of transaction
|
||||||
- creation: user has get gradidos created
|
- `creation`: user has get gradidos created
|
||||||
- send: user has send another user gradidos
|
- `send`: user has send another user gradidos
|
||||||
- receiver: user has received gradidos from another user
|
- `receiver`: user has received gradidos from another user
|
||||||
- transaction_id: id of transaction in db, in stage2 also the hedera sequence number of transaction
|
- `transaction_id`: id of transaction in db, in stage2 also the hedera sequence number of transaction
|
||||||
- date: date of ordering transaction (booking date)
|
- `date`: date of ordering transaction (booking date)
|
||||||
- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 1920000 = 192,00 GDD
|
- `balance`: Gradido Cent, 4 Nachkommastellen (2 Reserve), 1920000 = 192,00 GDD
|
||||||
- memo: Details about transaction
|
- `memo`: Details about transaction
|
||||||
- pubkey: optional, if other party isn't known, hexadecimal representation of 32 Byte public key of user [0-9a-f]
|
|
||||||
|
|
||||||
- transactionExecutingCount: how many transaction for this user currently pending and waiting for signing
|
## Creation transaction
|
||||||
- count: sum of finished transactions user is involved
|
Makes a creation transaction to create new Gradido
|
||||||
- gdtSum: sum of gdt of user in cent with 2 places (Nachkommastellen)
|
|
||||||
- timeUsed: time used for getting data from db in seconds, only for analyse backend performance
|
|
||||||
|
|
||||||
## Creation Transaction
|
This assumes you have set
|
||||||
Make a creation transaction
|
|
||||||
With new Option set in Login-Server:
|
|
||||||
```ini
|
```ini
|
||||||
unsecure.allow_auto_sign_transactions = 1
|
unsecure.allow_auto_sign_transactions = 1
|
||||||
```
|
```
|
||||||
transactions can be auto-signed directly with handing in transaction.
|
in the Login-Server, so transactions can be auto-signed directly with handing in the transaction.
|
||||||
Normally a forwarding to login-server check transactions side is neccessary to minimize security risks.
|
Normally a forwarding to login-server check transactions side is necessary to minimize security risks. // TODO this is not documented
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/transaction-creations/ajaxCreate`
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
POST http://localhost/transaction-creations/ajaxCreate
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"session_id" : -127182,
|
"session_id" : -127182,
|
||||||
@ -85,22 +104,36 @@ POST http://localhost/transaction-creations/ajaxCreate
|
|||||||
"auto_sign": true
|
"auto_sign": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
return if everything is ok:
|
|
||||||
```json
|
|
||||||
{"state":"success", "timeUsed": 0.0122}
|
|
||||||
```
|
|
||||||
- timeUsed: time used for getting data from db in seconds, only for analyse backend performance
|
|
||||||
|
|
||||||
## Send Coins Transaction
|
// TODO description of fields
|
||||||
Make a simple GDD Transaction, send Coins from one user to other.
|
|
||||||
With new Option set in Login-Server:
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state":"success",
|
||||||
|
"timeUsed": 0.0122
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `timeUsed`: time used for getting data from db in seconds, only for analyse backend performance
|
||||||
|
|
||||||
|
## Send transaction
|
||||||
|
Make a simple GDD Transaction, send Coins from logged in user to another.
|
||||||
|
|
||||||
|
This assumes you have set
|
||||||
```ini
|
```ini
|
||||||
unsecure.allow_auto_sign_transactions = 1
|
unsecure.allow_auto_sign_transactions = 1
|
||||||
```
|
```
|
||||||
transactions can be auto-signed directly with handing in transaction.
|
in the Login-Server, so transactions can be auto-signed directly with handing in the transaction.
|
||||||
Normally a forwarding to login-server check transactions side is neccessary to minimize security risks.
|
Normally a forwarding to login-server check transactions side is necessary to minimize security risks. // TODO this is not documented
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/transaction-send-coins/ajaxCreate`
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
POST http://localhost/transaction-send-coins/ajaxCreate
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"session_id" : -127182,
|
"session_id" : -127182,
|
||||||
@ -110,22 +143,37 @@ POST http://localhost/transaction-send-coins/ajaxCreate
|
|||||||
"auto_sign": true
|
"auto_sign": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- amout: amount to transfer, 2000000 = 200,00 GDD
|
|
||||||
- email: receiver email address, must be differ from user email
|
|
||||||
- memo: Details about transaction
|
|
||||||
- auto_sign: set to true to directly sign transaction if unsecure.allow_auto_sign_transactions = 1 is set
|
|
||||||
|
|
||||||
return if everything is ok:
|
- `amount`: amount to transfer, 2000000 = 200,00 GDD
|
||||||
|
- `email`: receiver email address, must be differ from user email
|
||||||
|
- `memo`: Details about transaction
|
||||||
|
- `auto_sign`: set to true to directly sign transaction if unsecure.allow_auto_sign_transactions = 1 is set
|
||||||
|
|
||||||
|
|
||||||
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"state":"success", "timeUsed": 0.0122}
|
{
|
||||||
|
"state":"success",
|
||||||
|
"timeUsed": 0.0122
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- timeUsed: time used for getting data from db in seconds, only for analyse backend performance
|
|
||||||
|
|
||||||
Than the transaction was created on community server, send to login-server, signed (if unsecure.allow_auto_sign_transactions = 1 and auto_sign = true)
|
- `timeUsed`: time used for getting data from db in seconds, only for analyse backend performance
|
||||||
and send back to community server and put into db.
|
|
||||||
After you get this answear you see the new transaction if you list transactions or call for the balance.
|
|
||||||
|
|
||||||
Without auto-sign the transaction is pending on login-server and waits for the user to review it at
|
### Process
|
||||||
http://localhost/account/checkTransactions
|
|
||||||
|
Once the transaction was created on community server, send to login-server, signed (if unsecure.allow_auto_sign_transactions = 1 and auto_sign = true) and then send back to community server to be finally put into the database.
|
||||||
|
|
||||||
|
After you get this answer you see the new transaction if you request the transaction list or call for the balance.
|
||||||
|
|
||||||
|
// TODO balance and at least last transaction must be returned with the call itself
|
||||||
|
|
||||||
|
Without auto-sign the transaction is pending on the login-server and waits for the user to review it at `http://localhost/account/checkTransactions`
|
||||||
|
|
||||||
|
// TODO how is this more secure?
|
||||||
|
// TODO Is this in line with our usability goals?
|
||||||
|
// TODO Should this not be handled client side?
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
@ -1,158 +1,191 @@
|
|||||||
<mxfile>
|
<mxfile>
|
||||||
<diagram id="5IrTVgsBGhx6YnP7yutc" name="Page-1">
|
<diagram id="5IrTVgsBGhx6YnP7yutc" name="Page-1">
|
||||||
<mxGraphModel dx="974" dy="537" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
<mxGraphModel dx="900" dy="451" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
<root>
|
<root>
|
||||||
<mxCell id="0"/>
|
<mxCell id="0"/>
|
||||||
<mxCell id="1" parent="0"/>
|
<mxCell id="1" parent="0"/>
|
||||||
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="2" target="3">
|
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="2" target="3" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="2" target="4">
|
<mxCell id="7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="2" target="4" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="2" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="2" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="60" y="70" width="190" height="210" as="geometry"/>
|
<mxGeometry x="60" y="70" width="190" height="210" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="3" target="5">
|
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="3" target="5" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="3" target="4">
|
<mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="3" target="4" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="3" value="Login Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="3" value="Login Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="410" y="70" width="80" height="80" as="geometry"/>
|
<mxGeometry x="410" y="70" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="4" target="5">
|
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="4" target="5" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="4" target="3">
|
<mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="4" target="3" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="4" value="Community Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="4" value="Community Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="410" y="200" width="80" height="80" as="geometry"/>
|
<mxGeometry x="410" y="200" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="5" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="5" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="610" y="140" width="80" height="80" as="geometry"/>
|
<mxGeometry x="610" y="140" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="13" value="CakePHP<br>Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="13" value="CakePHP<br>Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="410" y="280" width="80" height="60" as="geometry"/>
|
<mxGeometry x="410" y="280" width="80" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="14" value="Einzel HTML" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="14" value="Einzel HTML" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="410" y="30" width="80" height="40" as="geometry"/>
|
<mxGeometry x="410" y="30" width="80" height="40" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="17" target="27">
|
<mxCell id="28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="17" target="27" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="17" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="17" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="60" y="460" width="190" height="210" as="geometry"/>
|
<mxGeometry x="60" y="460" width="190" height="210" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="20" target="24">
|
<mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="20" target="24" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="20" target="23">
|
<mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="20" target="23" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="20" value="Login Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="20" value="Login Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="540" y="460" width="80" height="80" as="geometry"/>
|
<mxGeometry x="540" y="460" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="23" target="24">
|
<mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="23" target="24" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="23" target="20">
|
<mxCell id="22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="23" target="20" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="23" value="Community Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="23" value="Community Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="540" y="590" width="80" height="80" as="geometry"/>
|
<mxGeometry x="540" y="590" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="24" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="24" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="740" y="540" width="80" height="80" as="geometry"/>
|
<mxGeometry x="740" y="540" width="80" height="80" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="25" value="CakePHP<br>Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="25" value="CakePHP<br>Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#6a00ff;strokeColor=#3700CC;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="540" y="680" width="80" height="60" as="geometry"/>
|
<mxGeometry x="540" y="680" width="80" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="26" value="Einzel HTML" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="26" value="Einzel HTML" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="540" y="410" width="80" height="40" as="geometry"/>
|
<mxGeometry x="540" y="410" width="80" height="40" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="27" target="20">
|
<mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="27" target="20" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="27" target="23">
|
<mxCell id="30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="27" target="23" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="27" value="GraphQL" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="27" value="GraphQL" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="320" y="460" width="80" height="210" as="geometry"/>
|
<mxGeometry x="320" y="460" width="80" height="210" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="31" target="27">
|
<mxCell id="34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="31" target="27" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="31" value="Test<br>(Backend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="31" value="Test<br>(Backend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="320" y="370" width="80" height="60" as="geometry"/>
|
<mxGeometry x="320" y="370" width="80" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="32" target="17">
|
<mxCell id="33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="32" target="17" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="32" value="Test<br>(Frontend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="32" value="Test<br>(Frontend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="60" y="370" width="190" height="60" as="geometry"/>
|
<mxGeometry x="60" y="370" width="190" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="36" target="48">
|
<mxCell id="35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="36" target="48" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="36" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="36" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="60" y="840" width="190" height="210" as="geometry"/>
|
<mxGeometry x="60" y="1258" width="190" height="210" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="60" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="43" target="54">
|
<mxCell id="60" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="43" target="54" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="64" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="43" target="55">
|
<mxCell id="64" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="43" target="55" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="43" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="43" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="560" y="902.5" width="85" height="85" as="geometry"/>
|
<mxGeometry x="560" y="1320.5" width="85" height="85" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="53" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="48" target="43">
|
<mxCell id="53" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="48" target="43" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="48" value="GraphQL" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="48" value="GraphQL" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="320" y="840" width="200" height="210" as="geometry"/>
|
<mxGeometry x="320" y="1258" width="200" height="210" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="49" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="50" target="48">
|
<mxCell id="49" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="50" target="48" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="50" value="Test<br>(Backend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="50" value="Test<br>(Backend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="320" y="750" width="200" height="60" as="geometry"/>
|
<mxGeometry x="320" y="1168" width="200" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="51" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="52" target="36">
|
<mxCell id="51" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="52" target="36" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="52" value="Test<br>(Frontend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="52" value="Test<br>(Frontend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="60" y="750" width="190" height="60" as="geometry"/>
|
<mxGeometry x="60" y="1168" width="190" height="60" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="61" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="54" target="55">
|
<mxCell id="61" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="54" target="55" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="65" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="54" target="56">
|
<mxCell id="65" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="54" target="56" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="54" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="54" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="645" y="817.5" width="85" height="85" as="geometry"/>
|
<mxGeometry x="645" y="1235.5" width="85" height="85" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="62" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="55" target="56">
|
<mxCell id="62" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="55" target="56" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="55" target="43">
|
<mxCell id="66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="55" target="43" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="55" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="55" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="730" y="902.5" width="85" height="85" as="geometry"/>
|
<mxGeometry x="730" y="1320.5" width="85" height="85" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="63" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="56" target="43">
|
<mxCell id="63" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="56" target="43" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="56" target="54">
|
<mxCell id="67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="56" target="54" edge="1">
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
<mxCell id="56" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
<mxCell id="56" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" parent="1" vertex="1">
|
||||||
<mxGeometry x="645" y="987.5" width="85" height="85" as="geometry"/>
|
<mxGeometry x="645" y="1405.5" width="85" height="85" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="68" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="69" target="81">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="69" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="59" y="865" width="190" height="211" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="72" target="76">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="72" value="Login Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="498.55" y="865" width="207.91" height="210.5" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="76" value="Node Server" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#1ba1e2;strokeColor=#006EAF;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="740" y="929.75" width="80" height="81" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="79" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="81" target="72">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="81" value="GraphQL" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="319" y="865" width="121" height="211" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="83" target="81">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="83" value="Test<br>(Backend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="319" y="775" width="121" height="61" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="84" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="85" target="69">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="85" value="Test<br>(Frontend)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#ffffff;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="59" y="775" width="190" height="61" as="geometry"/>
|
||||||
</mxCell>
|
</mxCell>
|
||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
BIN
docu/graphics/neue-struktur.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 217 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@ -1,236 +1,321 @@
|
|||||||
# login server api
|
# Login Server API
|
||||||
|
|
||||||
Gradido js-Frontend vereinfachte Zusammenarbeit mit Login-Server und Community-Server
|
This document describes the login server API. The login server is written in C++ and mixes front with backend.
|
||||||
|
The primary task of the login server is to store and handle the private keys of the users securely.
|
||||||
|
|
||||||
Es ist mit einer Zusatz-Option möglich das Login und Registrieren via ajax-Request passiern kann.
|
## Configuration
|
||||||
Dafür muss in der Login-Server Config
|
You can configure the login server to handle login and register requests via http call, by setting the following config option:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
unsecure.allow_passwort_via_json_request = 1
|
unsecure.allow_passwort_via_json_request = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
gesetzt sein.
|
To disable CORs restrictions you can set:
|
||||||
|
|
||||||
mit:
|
|
||||||
```ini
|
```ini
|
||||||
unsercure.allow_cors_all = 1
|
unsercure.allow_cors_all = 1
|
||||||
```
|
```
|
||||||
Wird bei allen JSON-Requests zum Header hinzugefügt:
|
|
||||||
- Access-Control-Allow-Origin:*
|
|
||||||
- Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
|
|
||||||
|
|
||||||
|
This will result in all JSON-Request headers to contain:
|
||||||
|
|
||||||
In diesen Beispielen gehe ich jetzt davon aus, das du das gesamte Gradido Projekt mit Docker gebaut hast und auf dem lokalen Rechner laufen lässt.
|
```
|
||||||
|
Access-Control-Allow-Origin: *
|
||||||
Login
|
Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
|
||||||
Einloggen über:
|
|
||||||
|
|
||||||
|
|
||||||
## unsecureLogin
|
|
||||||
|
|
||||||
POST http://localhost/login_api/unsecureLogin
|
|
||||||
```json
|
|
||||||
data: {"email": "max.musterman@gmail.de", "password": "123abcDE&"}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Wenn alles okay ist erhältst du zum Beispiel:
|
## Error handling
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
## Login
|
||||||
|
To login you can use the `unsecureLogin` request
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/login_api/unsecureLogin`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"state":"success", "session_id": -127182, "clientIP":"52.68.96.58"}
|
{
|
||||||
|
"email": "max.musterman@gmail.de",
|
||||||
|
"password": "123abcDE&"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- clientIP: should be the same as where the js-client is running, else maybe a man-in-the-middle attacks is happening or
|
|
||||||
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state":"success",
|
||||||
|
"session_id": -127182,
|
||||||
|
"clientIP":"123.123.123.123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `clientIP`: should be the same as where the js-client is running, else maybe a man-in-the-middle attacks is happening or
|
||||||
nginx was wrong configured.
|
nginx was wrong configured.
|
||||||
- session_id: can be also negative
|
- `session_id`: can be also negative
|
||||||
|
|
||||||
Anmelden
|
## Create user
|
||||||
Registrieren kannst du einen neuen Benutzer mit:
|
Register a new User
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/login_api/createUser`
|
||||||
|
|
||||||
## createUser
|
with:
|
||||||
|
|
||||||
POST http://localhost/login_api/createUser
|
|
||||||
```json
|
```json
|
||||||
data: {
|
{
|
||||||
"email":"max.musterman@gmail.de",
|
"email":"max.musterman@gmail.de",
|
||||||
"first_name":"Max",
|
"first_name":"Max",
|
||||||
"last_name":"Musterman" ,
|
"last_name":"Musterman" ,
|
||||||
"emailType": 2,
|
"emailType": 2,
|
||||||
"password":"123abcDE&"
|
"password":"123abcDE&",
|
||||||
"login_after_register":true
|
"login_after_register":true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- emailType: control email-text sended with email verification code
|
- `emailType`: control email-text sended with email verification code
|
||||||
- 2: default, if user has registered directly
|
- 2: default, if user has registered directly
|
||||||
- 5: if user was registered by an admin
|
- 5: if user was registered by an admin
|
||||||
- login_after_register: if set to true auto-login after create user was successfull, means session cookie is set and session_id returned
|
- `login_after_register`: if set to true auto-login after create user was successfull, means session cookie is set and session_id returned
|
||||||
|
|
||||||
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
Wenn alles okay ist erhältst du:
|
|
||||||
```json
|
```json
|
||||||
{"state":"success"}
|
{
|
||||||
|
"state":"success"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Dann wurde für den Benutzer ein neues Konto angelegt, seine Schlüssel generiert und ihm eine Bestätigungs-Email geschickt.
|
Now a new user is registered, its keys were generated and it can be logged in. A confirmation EMail has been sent to the user as well.
|
||||||
|
|
||||||
|
// TODO return type if `login_after_register=true` is used
|
||||||
|
|
||||||
## Logout
|
## Logout
|
||||||
|
To logout an logged in session:
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/login_api/logout`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
Abmelden
|
|
||||||
POST http://localhost/login_api/logout
|
|
||||||
```json
|
```json
|
||||||
data: {"session_id": -127182}
|
{
|
||||||
|
"session_id": -127182
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Wenn alles okay ist erhältst du:
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"state":"success"}
|
{
|
||||||
|
"state":"success"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Update User Data
|
## Update user data
|
||||||
Update first name, last name, user language and enable/disable user
|
This request allows you to update first name, last name, user language and enable/disable user.
|
||||||
Language currently supported de and en
|
|
||||||
User will be disabled if he wants a account delete but has transactions.
|
Additionally the user's password can be changed if the following option is set:
|
||||||
Until transactions are saved in real blockchain, we need this data because the public key
|
|
||||||
is in db only saved in state_users so we wenn delete this entry, validating all transactions not longer possible.
|
|
||||||
Disabled User cannot login and cannot receive transactions.
|
|
||||||
In update Object only one of the sets needs to be there.
|
|
||||||
|
|
||||||
Update password can only be used if in Login-Server config:
|
|
||||||
```ini
|
```ini
|
||||||
unsecure.allow_passwort_via_json_request = 1
|
unsecure.allow_passwort_via_json_request = 1
|
||||||
```
|
```
|
||||||
is set
|
|
||||||
|
|
||||||
POST http://localhost/login_api/updateUserInfos
|
### Request
|
||||||
|
`POST http://localhost/login_api/updateUserInfos`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
|
{
|
||||||
"User.first_name": "Max",
|
"session_id": -127182,
|
||||||
"User.last_name" : "Musterman",
|
"email": "max.musterman@gmail.de",
|
||||||
"User.disabled": 0,
|
"update": {
|
||||||
"User.language": "de"
|
"User.first_name": "Max",
|
||||||
"User.password": "1234"
|
"User.last_name" : "Musterman",
|
||||||
}
|
"User.disabled": 0,
|
||||||
}
|
"User.language": "de"
|
||||||
```
|
"User.password": "1234"
|
||||||
also valid
|
}
|
||||||
```json
|
|
||||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
|
|
||||||
"User.last_name" : "Musterman"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It returns if everything is okay
|
Notes:
|
||||||
|
- `user.language`: currently supports `de` and `en` only
|
||||||
|
- User will be disabled if he wants his account deleted, but has transactions. Until transactions are saved in real blockchain, we need this data because the public key is in db only saved in state_users so if we delete this entry, validating all transactions is no longer possible.
|
||||||
|
- Disabled Users can neither login nor receive transactions.
|
||||||
|
- It is not required to provide all fields of `update`, it can be a subset depending on what you intend to change.
|
||||||
|
|
||||||
|
### Response
|
||||||
|
In case of success:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"state":"success", "valid_values": 4, "errors":[]}
|
{
|
||||||
|
"state":"success",
|
||||||
|
"valid_values": 4,
|
||||||
|
"errors": [...]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- valid_values: should contain count of entrys in update if no error occured (User.password will not be counted)
|
|
||||||
- errors: contain on error string for every entry in update, which type isn't like expected
|
- `valid_values`: should contain count of entries in update if no error occurred (User.password will not be counted)
|
||||||
- password:
|
- `errors`: contain on error string for every entry in update, which type isn't like expected
|
||||||
|
- `password`:
|
||||||
- "new password is the same as old password": no change taking place
|
- "new password is the same as old password": no change taking place
|
||||||
- "password changed, coludn"t load private key for re-encryption": password was successfully changed, is at the moment only a warning as long as user_backups are unencrypted, safe to ignore
|
- "password changed, coludn"t load private key for re-encryption": password was successfully changed, is at the moment only a warning as long as user_backups are unencrypted, safe to ignore
|
||||||
- "stored pubkey and private key didn't match": error by re-encryption keys, no changes saved
|
- "stored pubkey and private key didn't match": error by re-encryption keys, no changes saved
|
||||||
- "User.password isn't valid": if password validation failed, followed by reasons why (additional array in array)
|
- "User.password isn't valid": if password validation failed, followed by reasons why (additional array in array)
|
||||||
example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{"errors":[
|
{
|
||||||
"User.password isn't valid",[
|
"errors": [
|
||||||
"Passwort: Dein Passwort ist zu kurz!\n",
|
"User.password isn't valid",
|
||||||
"Passwort: Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!\n"
|
[
|
||||||
]
|
"Passwort: Dein Passwort ist zu kurz!\n",
|
||||||
],
|
"Passwort: Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!\n"
|
||||||
"state":"success",
|
]
|
||||||
"valid_values":0
|
],
|
||||||
}
|
"state":"success",
|
||||||
```
|
"valid_values":0
|
||||||
## Retrieve User Data
|
}
|
||||||
Retrieve different user data, in ask only one field is needed, or every possible combination
|
```
|
||||||
from the available fields
|
|
||||||
|
## Retrieve user data
|
||||||
|
Retrieve different user data.
|
||||||
|
|
||||||
|
You can query a subset of data or all of it at once.
|
||||||
|
|
||||||
|
Normal Users can only retrieve data for themselves, admins (login-server admin) can retrieve data for every user.
|
||||||
|
|
||||||
Normal User can only retrieve data for himself, admins (login-server admin) can retrieve data from every user
|
|
||||||
Email is also the email address of user from which data are asked
|
Email is also the email address of user from which data are asked
|
||||||
|
|
||||||
POST http://localhost/login_api/getUserInfos
|
### Request
|
||||||
|
`POST http://localhost/login_api/getUserInfos`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "ask": [
|
{
|
||||||
"EmailVerificationCode.Register",
|
"session_id": -127182,
|
||||||
"loginServer.path",
|
"email": "max.musterman@gmail.de",
|
||||||
"user.pubkeyhex",
|
"ask": [
|
||||||
"user.first_name",
|
"EmailVerificationCode.Register",
|
||||||
"user.last_name",
|
"loginServer.path",
|
||||||
"user.disabled",
|
"user.pubkeyhex",
|
||||||
"user.email_checked",
|
"user.first_name",
|
||||||
]
|
"user.last_name",
|
||||||
}
|
"user.disabled",
|
||||||
```
|
"user.email_checked",
|
||||||
returns if no error occured:
|
]
|
||||||
```json
|
|
||||||
{"state": "success", "userData": {
|
|
||||||
"EmailVerificationCode.Register": "2718271129122",
|
|
||||||
"pubkeyhex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
|
|
||||||
"first_name": "Max",
|
|
||||||
"last_name": "Musterman",
|
|
||||||
"disabled": 0,
|
|
||||||
"email_checked": 1
|
|
||||||
}, "server": {
|
|
||||||
"loginServer.path": "http://localhost/account"
|
|
||||||
},
|
|
||||||
"errors": []
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Return only the fields which are defined in ask
|
### Response
|
||||||
- EmailVerificationCode.Register: return the email verification code for check email (create one if none exist), work only if logged in user is admin and the email isn't from him
|
In case of success:
|
||||||
- loginServer.path: the redirect path to login-server, for example for login with login-server html frontend
|
|
||||||
- user.pubkeyhex: public key of user in hex-format
|
|
||||||
- user.first_name: first name of user
|
|
||||||
- user.last_name: last name of user
|
|
||||||
- user.disabled: User will be disabled if he wants a account delete but has transactions.
|
|
||||||
Until transactions are saved in real blockchain, we need this data because the public key
|
|
||||||
is in db only saved in state_users so we wenn delete this entry, validating all transactions not longer possible.
|
|
||||||
Disabled User cannot login and cannot receive transactions.
|
|
||||||
- email_checked: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1
|
|
||||||
|
|
||||||
- errors: array of strings if error occure
|
```json
|
||||||
|
{
|
||||||
|
"state": "success",
|
||||||
|
"userData": {
|
||||||
|
"EmailVerificationCode.Register": "2718271129122",
|
||||||
|
"pubkeyhex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
|
||||||
|
"first_name": "Max",
|
||||||
|
"last_name": "Musterman",
|
||||||
|
"disabled": 0,
|
||||||
|
"email_checked": 1
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"loginServer.path": "http://localhost/account"
|
||||||
|
},
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Return only the fields which are defined in request
|
||||||
|
- `EmailVerificationCode.Register`: return the email verification code for check email (create one if none exist), work only if logged in user is admin and the email isn't from him
|
||||||
|
- `loginServer.path`: the redirect path to login-server, for example for login with login-server html frontend
|
||||||
|
- `user.pubkeyhex`: public key of user in hex-format
|
||||||
|
- `user.first_name`: first name of user
|
||||||
|
- `user.last_name`: last name of user
|
||||||
|
- `user.disabled`: User will be disabled if he wants a account delete but has transactions. Until transactions are saved in real blockchain, we need this data because the public key
|
||||||
|
is in db only saved in state_users so if we delete this entry, validating all transactions is no longer possible. Disabled User cannot login and cannot receive transactions.
|
||||||
|
- `email_checked`: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1
|
||||||
|
- `errors`: array of strings if error occure
|
||||||
|
|
||||||
## Login by Email Verification Code
|
## Login by Email Verification Code
|
||||||
Used for replace http://localhost/account/checkEmail
|
Login while confirming your EMail. This API call is used to replace http://localhost/account/checkEmail (HTML).
|
||||||
Can be used to set check_email to 1 (will be done automaticly if called with valid email verification code of type register or registerDirect)
|
|
||||||
Can be used for password reset (additional step required: call update user info with new password)
|
|
||||||
|
|
||||||
GET http://localhost/login_api/loginViaEmailVerificationCode?emailVerificationCode=382738273892983
|
If successful `check_email` will be set to 1 in the database.
|
||||||
|
|
||||||
|
// TODO why would I want to do this?
|
||||||
|
// TODO ??? (will be done automaticly if called with valid email verification code of type register or registerDirect)
|
||||||
|
// TODO ??? Can be used for password reset (additional step required: call update user info with new password)
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`GET http://localhost/login_api/loginViaEmailVerificationCode?emailVerificationCode=382738273892983`
|
||||||
|
|
||||||
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
return
|
|
||||||
```json
|
```json
|
||||||
{"state":"success", "email_verification_code_type":"resetPassword","info":[],"session_id":1853761475}
|
{
|
||||||
|
"state":"success", "email_verification_code_type":"resetPassword",
|
||||||
|
"info":[],
|
||||||
|
"session_id":1853761475
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- email_verification_code_type
|
|
||||||
- resetPassword: for password resets, will be deleted immediately, is a only one use code
|
- `email_verification_code_type`
|
||||||
- registerDirect: code generated by register for check email
|
- `resetPassword`: for password resets, will be deleted immediately, is a only one use code
|
||||||
- register: code generated by auto-register via elopage for check email
|
- `registerDirect`: code generated by register for check email
|
||||||
- info can contain additional info strings
|
- `register`: code generated by auto-register via elopage for check email
|
||||||
|
- `info`: can contain additional info strings
|
||||||
- "user hasn't password": if user hasn't set a password yet (for example if he was registered via elopage)
|
- "user hasn't password": if user hasn't set a password yet (for example if he was registered via elopage)
|
||||||
- "email already activated": if email was already checked
|
- "email already activated": if email was already checked
|
||||||
- session_id: session_id for new session
|
- `session_id`: session_id for new session
|
||||||
|
|
||||||
## Send Emails
|
## Send E-Mails
|
||||||
Let send Login-Server Password reset E-Mail to User
|
Lets you send Login-Server Password reset E-Mail to an user.
|
||||||
Can be also used for admin interface to resend email verification code
|
Can be also used for the admin interface to resend email verification codes
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`POST http://localhost/login_api/sendEmail`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
POST http://localhost/login_api/sendEmail
|
|
||||||
```json
|
```json
|
||||||
{"email": "max.musterman@gmail.de", "email_text":7, "email_verification_code_type":"resetPassword" }
|
{
|
||||||
|
"email": "max.musterman@gmail.de",
|
||||||
|
"email_text":7, "email_verification_code_type":"resetPassword"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
also valid:
|
also valid:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"email": "max.musterman@gmail.de", "email_text":"user reset Password", "email_verification_code_type":"resetPassword" }
|
{
|
||||||
|
"email": "max.musterman@gmail.de",
|
||||||
|
"email_text":"user reset Password", "email_verification_code_type":"resetPassword" }
|
||||||
```
|
```
|
||||||
|
// TODO this makes no sense, why two fields email_text & code_type?
|
||||||
|
|
||||||
or:
|
or:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "email_text":5, "email_verification_code_type":"register" }
|
{
|
||||||
|
"session_id": -127182,
|
||||||
|
"email": "max.musterman@gmail.de",
|
||||||
|
"email_text":5,
|
||||||
|
"email_verification_code_type":"register"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- session_id: not needed for resetPassword emails
|
- `session_id`: not needed for resetPassword emails
|
||||||
- email_type: choose which email text should be used (number or string can be used)
|
- `email_type`: choose which email text should be used (number or string can be used)
|
||||||
- "email user verification code" (2): default text used when new user has registered
|
- "email user verification code" (2): default text used when new user has registered
|
||||||
- "email user verification code resend" (3): text used when user hasn't activated his account 7 days after register
|
- "email user verification code resend" (3): text used when user hasn't activated his account 7 days after register
|
||||||
- "email user verification code resend after long time" (4): text used when user hasn't activated his account more than 7 days after register
|
- "email user verification code resend after long time" (4): text used when user hasn't activated his account more than 7 days after register
|
||||||
@ -238,31 +323,40 @@ or:
|
|||||||
- "email admin user verification code resend" (6): used if admin trigger sending the email with email verification code again
|
- "email admin user verification code resend" (6): used if admin trigger sending the email with email verification code again
|
||||||
- "user reset Password" (7): used for reset password email text
|
- "user reset Password" (7): used for reset password email text
|
||||||
- "email custom tex" (8): used if custom email text should be used (than email_custom_text and email_custom_subject must also be filled)
|
- "email custom tex" (8): used if custom email text should be used (than email_custom_text and email_custom_subject must also be filled)
|
||||||
- email_verification_code_type
|
- `email_verification_code_type`
|
||||||
- resetPassword: for password resets, will be deleted immediately, is a only one use code, can be used without session_id
|
- resetPassword: for password resets, will be deleted immediately, is a only one use code, can be used without session_id
|
||||||
- registerDirect: code generated by register for check email, can only be used by admins for another user
|
- registerDirect: code generated by register for check email, can only be used by admins for another user
|
||||||
- register: code generated by auto-register via elopage for check email, can only be used by admins for another user
|
- register: code generated by auto-register via elopage for check email, can only be used by admins for another user
|
||||||
- email_custom_text (optional): can be used to send email with custom text
|
- `email_custom_text`: (optional) can be used to send email with custom text
|
||||||
placeholder for email text, will be replaced
|
placeholder for email text, will be replaced
|
||||||
- [first_name] first name
|
- [first_name] first name
|
||||||
- [last_name] last name
|
- [last_name] last name
|
||||||
- [duration] time span since user has created account (ex.: 10 Days) with largest unit, day is last unit
|
- [duration] time span since user has created account (ex.: 10 Days) with largest unit, day is last unit
|
||||||
- [link] login-server checkEmail link with email verification code (ex.: http://localhost/account/checkEmail/382738273892983)
|
- [link] login-server checkEmail link with email verification code (ex.: http://localhost/account/checkEmail/382738273892983)
|
||||||
- [code] email verification code if you like to use your one link
|
- [code] email verification code if you like to use your one link
|
||||||
- email_custom_subject (optional): for custom email the subject
|
- `email_custom_subject`: (optional) for custom email the subject
|
||||||
|
|
||||||
return
|
### Response
|
||||||
```json
|
In case of success returns:
|
||||||
{"state":"success"}
|
|
||||||
```
|
|
||||||
if everything is okay
|
|
||||||
return
|
|
||||||
```json
|
|
||||||
{"state":"warning", "msg":"email already sended"}
|
|
||||||
```
|
|
||||||
if emails was successfully sended but was already sended in past also.
|
|
||||||
|
|
||||||
return with "state":"error" and additional "msg" if error occured (no email sended):
|
```json
|
||||||
|
{
|
||||||
|
"state":"success"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state":"warning",
|
||||||
|
"msg":"email already sended"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
(if emails was successfully sent but was already sent before)
|
||||||
|
|
||||||
|
Furthermore following error cases can take place:
|
||||||
|
A result with `"state":"error"` and an additional `"msg"` if an error occurred (no email sended):
|
||||||
- "email already send less than a hour before": User has already get a password reset email and haven't used the link yet
|
- "email already send less than a hour before": User has already get a password reset email and haven't used the link yet
|
||||||
- "not supported email type": with "email_verification_code_type":"resetPassword" only email type 7 or 8 allowed
|
- "not supported email type": with "email_verification_code_type":"resetPassword" only email type 7 or 8 allowed
|
||||||
- "admin needed": only admins can send email verification emails because this emails normally sended out automaticly
|
- "admin needed": only admins can send email verification emails because this emails normally sended out automaticly
|
||||||
@ -272,18 +366,28 @@ return with "state":"error" and additional "msg" if error occured (no email send
|
|||||||
- "invalid verification code type": could not parse email verification code type
|
- "invalid verification code type": could not parse email verification code type
|
||||||
- "json exception": error parsing input json, more infos can be found in details
|
- "json exception": error parsing input json, more infos can be found in details
|
||||||
|
|
||||||
|
|
||||||
## Check Running Transactions / password encryption
|
## Check Running Transactions / password encryption
|
||||||
Check if transactions on login-server for user are processed
|
Check if transactions on login-server for user are processed
|
||||||
|
|
||||||
GET http://localhost/login_api/getRunningUserTasks?email=max.musterman%40gmail.de
|
|
||||||
**OR**
|
### Request
|
||||||
POST http://localhost/login_api/getRunningUserTasks
|
`GET http://localhost/login_api/getRunningUserTasks?email=max.musterman%40gmail.de`
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
`POST http://localhost/login_api/getRunningUserTasks`
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"email":"max.musterman@gmail.de"}
|
{
|
||||||
|
"email":"max.musterman@gmail.de"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
return
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"state":"success",
|
"state":"success",
|
||||||
@ -295,21 +399,33 @@ return
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
return only entrys which > 0
|
|
||||||
|
return only entries which > 0
|
||||||
- password creation: after register or password change, login possible after tasks is finish
|
- password creation: after register or password change, login possible after tasks is finish
|
||||||
- sign transaction: after check transaction in backend, before transaction is in db
|
- sign transaction: after check transaction in backend, before transaction is in db
|
||||||
- prepare transaction: after sending transaction to login-server, before they can be checked
|
- prepare transaction: after sending transaction to login-server, before they can be checked
|
||||||
- ready for sign transaction: transactions ready for signing from user
|
- ready for sign transaction: transactions ready for signing from user
|
||||||
|
|
||||||
## Check Session State
|
## Check Session State
|
||||||
GET http://localhost/login_api/checkSessionState?session_id=-127182
|
Check if a given session is still valid
|
||||||
|
|
||||||
|
### Request
|
||||||
|
`GET http://localhost/login_api/checkSessionState?session_id=-127182`
|
||||||
|
|
||||||
|
### Response
|
||||||
|
In case of success returns:
|
||||||
|
|
||||||
return if session is still open
|
|
||||||
```json
|
```json
|
||||||
{"state":"success"}
|
{
|
||||||
```
|
"state":"success"
|
||||||
else return
|
}
|
||||||
```json
|
|
||||||
{"state":"not found", "msg": "session not found"}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state":"not found",
|
||||||
|
"msg": "session not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Before Width: | Height: | Size: 78 KiB |
@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
verbose: true,
|
verbose: true,
|
||||||
collectCoverageFrom: ['**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
|
//collectCoverageFrom: ['**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
|
||||||
moduleFileExtensions: [
|
moduleFileExtensions: [
|
||||||
'js',
|
'js',
|
||||||
//'jsx',
|
//'jsx',
|
||||||
@ -13,12 +13,12 @@ module.exports = {
|
|||||||
'\\.(css|less)$': 'identity-obj-proxy',
|
'\\.(css|less)$': 'identity-obj-proxy',
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.vue$': '<rootDir>/node_modules/vue-jest',
|
'^.+\\.vue$': 'vue-jest',
|
||||||
'^.+\\.(js|jsx)?$': '<rootDir>/node_modules/babel-jest',
|
'^.+\\.(js|jsx)?$': 'babel-jest',
|
||||||
|
'<rootDir>/node_modules/vee-validate/dist/rules': 'babel-jest',
|
||||||
},
|
},
|
||||||
setupFiles: ['<rootDir>/test/testSetup.js'],
|
setupFiles: ['<rootDir>/test/testSetup.js'],
|
||||||
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
|
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
|
||||||
// snapshotSerializers: ['jest-serializer-vue'],
|
// snapshotSerializers: ['jest-serializer-vue'],
|
||||||
transformIgnorePatterns: ['<rootDir>/node_modules/'],
|
transformIgnorePatterns: ['<rootDir>/node_modules/(?!vee-validate/dist/rules)'],
|
||||||
preset: '@vue/cli-plugin-unit-jest',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@
|
|||||||
"eslint-plugin-vue": "^7.8.0",
|
"eslint-plugin-vue": "^7.8.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"flatpickr": "^4.5.7",
|
"flatpickr": "^4.5.7",
|
||||||
|
"flush-promises": "^1.0.2",
|
||||||
"fuse.js": "^3.2.0",
|
"fuse.js": "^3.2.0",
|
||||||
"google-maps": "^3.2.1",
|
"google-maps": "^3.2.1",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"quill": "^1.3.6",
|
"quill": "^1.3.6",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
"sweetalert2": "^9.5.4",
|
"sweetalert2": "^9.5.4",
|
||||||
"vee-validate": "^3.4.5",
|
"vee-validate": "^3.4.5",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
|
|||||||
108
frontend/src/views/Pages/Login.spec.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { mount, RouterLinkStub } from '@vue/test-utils'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import flushPromises from 'flush-promises'
|
||||||
|
|
||||||
|
import Login from './Login'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('Login', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
let mocks = {
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = {
|
||||||
|
loginfail: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
let store = new Vuex.Store({
|
||||||
|
state,
|
||||||
|
})
|
||||||
|
|
||||||
|
let stubs = {
|
||||||
|
RouterLink: RouterLinkStub,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(Login, { localVue, mocks, store, stubs })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the Login form', () => {
|
||||||
|
expect(wrapper.find('div.login-form').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Login header', () => {
|
||||||
|
it('has a welcome message', () => {
|
||||||
|
expect(wrapper.find('div.header').text()).toBe('Gradido site.login.community')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('links', () => {
|
||||||
|
it('has a link "Forgot Password?"', () => {
|
||||||
|
expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual(
|
||||||
|
'site.login.forgot_pwd',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('links to /password when clicking "Forgot Password?"', () => {
|
||||||
|
expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/password')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a link "Create new account"', () => {
|
||||||
|
expect(wrapper.findAllComponents(RouterLinkStub).at(1).text()).toEqual(
|
||||||
|
'site.login.new_wallet',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('links to /register when clicking "Create new account"', () => {
|
||||||
|
expect(wrapper.findAllComponents(RouterLinkStub).at(1).props().to).toBe('/register')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Login form', () => {
|
||||||
|
it('has a login form', () => {
|
||||||
|
expect(wrapper.find('form').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has an Email input field', () => {
|
||||||
|
expect(wrapper.find('input[placeholder="Email"]').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has an Password input field', () => {
|
||||||
|
expect(wrapper.find('input[placeholder="form.password"]').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a Submit button', () => {
|
||||||
|
expect(wrapper.find('button[type="submit"]').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows a warning when no valid Email is entered', async () => {
|
||||||
|
wrapper.find('input[placeholder="Email"]').setValue('no_valid@Email')
|
||||||
|
await flushPromises()
|
||||||
|
await expect(wrapper.find('.invalid-feedback').text()).toEqual(
|
||||||
|
'The Email field must be a valid email',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows a warning when password is too short', async () => {
|
||||||
|
wrapper.find('input[placeholder="form.password"]').setValue('1234')
|
||||||
|
await flushPromises()
|
||||||
|
await expect(wrapper.find('.invalid-feedback').text()).toEqual(
|
||||||
|
'The Password field must be at least 6 characters',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// to do: test submit button
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="login-form">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header p-4">
|
<div class="header p-4">
|
||||||
<b-container>
|
<b-container>
|
||||||
|
|||||||
@ -1,8 +1,30 @@
|
|||||||
import { createLocalVue } from '@vue/test-utils'
|
import { createLocalVue } from '@vue/test-utils'
|
||||||
import ElementUI from 'element-ui'
|
import ElementUI from 'element-ui'
|
||||||
import BootstrapVue from 'bootstrap-vue'
|
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import { ValidationProvider, ValidationObserver } from 'vee-validate'
|
||||||
|
import * as rules from 'vee-validate/dist/rules'
|
||||||
|
import { extend } from 'vee-validate'
|
||||||
|
import { messages } from 'vee-validate/dist/locale/en.json'
|
||||||
|
import BaseInput from '@/components/Inputs/BaseInput.vue'
|
||||||
|
import BaseButton from '@/components/BaseButton.vue'
|
||||||
|
import RegeneratorRuntime from 'regenerator-runtime'
|
||||||
|
|
||||||
global.localVue = createLocalVue()
|
global.localVue = createLocalVue()
|
||||||
|
|
||||||
|
Object.keys(rules).forEach((rule) => {
|
||||||
|
extend(rule, {
|
||||||
|
...rules[rule], // copies rule configuration
|
||||||
|
message: messages[rule], // assign message
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
global.localVue.use(ElementUI)
|
global.localVue.use(ElementUI)
|
||||||
global.localVue.use(BootstrapVue)
|
global.localVue.use(BootstrapVue)
|
||||||
|
global.localVue.use(Vuex)
|
||||||
|
global.localVue.use(IconsPlugin)
|
||||||
|
global.localVue.use(RegeneratorRuntime)
|
||||||
|
global.localVue.component(BaseInput.name, BaseInput)
|
||||||
|
global.localVue.component('validation-provider', ValidationProvider)
|
||||||
|
global.localVue.component('validation-observer', ValidationObserver)
|
||||||
|
global.localVue.component(BaseButton.name, BaseButton)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ function resolveSrc(_path) {
|
|||||||
|
|
||||||
let vue_path = process.env.VUE_PATH
|
let vue_path = process.env.VUE_PATH
|
||||||
if (vue_path == undefined) {
|
if (vue_path == undefined) {
|
||||||
vue_path = ''
|
vue_path = '/vue'
|
||||||
}
|
}
|
||||||
|
|
||||||
// vue.config.js
|
// vue.config.js
|
||||||
|
|||||||
@ -6404,6 +6404,11 @@ flatted@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
|
||||||
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
|
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
|
||||||
|
|
||||||
|
flush-promises@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/flush-promises/-/flush-promises-1.0.2.tgz#4948fd58f15281fed79cbafc86293d5bb09b2ced"
|
||||||
|
integrity sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA==
|
||||||
|
|
||||||
flush-write-stream@^1.0.0:
|
flush-write-stream@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
|
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
|
||||||
@ -11206,7 +11211,7 @@ regenerator-runtime@^0.11.0:
|
|||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4:
|
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
|
||||||
version "0.13.7"
|
version "0.13.7"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
||||||
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
|
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
|
||||||
|
|||||||
@ -52,4 +52,4 @@ EXPOSE 1200
|
|||||||
EXPOSE 1201
|
EXPOSE 1201
|
||||||
|
|
||||||
#CMD gdb -ex=r Gradido_LoginServer
|
#CMD gdb -ex=r Gradido_LoginServer
|
||||||
CMD Gradido_LoginServer
|
CMD ./Dockerfiles/build_and_run.sh; ./build_vol/bin/Gradido_LoginServer
|
||||||
|
|||||||
@ -4,6 +4,6 @@ cd build_vol
|
|||||||
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
make -j$(nproc) Gradido_LoginServer
|
make -j$(nproc) Gradido_LoginServer
|
||||||
echo "building done"
|
#echo "building done"
|
||||||
chmod +x ./bin/Gradido_LoginServer
|
chmod +x ./bin/Gradido_LoginServer
|
||||||
./bin/Gradido_LoginServer
|
#./bin/Gradido_LoginServer
|
||||||
|
|||||||
@ -277,6 +277,7 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
|
|||||||
CronManager::getInstance()->init(php_server_ping);
|
CronManager::getInstance()->init(php_server_ping);
|
||||||
|
|
||||||
printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data());
|
printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data());
|
||||||
|
std::clog << "[Gradido_LoginServer::main] started in " << usedTime.string().data() << std::endl;
|
||||||
// wait for CTRL-C or kill
|
// wait for CTRL-C or kill
|
||||||
waitForTerminationRequest();
|
waitForTerminationRequest();
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,8 @@ bool EmailManager::init(const Poco::Util::LayeredConfiguration& cfg)
|
|||||||
|
|
||||||
void EmailManager::addEmail(model::Email* email) {
|
void EmailManager::addEmail(model::Email* email) {
|
||||||
if (mDisableEmail) {
|
if (mDisableEmail) {
|
||||||
std::string log_message = "Email should be sended to: ";
|
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
|
||||||
|
std::string log_message = dateTimeString + " Email should be sended to: ";
|
||||||
auto email_user = email->getUser();
|
auto email_user = email->getUser();
|
||||||
Poco::AutoPtr<model::table::User> email_model;
|
Poco::AutoPtr<model::table::User> email_model;
|
||||||
if (email_user) {
|
if (email_user) {
|
||||||
@ -133,14 +134,33 @@ int EmailManager::ThreadFunction()
|
|||||||
if (catalogs[lang_code].isNull()) {
|
if (catalogs[lang_code].isNull()) {
|
||||||
catalogs[lang_code] = lm->getFreeCatalog(lang_code);
|
catalogs[lang_code] = lm->getFreeCatalog(lang_code);
|
||||||
}
|
}
|
||||||
|
bool email_sended = false;
|
||||||
if (email->draft(&mailMessage, catalogs[lang_code])) {
|
if (email->draft(&mailMessage, catalogs[lang_code])) {
|
||||||
|
|
||||||
|
try {
|
||||||
mailClientSession.sendMessage(mailMessage);
|
mailClientSession.sendMessage(mailMessage);
|
||||||
|
email_sended = true;
|
||||||
|
}
|
||||||
|
catch (Poco::Exception& ex) {
|
||||||
|
email_sended = false;
|
||||||
|
errors.addError(new ParamError(function_name, "poco exception sending email", ex.displayText()));
|
||||||
|
auto user = email->getUser();
|
||||||
|
if (user && !user->getModel().isNull()) {
|
||||||
|
errors.addError(new ParamError(function_name, "email", user->getModel()->getEmail()));
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.sendErrorsAsEmail();
|
||||||
|
|
||||||
|
}
|
||||||
// add for debugging
|
// add for debugging
|
||||||
if (email_user) {
|
if (email_user) {
|
||||||
//printf("send email to %s\n", user_model->getEmail().data());
|
//printf("send email to %s\n", user_model->getEmail().data());
|
||||||
auto user_model = email_user->getModel();
|
auto user_model = email_user->getModel();
|
||||||
std::string log_message = "Email sended to: ";
|
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
|
||||||
|
std::string log_message = dateTimeString + " Email sended to: ";
|
||||||
|
if (!email_sended) {
|
||||||
|
log_message = dateTimeString + " Email not sended to: ";
|
||||||
|
}
|
||||||
if (user_model) {
|
if (user_model) {
|
||||||
log_message += email_user->getModel()->getNameWithEmailHtml();
|
log_message += email_user->getModel()->getNameWithEmailHtml();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,14 +14,6 @@ server {
|
|||||||
root /var/www/cakephp/webroot;
|
root /var/www/cakephp/webroot;
|
||||||
index index.php;
|
index index.php;
|
||||||
|
|
||||||
location ~* \.(png|jpg|ico|webp)$ {
|
|
||||||
expires 30d;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(js|css) {
|
|
||||||
# expires 1d;
|
|
||||||
expires 1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
fastcgi_pass community-server:9000;
|
fastcgi_pass community-server:9000;
|
||||||
@ -73,6 +65,29 @@ server {
|
|||||||
try_files $uri $uri/ /index.php?$args;
|
try_files $uri $uri/ /index.php?$args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /vue {
|
||||||
|
|
||||||
|
location /vue/sockjs-node {
|
||||||
|
rewrite /vue/(.*) /$1;
|
||||||
|
}
|
||||||
|
location ~* \.(png) {
|
||||||
|
expires 1d;
|
||||||
|
rewrite /vue/(.*) /$1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
#rewrite /vue/(.*) /$1 break;
|
||||||
|
|
||||||
|
proxy_pass http://frontend:8080;
|
||||||
|
proxy_redirect off;
|
||||||
|
}
|
||||||
|
|
||||||
# access_log /var/log/nginx/access.log main;
|
# access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||