This is the combined project for riichi.moe and a majsoul api based on https://github.com/SAPikachu/amae-koromo-scripts
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.
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.
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.
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.
/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
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.
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.
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.