{"_id":"@crisog/court-backend-server","_rev":"1-069404f0c313dd70889f667acf377571","name":"@crisog/court-backend-server","dist-tags":{"latest":"0.2.22"},"versions":{"0.2.22":{"name":"@crisog/court-backend-server","version":"0.2.22","author":{"name":"Aragon One"},"license":"(GPL-3.0-or-later OR AGPL-3.0-or-later)","engines":{"node":">=9.0.0"},"scripts":{"build":"babel ./src --out-dir ./build --source-maps --copy-files","start":"npm run build && ./scripts/db-setup.sh && node ./build","start:dev":"./scripts/db-setup.sh && nodemon --ignore ./build --exec babel-node ./src/index.js","knex":"npx babel-node ./node_modules/.bin/knex","test":"npx mocha test --recursive --exit --require @babel/register"},"dependencies":{"@aragon/court":"1.1.0","@aragonone/court-backend-shared":"^0.2.22","@promster/express":"^4.0.0","@promster/server":"^4.0.0","bcryptjs":"^2.4.3","body-parser":"^1.19.0","cookie-parser":"^1.4.5","core-js":"^3.6.2","cors":"^2.8.5","dotenv":"^8.2.0","ethers":"^4.0.47","express":"^4.17.1","express-session":"^1.17.0","helmet":"^3.21.2","http-status-codes":"^1.4.0","jsonwebtoken":"^8.5.1","knex":"^0.21.0","morgan":"^1.9.1","objection":"^2.1.3","pg":"^7.18.2","pg-hstore":"^2.3.3","postmark":"^2.5.3","prom-client":"^11.5.3","regenerator-runtime":"^0.13.3","validator":"^13.0.0","web3-utils":"^1.2.4"},"devDependencies":{"@babel/cli":"^7.7.7","@babel/core":"^7.7.7","@babel/node":"^7.7.7","@babel/preset-env":"^7.7.7","chai":"^4.2.0","chai-http":"^4.3.0","mocha":"^7.1.1","nodemon":"^2.0.2"},"description":"This server aims to provide different kind of services to complement the logic implemented at the smart contracts level.","_id":"@crisog/court-backend-server@0.2.22","_nodeVersion":"10.22.1","_npmVersion":"6.14.6","dist":{"integrity":"sha512-+fj7EZArq80wBkJ058Q0WRLMbGObKmp4vxmTnnBRBzUaUx3RTDZn+Mu0/pnH16H+ajHI/WTtO7p3lMQORXCyUQ==","shasum":"e6fd36ca8aece5dc774551c9eb3f61f4f2f660e4","tarball":"https://registry.npmjs.org/@crisog/court-backend-server/-/court-backend-server-0.2.22.tgz","fileCount":66,"unpackedSize":79863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLUwiCRA9TVsSAnZWagAAvxwQAKBbPrUvV6CUfRaMo5bG\nEvw8IB/OIBL8PkEUBR8yXvNmdwcCx5RcBD47XoEBRSycGp4LPL6Es5oTWYj1\nDsh0nmOySK7Hf69w734bqJn00K/iX5sx2lx8XwXzR+6BqlduI9RHQiTrcSVY\n9ZGfDYQNIIRSEp5Uep+SNLnJuHM1m4JlG4br83vMTqHqVe54KZqAfJWE1as4\ny4F7vuuPRXH9YlHRlkRjOUIFLEK+TpbWSFHLT/z8JO6Fj1XS/Ghx0CeT/8+3\nM7Yd/AvKGFCzmuT7K0iAC40HAa34tnOe67YzTvo9oG8/XjIkd1wgiqefhgMu\nuI2BD4sIX/3shZ9Lc9CIGW7BSM4sGPJsTzDesB5OanW2jtd60EKU0OXW7ol2\nlfs4o8K4OBLYv1fvlg5OhL/ZojvISvP2WFlTNdypFGLj/dgE2g8ly4FZ9bU+\nRSQIuGTT6dstYXKDbnj+POscN2ZLm5EGFh+sgM9gUZPBFd4TYlOW/nk4pUAU\nZYk4eL0lqsik3ek1gVWYWr6hkght38PmLTyiil/gFKmEsRD9p2d+O93p6CUj\nq8IoD2Djde0Gi8MXHhwBppIvbhyYspgV0iMnH6iIed9E+dQsvgkYfBMCi6Fs\nMxYQThGfL6Bu2fw/XE3Y8EnC81upa/MUBIwsad7brtwPmnSsPe8Y4GJgQbNm\nTNHr\r\n=mrdW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC3Vsua58bV2G5ptIZk2D6ASQlrcbFYwECVF05lAP9QGAiEA50z6RsEPgSxRH4LFSOQJ4uq6nzRfqvzCp+ohHRkVGgQ="}]},"_npmUser":{"name":"crisog","email":"ortega.cpp@gmail.com"},"directories":{},"maintainers":[{"name":"crisog","email":"ortega.cpp@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/court-backend-server_0.2.22_1613581345691_0.06475197876165417"},"_hasShrinkwrap":false}},"time":{"created":"2021-02-17T17:02:25.652Z","0.2.22":"2021-02-17T17:02:25.904Z","modified":"2022-04-05T01:49:49.440Z"},"maintainers":[{"name":"crisog","email":"ortega.cpp@gmail.com"}],"description":"This server aims to provide different kind of services to complement the logic implemented at the smart contracts level.","author":{"name":"Aragon One"},"license":"(GPL-3.0-or-later OR AGPL-3.0-or-later)","readme":"# Court Backend server\n\nThis server aims to provide different kind of services to complement the logic implemented at the smart contracts level.\n\n### Setup\n\nTo work locally, simply go to the root directory, and make sure you have set up a propoer `.env` file following the `.env.sample` file.\nOnce you have done that, spin up a docker container with:\n```bash\ndocker-compose build\ndocker-compose up -d\n```\n\n### Endpoints\n\nAll the provided endpoints are `Content-Type: application/json`\n\n#### 1. Users\n\n##### 1.1. Check existing subscription details\n\n  Request:\n\n  - Method: `GET`\n  - Path: `/users/<address>`\n  - Body: None\n\n  Successful response: \n\n  - Code: `200 OK`\n  - Body:\n\n  ```\n  {\n    \"emailExists\": true || false,\n    \"emailVerified\": true || false,\n    \"addressVerified\": true || false,\n    \"notificationsDisabled\": true || false\n  }\n  ```\n\n##### 1.2. Create session\n\n  This request will authenticate the session with address signature and respond with the appropriate session `Set-Cookie` header. In addition, it will set `\"addressVerified\": true` using the provided signature.\n\n  All following requests should have the returned `Cookie` HTTP header in order to be authenticated.\n\n  Request:\n\n  - Method: `POST`\n  - Path: `/users/<address>/sessions`\n  - Body:\n\n  ```json\n  {\n    \"signature\": \"0xa8afa92...8b1e52a\",\n    \"timestamp\": 123456\n  }\n  ```\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"authenticated\": true\n  }\n  ```\n\n  Error responses: invalid signature / timestamp\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```\n  {\n    \"errors\": [\n      { \"signature\": \"Given signature is invalid\" }\n      ...\n      { \"timestamp\": \"Given timestamp is invalid\" }\n    ]\n  }\n  ```\n\n##### 1.3. Delete session\n\n  Request:\n\n  - Method: `DELETE`\n  - Path: `/users/<address>/sessions:current`\n  - Body: None\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"deleted\": true\n  }\n  ```\n\n##### 1.4. Delete sessions on all user devices\n\n  Request:\n\n  - Method: `DELETE`\n  - Path: `/users/<address>/sessions`\n  - Body: None\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"deleted\": true\n  }\n  ```\n\n##### 1.5. Get current email\n\n  Request:\n\n  - Method: `GET`\n  - Path: `/users/<address>/email`\n  - Body: None\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```\n  {\n    \"email\": \"new-juror@aragoncourt.com\" || null\n  }\n  ```\n\n##### 1.6. Subscribe juror / Change juror email\n\n  Note: this will also automatically send verification email\n\n  Request:\n\n  - Method: `PUT`\n  - Path: `/users/<address>/email`\n  - Body:\n\n  ```json\n  {\n    \"email\": \"juror@aragoncourt.com\"\n  }\n  ```\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"email\": \"juror@aragoncourt.com\",\n    \"sent\": true\n  }\n  ```\n\n  Error response: email already set / bad format\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```\n  {\n    \"errors\": [\n      { \"email\": \"Given email is already set\" }\n      ...\n      { \"email\": \"Given email address is not valid\" }\n    ]\n  }\n  ```\n\n  Error response: Could not send email\n\n  - Code: `500 Internal Server Error`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"email\": \"Could not send email.\" }\n    ]\n  }\n  ```\n\n##### 1.7. Verify juror email\n\n  Note: this endpoint is unauthenticated\n\n  Request:\n\n  - Method: `POST`\n  - Path: `/users/<address>/email:verify`\n  - Body:\n\n  ```json\n  {\n    \"token\": \"V5Z6drJdytlNa98asfnOs13Gf90K9vZFVdSQ\"\n  }\n  ```\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"verified\": true\n  }\n  ```\n\n  Error response: user email / token errors\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```\n  {\n    \"errors\": [\n      { \"email\": \"No associated email found\" }\n      ...\n      { \"email\": \"Email is already verified\" }\n      ...\n      { \"token\": \"A token must be given\" }\n      ...\n      { \"token\": \"Given token is invalid\" }\n      ...\n      { \"token\": \"Given token has expired\" }\n    ]\n  }\n  ```\n\n##### 1.8. Re-send verification email\n\n  Request:\n\n  - Method: `POST`\n  - Path: `/users/<address>/email:resend`\n  - Body: None\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"sent\": true\n  }\n  ```\n\n  Error response: email errors\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```\n  {\n    \"errors\": [\n      { \"email\": \"No associated email found\" }\n      ...\n      { \"email\": \"Email is already verified\" }\n    ]\n  }\n  ```\n\n  Error response: Could not send email\n\n  - Code: `500 Internal Server Error`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"email\": \"Could not send email.\" }\n    ]\n  }\n  ```\n\n##### 1.9. Delete email / Cancel sign up process\n\n  Request:\n\n  - Method: `DELETE`\n  - Path: `/users/<address>/email`\n  - Body: None\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"deleted\": true\n  }\n  ```\n\n##### 1.10. Switch notifications off/back on\n\n  Request:\n\n  - Method: `PUT`\n  - Path: `/users/<address>/notifications`\n  - Body:\n\n  ```\n  {\n    \"disabled\": true || false\n  }\n  ```\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```\n  {\n    \"disabled\": true || false\n  }\n  ```\n\n  Error response: missing option\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"disabled\": \"request must contain a boolean \\\"disabled\\\" property\" }\n    ]\n  }\n  ```\n\n##### 1.11. Create unverified user\n\n  - Method: `POST`\n  - Path: `/users`\n  - Body:\n\n  ```json\n  {\n    \"email\": \"juror@aragoncourt.com\",\n    \"address\": \"0x6e26ADFa527BcC8B6aEf88716486cBdb4f7914e1\"\n  }\n  ```\n\n  Successful response:\n\n  - Code: `200 OK`\n  - Body:\n\n  ```json\n  {\n    \"created\": true\n  }\n  ```\n\n  Error response: address / email errors\n\n  - Code: `400 Bad Request`\n  - Body:\n\n  ```\n  {\n    \"errors\": [\n      { \"address\": \"An address must be given\" }\n      ...\n      { \"address\": \"Given address is not valid\" }\n      ...\n      { \"email\": \"An email address must be given\" }\n      ...\n      { \"email\": \"An email address must be given\" }\n      ...\n      { \"email\": \"Given email address is not valid\" }\n    ]\n  }\n  ```\n\n\n##### 1.A1. Session error responses\n\n  Error response: missing user\n\n  - Code: `404 Not Found`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"address\": \"User <address> not found.\" }\n    ]\n  }\n  ```\n\n  Error response: no session found\n\n  - Code: `401 Unauthorized`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"access\": \"Unauthorized, please authenticate at /users/<address>/sessions\" }\n    ]\n  }\n  ```\n\n  Error response: session found for another user\n\n  - Code: `403 Forbidden`\n  - Body:\n\n  ```json\n  {\n    \"errors\": [\n      { \"access\": \"You don't have permission to edit user <address>\" }\n    ]\n  }\n  ```\n\n#### 2. Reveals\n\n##### 2.1. Create\n\n- URL: /reveals\n- Method: POST\n- Body: \n  - `juror`: Ethereum address of the juror requesting for the reveal \n  - `voteId`: Vote ID to be revealed for the given juror\n  - `outcome`: Outcome voted in favor of\n  - `salt`: Salt used for the committed vote to be revealed\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"reveal\": {\n          \"id\": 1,\n          \"juror\": \"0x4ecc4fe717d70abee26e7e524b2e6caf29b6217d\",\n          \"voteId\": \"0\", \n          \"disputeId\": \"0\", \n          \"roundNumber\": \"0\",\n          \"createdAt\": \"2019-12-25T14:58:58.705Z\",\n          \"updatedAt\":\"2019-12-25T14:58:58.705Z\"\n        }\n      }\n    ```\n\n##### 2.2. Show\n\n- URL: /reveals/:juror/:voteId\n- Method: GET\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"reveal\": {\n          \"id\": 1,\n          \"juror\": \"0x4ecc4fe717d70abee26e7e524b2e6caf29b6217d\",\n          \"voteId\": \"0\", \n          \"disputeId\": \"0\", \n          \"roundNumber\": \"0\",\n          \"createdAt\": \"2019-12-25T14:58:58.705Z\",\n          \"updatedAt\":\"2019-12-25T14:58:58.705Z\"\n        }\n      }\n    ```\n\n##### 2.3. All\n\n- URL: /reveals\n- Method: GET\n- Header:\n  - Cookie: `aragonCourtSessionID=<SID>`\n- Query: \n  - `limit`: Number of items to be fetched\n  - `page`: Page number to be used for the items to be fetched based on the limit requested\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"reveals\":[\n          {\n            \"id\": 9,\n            \"juror\": \"0x4ecc4fe717d70abee26e7e524b2e6caf29b6217d\",\n            \"voteId\": \"0\", \n            \"disputeId\": \"0\", \n            \"roundNumber\": \"0\",\n            \"outcome\": \"4\",\n            \"salt\": \"0x609a2445eb34bc29b4d87aea2cae24fba90a1583b14df2d765ae1f89d32b4beb\",\n            \"revealed\": false,\n            \"createdAt\": \"2019-12-25T14:58:58.705Z\",\n            \"updatedAt\":\"2019-12-25T14:58:58.705Z\"\n          }\n        ],\n        \"total\": 1\n      }\n    ```\n\n#### 3. Admins\n\n##### 3.1. Login\n\n- URL: /login\n- Method: POST\n- Body: \n  - `email`: Admin email\n  - `password`: Admin password\n- Response: \n  - Code: 200\n  - Header:\n    - Set-Cookie: `aragonCourtSessionID=<SID>; Path; Expires; HttpOnly`\n  - Content example: \n    ```json\n      {\n        \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ImZhY3VAYXJhZ29uLm9uZSIsImlhdCI6MTU3ODA4MTI5NCwiZXhwIjoxNTc4MDg0ODk0fQ.lmcJOE0eblD_maqXqyK0_H2swXnvG9lfEm1aJRGiAVw\"\n      }\n    ```\n    \n\n##### 3.2. Me\n\n- URL: /me\n- Method: GET\n- Header:\n  - Cookie: `aragonCourtSessionID=<SID>`\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"admin\": {\n          \"id\": 1,\n          \"email\": \"admin@aragon.one\"\n        }\n      }\n    ```\n\n##### 3.3. All\n\n- URL: /admins\n- Method: GET\n- Header:\n  - Cookie: `aragonCourtSessionID=<SID>`\n- Query: \n  - `limit`: Number of items to be fetched\n  - `page`: Page number to be used for the items to be fetched based on the limit requested\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"reveals\":[\n          {\n            \"id\": 9,\n            \"juror\": \"0x4ecc4fe717d70abee26e7e524b2e6caf29b6217d\",\n            \"voteId\": \"0\", \n            \"disputeId\": \"0\", \n            \"roundNumber\": \"0\",\n            \"outcome\": \"4\",\n            \"salt\": \"0x609a2445eb34bc29b4d87aea2cae24fba90a1583b14df2d765ae1f89d32b4beb\",\n            \"revealed\": false,\n            \"createdAt\": \"2019-12-25T14:58:58.705Z\",\n            \"updatedAt\":\"2019-12-25T14:58:58.705Z\"\n          }\n        ],\n        \"total\": 1\n      }\n    ```\n\n##### 3.4. Create\n\n- URL: /admins\n- Method: POST\n- Header:\n  - Cookie: `aragonCourtSessionID=<SID>`\n- Body: \n  - `email`: Admin email \n  - `password`: Admin password\n- Response: \n  - Code: 200\n  - Content example: \n    ```json\n      {\n        \"admin\": {\n          \"id\": 2,\n          \"email\": \"admin@aragon.one\"\n        }\n      }\n    ```\n\n##### 3.5. Delete\n\n- URL: /admins/:id\n- Method: DELETE\n- Header:\n  - Cookie: `aragonCourtSessionID=<SID>` \n- Response: \n  - Code: 200\n  - Content example: empty\n\n### Keys\n\nThis repo needs the private key to be defined as a envrionment variable `PRIVATE_KEY`. \n\nAlso email verification requires JWT private key `EMAIL_JWT_PRIVATE_KEY`.\nThis can be generated with `node -e \"console.log(require('crypto').randomBytes(256).toString('base64'));\"`\n","readmeFilename":"README.md"}