Skip to content

vg-mjg/majsoul-api

Repository files navigation

This is the combined project for riichi.moe and a majsoul api based on https://github.com/SAPikachu/amae-koromo-scripts

Secrets

The project requires quite a few secrets to run. Access tokens for the majsoul api, database credentials and riichi admin password are the important ones. There is a file /api/src/secrets.json.examples that is a template for the secrets.json that has to be colocated to the scripts to be picked up. These secrets will be discussed in detail later also.

Majsoul API

The foundation is the /api/src/majsoul folder. This is designed to be a standalone node package but it hasn't been moved out yet because it's more convenient to develop like this. This module only knows about majsoul concepts such as "Contests" and "GameResults". Anything you can see or interact with in the majsoul client. It connects to majsoul servers via a websocket the same way that the majsoul client does and communicates using protocol buffers that are published by yostar for it's own client. In order to connect this way the api requires your majsoul accessToken. This is a token generated by yostar when you complete a login. It is your yostar login. You need to get your uid and accessToken by snooping it from the real majsoul client after a successful login. There will be a request to passport.mahjongsoul.com, you can take the accessToken from one of those responses, as well as the uid. The API itself is just a library, another component must consume the library to get anything to actually happen.

Store

The /api/src/store folder is a collection of helpers and typings for the database. The riichi backend uses a mongodb store. In order to save time and prevent errors the types in the database are defined in this store folder and used throught the application. These objects are concerned with the riichi domain. It has ideas like "Teams" and "Sessions" which Majsoul does not have but are necessary for the league. These types are specifically the types that appear in the database. Types that are server and accepted by the rest api or client are defined elsewhere by extending these types. The database needs a username and password to access it. They are also provided for in the secrets file read by the backend application under the 'mongo' key. The store is also a library and needs to be consumed by something.

Connector

The connector is the executable that connects to the majsoul api, loads information about tournaments (known as Contests), players, and games, and dumps all that information into the database in a correct format. The data is relatively normalised. The connector also loads and dumps some data into the google docs spreadsheet. This is how we load the team information and session information. In order to connect you must get a google docs api token from google. Follow the instructions here. You only need to do step 1 and get that token information into secrets.json. The second half of the connection is done interactively when you run the connector for the first time. The connector then sits and waits for updates from majsoul about games and keeps propogating those updates. It will exit if it loses connection to majsoul.

In /api:

yarn
yarn run tsc
cp secrets.json.example dist/secrets.json
yarn connect

Remember you must update the secrets in app/dist/secrets.json

You will also need to have a mongo instance running on the default mongo port on localhost with the provided mongo creds. If you're using docker you can start a development mongo stack by running

docker stack deploy -c mongo.yml mongo

But you will need docker swarm started.

Rest API

/api/src/rest The rest api is a node express rest api that connects to the database and serves out some resources over rest. This is how the frontend actually gets it's information. Types used by the rest api are defined here also.

There are admin methods and a login api that allow you to edit some resources from the frontend, such as upload team images and change session times. In order for this to work you must provide an ssl certificate for signing the jwt tokens. The ssl certs must be in pem format and placed in the /api/dist/ folder. The private key must be named "riichi.key.pem" and the public key must be named "riichi.crt.pem". The "riichi" field in the secrets.json is used to create the admin user, so use those creads or change them to your liking. They are uploaded into the database when the rest api starts.

The rest API also needs mongobd as explained in the Connector section because the rest api is serving data from the mongodb store.

In /api:

yarn
yarn run tsc
cp secrets.json.example dist/secrets.json
# create and copy certs!
yarn rest

Frontend

The frontend is written in React Redux with Bootstrap. It's the simplest part to run, once the rest is going, but because the frontend shares types with the backed we must link the who using node modules link.

After building the backend, In /api:

yarn link

In /frontend:

yarn
yarn link majsoul-api
yarn run webpack-dev-server

And it should be good to go.

Docker and Production

In production riichi.moe runs in docker. To this end there are two docker files that build the backend and the frontend respectively. The backend Dockerfile is in /api, the frontend Dockerfile is in / because it needs to copy the backend for type information.

There is a docker compose file used to build and deploy the entire stack. It will start a mongo instance, an nginx instance which will serve the compiled frontend bundle, and an instance for the connector and rest api. It relies on 4 secrets, most of which are familiar except the mongo_creds file which must contain only the password of the mongodb root user and is used by the mongodb image to initialise the database. The username will be root, and you must reflect this in your secrets.json.

docker compose build
docker stack deploy -c docker-compose.yaml majsoul

It must be called majsoul. The name is part of the dns names of the docker containers in the swarm and it is used to find the mongodb instance and the rest apis.

Environments

In docker production NODE_ENV === "production" and the code relies on this. Also there is an env file you can use for staging called 'majsoul.env' which is loaded by docker if it is present. Currently it can be set to

MAJSOUL_ENV=staging

To disable writing to the spreadsheet in staging.