Giter Site home page Giter Site logo

shadowsocks / shadowsocks-hub-api Goto Github PK

View Code? Open in Web Editor NEW
25.0 7.0 27.0 107 KB

A set of open and standard restful APIs for managing shadowsocks users, servers, nodes, products, accounts, and traffic.

JavaScript 100.00%
shadowsocks management users servers accounts traffic restful-api

shadowsocks-hub-api's Introduction

Shadowsocks Hub API

Shadowsocks Hub API provides a set of open and standard restful APIs for managing shadowsocks users, servers, nodes, products, accounts, and traffic. It is best suitable for companies, organizations, and groups of people to manage their internal shadowsocks infrastructures.

Its web app (including graphic UI) can be found from shadowsocks-hub. Alternatively, you may choose to develop your own shadowsocks management UI by utilizing this set of API. All common features have been made available in the form of restful APIs.

Shadowsocks Hub API is developed using Nodejs. It uses MySQL as its underlying database and shadowsocks-libev as its shadowsocks implementation.

Install (Ubuntu 16.04)

  1. Install Nodejs 6 or above (8 preferred).

  2. Install MySQL.

  3. Download and install Shadowsocks Hub API:

    cd ~
    git clone https://github.com/shadowsocks/shadowsocks-hub-api.git
    cd ~/shadowsocks-hub-api
    npm i
    
  4. Create a MySQL database sshub:

    CREATE DATABASE sshub;
    
  5. Create an environment file .env:

    cd ~/shadowsocks-hub-api
    touch .env
    
  6. Add the following configuration key value pairs to .env:

    JWT_SECRET = 2wk0M@ow094B^&9k3==~o2soejd$sEEo@2(
    LISTEN_PORT = 8000
    
    DATABASE_HOST = localhost
    DATABASE_PORT = 3306
    DATABASE_USER = root
    DATABASE_PASSWORD = d4f889df22769f54
    

    Change the values about the database connection to your local configuration.
    Change the value of JWT_SECRET with a long and random string.
    Change the value LISTEN_PORT to your preferred port for Shadowsocks Hub API to listen to.

  7. Initialize database:

    cd ~/shadowsocks-hub-api
    knex migrate:latest --env production
    
  8. Setup digital certificate

    All requests and responses are encrypted using https. It requires you to set up a digital certificate. You may create your own self-signed certificate:

    cd ~/shadowsocks-hub-api
    openssl req -nodes -x509 -newkey rsa:4096 -keyout server.key -out server.cert -days 365
    

    Alternatively, you may copy your digital certificate and key pair that you obtained from a Certificate Authority (e.g. Let's Encrypt) to ~/shadowsocks-hub-api with the certificate named as server.cert and the key named as server.key.

  9. Shadowsocks Hub API utilize shadowsocks-restful-api to manage shadowsocks protocol. Install it on every server that you would like to use as a shadowsocks node.

Update

If you have updated Shadowsocks Hub API from an older version to the latest version, run:

cd ~/shadowsocks-hub-api
knex migrate:latest --env production

Run

  1. Run Shadowsocks Hub API:

    cd ~/shadowsocks-hub-api
    node api.js
    
  2. Change admin credential

    For the sake of security, you should change the default admin user credential as soon as possible. This can be done by using the login API to obtain a token, and then using the update user API to change the username and password of the admin user. The default username and password for admin user are [email protected] and pleaseChangePassword, respectively.

  3. Run shadowsocks-restful-api on every server that you would like to use as a shadowsocks node.

Authentication

All the APIs except for the login API require an Authorization header. The header pattern is: Authorization: Bearer <token>. The token can be obtained from the login or refresh token API. The authentication is compatiable with OAuth 2. There are 3 APIs relating to authentication: login, refresh token, and invalidate refresh token.

Access token expires in 15 minutes while refresh token exipires in 1 week. Both login and refresh token APIs will produce a pair of new access token and refresh token. The difference is that login API requires username and password while refresh token API requires a valid refresh token to be provided in the Authorization Bearer header.

The invalidate refresh token API can be used to invalidate all previously obtained refresh tokens.

Rate limit

You may enforce a rate limit by setting the maximum number of requests allowed within 15-minute window from the same ip address. Requests exceeding the limit will be refused with HTTP status code 429 Too Many Requests. This setting can be done by add the following config to the .env file. RATE_LIMIT = 50

Change the number to your choice.

Bug report and feature request

Bug report and feature request are welcome. Bugs have a high priority to get addressed. Feature requests will be considered depending on their popularity and importance.

APIs

1. User APIs

There are two type of users: admin user and normal user. This API is OAuth 2 compatiable. Admin user has priviliege to make any API requests. Normal user has priviliege to make API requests relating to themselves only.

Login

Both admin user and normal user can call this API.

Request method: POST
Request URL: https://host_name:port/api/user/login
Request Header: Content-Type: application/json
Request Body: {"username":"your_username", "password":"your_login_password"}
Response HTTP Status Code: 201 Created
Response Body: {"token":"new_authentication_token", "refreshToken":"new_refresh_token"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -X POST -d '{"username":"[email protected]","password":"pleaseChangePassword"}' https://localhost:8000/api/user/login

Response example:

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDMzMiwiZXhwIjoxNTMxMjI1MjMyfQ.h82mSltnLua-XLPHyV7X2-lqe94O7dYE7Ujachg3NDY","refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDMzMiwiZXhwIjoxNTMxODI5MTMyfQ.PIL-thIdKr2ji5LDOelLXPfIZQUvo3II1KrOG0-lLx4"}

Refresh Token

Both admin user and normal user can call this API. This API is OAuth 2 compatiable. You will get a set of new token and refreshToken. Note that you need supply refreshToken instead of token to Authorization Bearer header.

Request method: GET
Request URL: https://host_name:port/api/user/refresh_token
Request Header: Content-Type: application/json
Authorization: Bearer your_refresh_token
Response HTTP Status Code: 200 OK
Response Body: {"token":"new_authentication_token", "refreshToken":"new_refresh_token"}
Response Error Status Code: 401 Unauthorized
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDMzMiwiZXhwIjoxNTMxODI5MTMyfQ.PIL-thIdKr2ji5LDOelLXPfIZQUvo3II1KrOG0-lLx4" https://localhost:8000/api/user/refresh_token

Response example:

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDM4NSwiZXhwIjoxNTMxMjI1Mjg1fQ.U_A74GyZy9xGZsj49weYaPK99iZeBaIFkCX9GVx9_dE","refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDM4NSwiZXhwIjoxNTMxODI5MTg1fQ.6OoLLVmTAsHMtDsLWPl9lS19w1WMQeyNyL9WLReEjaQ"}

Invalidate Refresh Token

Both admin user and normal user can call this API. This API is OAuth 2 compatiable. You will invalidate all previously obtained refreshToken. Note that you need supply refreshToken instead of token to Authorization Bearer header.

Request method: POST
Request URL: https://host_name:port/api/user/invalidate_refresh_token
Request Header: Content-Type: application/json
Authorization: Bearer your_refresh_token
Request Body: {}
Response HTTP Status Code: 201 Created
Response Body: {}
Response Error Status Code: 401 Unauthorized
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWVmYjQ3LTI4YzYtNDA1OC1hMDBiLTc1ZGI1MTQ0OGJkNyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMTIyNDMzMiwiZXhwIjoxNTMxODI5MTMyfQ.PIL-thIdKr2ji5LDOelLXPfIZQUvo3II1KrOG0-lLx4" -X POST -d '{"username":"[email protected]","password":"pleaseChangePassword"}' https://localhost:8000/api/user/invalidate_refresh_token

Response example:

{}

Create User

Only admin user can call this API.

Request method: POST
Request URL: https://host_name:port/api/user
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"role":"user", "email":"email_address", "password":"userPassword"}
Response HTTP Status Code: 201 Created
Response Body: {"id","user_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (user already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"role":"user","email":"[email protected]","password":"somePassword"}' https://localhost:8000/api/user

Response example:

{"id":"7768be69-b707-4111-a15c-84b7278bc588"}

Delete User

Only admin user can call this API.

Request method: DELETE
Request URL: https://host_name:port/api/user?id=user_id
Request Header: Authorization: Bearer your_authentication_token
Response HTTP Status Code: 204 No Content
Response Error Status Code: 400 Bad Request
401 Unauthorized
500 Internal Server Error

Request example (curl):

curl -ik -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X DELETE https://localhost:8000/api/user?id=7768be69-b707-4111-a15c-84b7278bc588

Get User

Both admin user and normal user can call this API. But normal user can get user about themselves. The admin user can get user about anyone.

Request method: GET
Request URL: https://host_name:port/api/user?id=user_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"id":"user_id", "type":"EmailUser", "role":"admin", "email":"email_address", "createdTime":epoch_time, "username":"email_address", "lastLoginTime":epoch_time}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (user does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/user?id=e2da99e0-7d47-4ce2-b8c8-3088e0132459

Response example:

{"id":"e2da99e0-7d47-4ce2-b8c8-3088e0132459","type":"EmailUser","role":"admin","email":"[email protected]","createdTime":1530753369303,"username":"[email protected]","lastLoginTime":1530759474686}

Update User

Both admin user and normal user can call this API. But normal user can get user about themselves. The admin user can get user about anyone.

Request method: PUT
Request URL: https://host_name:port/api/user
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"id":"user_id", "role":"user_role", "email":"new_email_address", "password":"new_password"}
Response HTTP Status Code: 200 OK
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (user does not exist)
409 Conflict (new email address already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X PUT -d '{"id":"e2da99e0-7d47-4ce2-b8c8-3088e0132459", "role":"admin","email":"[email protected]","password":"newPassword"}' https://localhost:8000/api/user

2. Server APIs

Servers are physical shadowsocks servers. Multiple servers are supported. Each server must have an external ip address or domain name that can be used to be connected by user shadowsocks clients.

Create Server

Only admin user can call this API.

Request method: POST
Request URL: https://host_name:port/api/server
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"ipAddressOrDomainName":"server_ip_address_or_domain_name"}
Response HTTP Status Code: 201 Created
Response Body: {"id","server_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (server already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"ipAddressOrDomainName":"127.0.0.1"}' https://localhost:8000/api/server

Response example:

{"id":"d0ee089d-f43d-4bae-b408-02f59db04e9c"}

Delete Server

Only admin user can call this API.

Request method: DELETE
Request URL: https://host_name:port/api/server?id=server_id
Request Header: Authorization: Bearer your_authentication_token
Response HTTP Status Code: 204 No Content
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (server is in use)
500 Internal Server Error

Request example (curl):

curl -ik -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X DELETE https://localhost:8000/api/server?id=d0ee089d-f43d-4bae-b408-02f59db04e9c

Get Server

Only admin user can call this API.

Request method: GET
Request URL: https://host_name:port/api/server?id=server_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"id":"server_id", "ipAddressOrDomainName":"server_ip_address_or_domain_name", "createdTime":epoch_time}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (server does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/server?id=c9be32dd-e2d6-4af4-876f-f086e82af20c

Response example:

{"id":"c9be32dd-e2d6-4af4-876f-f086e82af20c","ipAddressOrDomainName":"127.0.0.1","createdTime":1530773538890}

Update Server

Only admin user can call this API.

Request method: PUT
Request URL: https://host_name:port/api/server
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"id":"server_id", "ipAddressOrDomainName":"new_server_ip_address_or_domain_name"}
Response HTTP Status Code: 200 OK
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (server does not exist)
409 Conflict (new ip address or domain name already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X PUT -d '{"id":"c9be32dd-e2d6-4af4-876f-f086e82af20c","ipAddressOrDomainName":"127.0.0.2"}' https://localhost:8000/api/server

3. Node APIs

Nodes are logical shadowsocks servers. A node has to reside in one and only one server. A server may have multiple nodes. All nodes within a server appear like different shadowsocks servers to users, but they share the same resources of the server. A node must have a unique name, a port number and password that is used by shadowsocks-restful-api.

Create Node

Only admin user can call this API.

Request method: POST
Request URL: https://host_name:port/api/node
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"serverId":"server_id", "port":port_number, "password":"somePassword", "name":"someName"}
Response HTTP Status Code: 201 Created
Response Body: {"id","node_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (server does not exist)
409 Conflict (node already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"serverId":"c9be32dd-e2d6-4af4-876f-f086e82af20c", "port":4001, "password":"pleaseChangeThisPassword", "name":"New York"}' https://localhost:8000/api/node

Response example:

{"id":"5daefb47-28c6-4058-a00b-75db51448bd7"}

Delete Node

Only admin user can call this API.

Request method: DELETE
Request URL: https://host_name:port/api/node?id=node_id
Request Header: Authorization: Bearer your_authentication_token
Response HTTP Status Code: 204 No Content
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (node is in use)
500 Internal Server Error

Request example (curl):

curl -ik -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X DELETE https://localhost:8000/api/node?id=5daefb47-28c6-4058-a00b-75db51448bd7

Get Node

Only admin user can call this API.

Request method: GET
Request URL: https://host_name:port/api/node?id=node_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"id":"node_id", "server":{"id":"server_id", "ipAddressOrDomainName":"server_ip_address_or_domain_name", "createdTime":server_created_time}, "password":"some_password", "port":node_management_port_number, "name":"node_name", "comment":"come_comment", "createdTime":node_createdTime}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (node does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/node?id=075a74fd-d8ab-4313-bde3-f9cb99d7215d

Response example:

{"id":"075a74fd-d8ab-4313-bde3-f9cb99d7215d","server":{"id":"7b15f7ba-ee6c-452d-8264-0657f202590d","ipAddressOrDomainName":"127.0.0.1","createdTime":1530790525902},"password":"pleaseChangeThisPassword","port":4001,"name":"New York","comment":null,"createdTime":1530790577846}

Update Node

Only admin user can call this API.

Request method: PUT
Request URL: https://host_name:port/api/node
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"id":"node_id", "port":new_port_number, "password":"newPassword", "name":"newName"}
Response HTTP Status Code: 200 OK
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (node does not exist)
409 Conflict (new name already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X PUT -d '{"id":"075a74fd-d8ab-4313-bde3-f9cb99d7215d", "port":4002, "password":"newPassword", "name":"newName", "comment":"some comment"}' https://localhost:8000/api/node

4. Product APIs

Products are different types of shadowsocks services. A product must have a unique name, traffic, and period. The period of a product defines the life span of an account associated with this product. The traffic of a product defines the maximum amount of traffic allowed for an account associated with this product with in its period.

Create Product

Only admin user can call this API.

Request method: POST
Request URL: https://host_name:port/api/product
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"name":"some name", "traffic":max_traffic_allowed, "period":"valid_period"}
Response HTTP Status Code: 201 Created
Response Body: {"id","product_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (product already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"name":"dimond", "traffic":1000000000, "period":"monthly"}' https://localhost:8000/api/product

Response example:

{"id":"66c5a883-f729-498b-9442-f3d0bbca9345"}

Delete Product

Only admin user can call this API.

Request method: DELETE
Request URL: https://host_name:port/api/product?id=product_id
Request Header: Authorization: Bearer your_authentication_token
Response HTTP Status Code: 204 No Content
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (product is in use)
500 Internal Server Error

Request example (curl):

curl -ik -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X DELETE https://localhost:8000/api/product?id=66c5a883-f729-498b-9442-f3d0bbca9345

Get Product

Only admin user can call this API.

Request method: GET
Request URL: https://host_name:port/api/product?id=product_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"id":"server_id", "ipAddressOrDomainName":"server_ip_address_or_domain_name", "createdTime":epoch_time}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (product does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/product?id=b35aa689-756c-4752-bb7a-9b3e8e0dd699

Response example:

{"id":"b35aa689-756c-4752-bb7a-9b3e8e0dd699","name":"dimond","traffic":1000000000,"period":"monthly","createdTime":1530793170070}

Update Product

Only admin user can call this API.

Request method: PUT
Request URL: https://host_name:port/api/node
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"id":"product_id", "name":"newName", "traffic":newTraffic, "period":"newPeriod"}
Response HTTP Status Code: 200 OK
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (product does not exist)
409 Conflict (new name already exists)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X PUT -d '{"id":"b35aa689-756c-4752-bb7a-9b3e8e0dd699", "name":"newName", "traffic":5000000000, "period":"annual"}' https://localhost:8000/api/product

5. Account APIs

Accounts contain all information needed for a shadowsocks client to connect to a node. A user has to obtain an account before being able to use a product. An account contains data of its owner, the node and product associated with the account.

Request Account

Both admin user and normal user can call this API. But normal user can request account for themselves only. The admin user can do so for anyone.

Request method: POST
Request URL: https://host_name:port/api/account/request
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"userId":"user_id", "productId":"product_id"}
Response HTTP Status Code: 201 Created
Response Body: {"id","approval_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (user does not exist)
420 (product does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"userId":"5daefb47-28c6-4058-a00b-75db51448bd7", "productId":"b35aa689-756c-4752-bb7a-9b3e8e0dd699"}' https://localhost:8000/api/account/request

Response example:

{"id":"480e723d-6e61-4440-a6dd-6ae624342a8e"}

Approve Request (create accounts)

Only admin user can call this API.

Request method: POST
Request URL: https://host_name:port/api/account/approve
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Request Body: {"id","approval_id"}
Response HTTP Status Code: 201 Created
Response Body: {"id","approval_id"}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (approval_id does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X POST -d '{"id":"0e3f59ac-693c-4fbf-a286-6a9de41d3a74"}' https://localhost:8000/api/account/approve

Response example:

{"id":"0e3f59ac-693c-4fbf-a286-6a9de41d3a74"}

Delete Account

Only admin user can call this API.

Request method: DELETE
Request URL: https://host_name:port/api/account?id=account_id
Request Header: Authorization: Bearer your_authentication_token
Response HTTP Status Code: 204 No Content
Response Error Status Code: 400 Bad Request
401 Unauthorized
409 Conflict (account is in use)
500 Internal Server Error

Request example (curl):

curl -ik -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" -X DELETE https://localhost:8000/api/account?id=66c5a883-f729-498b-9442-f3d0bbca9345

Get Account

Both admin user and normal user can call this API. But normal user can get account belonging to themselves only. The admin user can get any account.

Request method: GET
Request URL: https://host_name:port/api/account?id=account_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"id":"account_id", "node":{"id":"node_id", "server":{"id":"server_id", "ipAddressOrDomainName":"server_ip_address_or_domain_name", "createdTime":server_created_time}, "port":node_management_port_number, "name":"node_name", "comment": "noe_comment", "createdTime":node_created_time},"port":account_port_number, "createdTime":account__created_time, "password":"account_password", "method":"encryption_method", "approval":{"id":"approval_id", "state":"approval_state", "createdTime":approval_created_time}, "request":{"id":"request_id", "user":{"id":"user_id", "role":"user_role", "email":"user_email_address", "createdTime":user_created_time, "username":"user_email_address"}, "product":{"id":"product_id", "name":"product_name", "traffic":product_traffic, "period":"product_period", "createdTime":product_created_time}, "createdTime":request_created_time}}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (account does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/account?id=b35aa689-756c-4752-bb7a-9b3e8e0dd699

Response example:

{"id":"c2e5a807-9525-4c23-b121-6024e2d1eebb","node":{"id":"cd41dc00-2e53-4d7f-8374-b44e82caaeaa","server":{"id":"aec2a2ad-ecb4-4a12-b90d-4acaeb8ec676","ipAddressOrDomainName":"127.0.0.1","createdTime":1530838310353},"port":4001,"name":"New York","comment":null,"createdTime":1530838367312},"port":50647,"createdTime":1530847875199,"password":"ee60fe5cf97433e9","method":"aes-256-cfb","approval":{"id":"1ab123d0-92d6-4c6d-991e-c82cd03182f2","state":"completed","createdTime":1530842543004},"request":{"id":"4da6972a-2e10-4f20-8cc0-4ad66e886607","user":{"id":"ad5ceb7f-4a6d-4577-8ed6-01e3acb2604c","role":"user","email":"[email protected]","createdTime":1530838144205,"username":"[email protected]"},"product":{"id":"48e6a637-f94e-4d8e-b71c-9793a37e7e2c","name":"dimond","traffic":1000000000,"period":"monthly","createdTime":1530838417112},"createdTime":1530842543116}}

Get Accounts by User Id

Both admin user and normal user can call this API. But normal user can get accounts for themselves only. The admin user can accounts for any users.

Request method: GET
Request URL: https://host_name:port/api/account/accounts_by_user_id?id=user_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: [{"id":"account_id", "node":{"id":"node_id", "server":{"id":"server_id", "ipAddressOrDomainName":"server_ip_address_or_domain_name", "createdTime":server_created_time}, "port":node_management_port_number, "name":"node_name", "comment": "noe_comment", "createdTime":node_created_time},"port":account_port_number, "createdTime":account__created_time, "password":"account_password", "method":"encryption_method", "approval":{"id":"approval_id", "state":"approval_state", "createdTime":approval_created_time}, "request":{"id":"request_id", "user":{"id":"user_id", "role":"user_role", "email":"user_email_address", "createdTime":user_created_time, "username":"user_email_address"}, "product":{"id":"product_id", "name":"product_name", "traffic":product_traffic, "period":"product_period", "createdTime":product_created_time}, "createdTime":request_created_time}},...]
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (account does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/account/accounts_by_user_id?id=3ec1072e-e6e3-438d-99ad-2f891b877697

Response example:

[{"id":"c2e5a807-9525-4c23-b121-6024e2d1eebb","node":{"id":"cd41dc00-2e53-4d7f-8374-b44e82caaeaa","server":{"id":"aec2a2ad-ecb4-4a12-b90d-4acaeb8ec676","ipAddressOrDomainName":"127.0.0.1","createdTime":1530838310353},"port":4001,"name":"New York","comment":null,"createdTime":1530838367312},"port":50647,"createdTime":1530847875199,"password":"ee60fe5cf97433e9","method":"aes-256-cfb","approval":{"id":"1ab123d0-92d6-4c6d-991e-c82cd03182f2","state":"completed","createdTime":1530842543004},"request":{"id":"4da6972a-2e10-4f20-8cc0-4ad66e886607","user":{"id":"ad5ceb7f-4a6d-4577-8ed6-01e3acb2604c","role":"user","email":"[email protected]","createdTime":1530838144205,"username":"[email protected]"},"product":{"id":"48e6a637-f94e-4d8e-b71c-9793a37e7e2c","name":"dimond","traffic":1000000000,"period":"monthly","createdTime":1530838417112},"createdTime":1530842543116}}]

6. Traffic APIs

Traffic is the amount of data transmitted by a node for an account.

Get Account Latest Traffic

Both admin user and normal user can call this API. But normal user can get latest traffic belonging to themselves only. The admin user can do so for any account.

Request method: GET
Request URL: https://host_name:port/api/traffic/account?id=account_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: {"usage":latest_usage}
Response Error Status Code: 400 Bad Request
401 Unauthorized
404 Not Found (account does not exist)
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A"  https://localhost:8000/api/traffic/account?id=766341be-bc67-49e2-bc44-1c9ac485a56b

Response example:

{"usage":298270232}

Get Account Traffic History

Both admin user and normal user can call this API. But normal user can get traffic history belonging to themselves only. The admin user can do so for any account.

Request method: GET
Request URL: https://host_name:port/api/traffic/history?id=account_id
Request Header: Content-Type: application/json
Authorization: Bearer your_authentication_token
Response HTTP Status Code: 200 OK
Response Body: [{"usage":accumulative_usage, "createdTime":epoch_time}...]
Response Error Status Code: 400 Bad Request
401 Unauthorized
500 Internal Server Error

Request example (curl):

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUyZGE5OWUwLTdkNDctNGNlMi1iOGM4LTMwODhlMDEzMjQ1OSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUzMDc1OTQ3NCwiZXhwIjoxNTMwODQ1ODc0fQ.XqS8UBj7hWNeKjaGlXjrZDHuVZWM_8thw__ojAkBG0A" https://localhost:8000/api/traffic/history?id=b12bd8d0-5943-48c4-9016-d45a654c5a1c

Response example:

[{"usage":0,"createdTime":1530840202934},{"usage":19380263,"createdTime":1530840300014}]

shadowsocks-hub-api's People

Contributors

eggham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

shadowsocks-hub-api's Issues

Crash when run api.js

I followed steps of documentation to start shadow socks hub nut I get the following crash log.

    throw new Error('Mode must be "sandbox" or "live"');
    ^

Error: Mode must be "sandbox" or "live" at Object.configure (/root/shadowsocks-hub-api/node_modules/paypal-rest-sdk/lib/api.js:26:15) at Object.configure (/root/shadowsocks-hub-api/node_modules/paypal-rest-sdk/lib/paypal-rest-sdk.js:10:13) at Object.regeneratorRuntime.mark.regeneratorRuntime.wrap.e.prev (/root/shadowsocks-hub-api/api.js:6037:7) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.<anonymous> (/root/shadowsocks-hub-api/api.js:1359:23) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.regeneratorRuntime.mark.regeneratorRuntime.wrap.e.prev (/root/shadowsocks-hub-api/api.js:6228:23) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.<anonymous> (/root/shadowsocks-hub-api/api.js:11026:23) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.<anonymous> (/root/shadowsocks-hub-api/api.js:11034:13) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.regeneratorRuntime.mark.regeneratorRuntime.wrap.e.prev (/root/shadowsocks-hub-api/api.js:11041:13) at t (/root/shadowsocks-hub-api/api.js:10:21) at Object.<anonymous> (/root/shadowsocks-hub-api/api.js:13600:13) at t (/root/shadowsocks-hub-api/api.js:10:21)

Shadowsocks account not working

Thank you for your fast bug fixes ๐Ÿ‘

How can I connect to shadowsocks account ?

I can create and approve account, Then I get the account details include: Method, password, port and server IP address. But by using this information I can not connect to shadowsocks server.

For shadowsocks-restful-api I use the following command to start shadowsocks and integrate it with restful api.

But for this repository I don't know how to start shadowsocks to integrate it with your code.
ss-manager -m aes-256-cfb -u --manager-address /tmp/shadowsocks-manager.sock --fast-open

traffic api 401 Unauthorized

  1. I send request to retrieve traffic history as depicted in document:

curl -ik -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkCI6IjEzZmFlMjNiLTg3MjItNDVlOS1iZDg2LTVkNjkyNTA4OGE3YyIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNTM4MjA0ODUxLCJleHAiOjE1MzgyMDU3NTF9.WdPUM8Ojjh8U45OiwWfB8yvHh-x-k7GJ1E-BojhVjCk" https://localhost:8000/api/traffic/history?id=13fae23b-8722-45e9-bd86-5d6925088a7c
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
Date: Sat, 29 Sep 2018 07:31:45 GMT
Connection: keep-alive
Transfer-Encoding: chunked

both traffic API has the same problem.

  1. in sshub database structure where is the table relates user_id to account_id ? only Get Accounts by User Id? BTW, https://host_name:port/api/account/accounts_by_user_id?id=account_id should be https://host_name:port/api/account/accounts_by_user_id?id=user_id

A route for user (not admin) to get their accounts

A route is required for user to get a list of their accounts.

When I add a new Account I get an ID which is not for the created account. If I want to get the requested account details first I should call account/all by admin token then get my account ID, Then login again with normal user and request to get my account details.

account/all should also work for normal user but just returns accounts that are associated with current user.

UnhandedPromiseRejection

restart "node api.js" after empty request response of "https://host:port/api/account/accounts_by_user_id?id=13fae23b-8722-45e9-bd86-5d6925088a7c

(node:4720) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:4720) UnhandledPromiseRejectionWarning: StatusCodeError: 429 - "Too many requests, please try again later."
at new StatusCodeError (/data/shadowsocks-hub-api/node_modules/request-promise-core/lib/errors.js:32:15)
at Request.plumbing.callback (/data/shadowsocks-hub-api/node_modules/request-promise-core/lib/plumbing.js:104:33)
at Request.RP$callback [as _callback] (/data/shadowsocks-hub-api/node_modules/request-promise-core/lib/plumbing.js:46:31)
at Request.self.callback (/data/shadowsocks-hub-api/node_modules/request/request.js:185:22)
at Request.emit (events.js:182:13)
at Request. (/data/shadowsocks-hub-api/node_modules/request/request.js:1157:10)
at Request.emit (events.js:182:13)
at IncomingMessage. (/data/shadowsocks-hub-api/node_modules/request/request.js:1079:12)
at Object.onceWrapper (events.js:273:13)
at IncomingMessage.emit (events.js:187:15)
at endReadableNT (_stream_readable.js:1081:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
(node:4720) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

edit:

I've found problem caused by sudden database table accounts, ss_account and traffic all emptied for unknown reason.

Extended token expiration option

Hello,

First of all thanks for your excellent work. I would like to request for an option to extended token expiration time or a way to renew token without password. Most of the application has an option like remember me. So, in this case if this application expire in 24 hours then other application will have conflict.

Another request to share source code of api.js in good format. Not in this complied or uglify way so that other users can contribute too.

Thanks

500 Internal Server Error on get account request

{{base_url}}/api/account?id=348dd645-53ee-462a-85f6-936dc3ba4337

I create Server, Node, Product and request an account and approve it, But when I try to get account details api returns 500 error with the following body

{ "error": {} }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.