Introduction

OJ Lab is an open source organization which aims to 🤔 find out and 🙌 share the solutions to build modernized online judge system.

This book will help you keep in pace with the development of OJ Lab.

Judge API v1

[WIP]

POST /api/v1/judge

{
    src: "",
    package_slug: ""
}

GET /api/v1/status

return spare | busy

Problem API V1

[WIP]

GET /api/v1/problem

[WIP]

POST /api/v1/problem

Should wait package to be put.

PUT /api/v1/problem/:problem-slug/package

Replace/put the problem package in S3 storage, mark the problem as package uploaded.

POST /api/v1/problem/:problem-slug/judge

Check problem is package uploaded, then send judge task to Judger.

Recommanded Setup

Pure '*nix' environment may be better, but some member of OJ Lab use 'Windows + WSL'.

Windows

Recommended to build development environment in WSL with VSCode remote.

Make sure to have a reliable network to avoid download problems.

Golang

Install by brew with brew install go.

You may need to reopen VSCode to refresh go environment. Also, you may need to change go proxy for essential installs go env -w GOPROXY=https://goproxy.cn.

Add GOPATH to PATH

Add export PATH="$PATH:$(go env GOPATH)/bin" to your cli profile, or you might miss some ability installed by go.

Docker

Install docker desktop in windows host.

Model Design Rules

🎯 You should design your model first before providing any kind of service.

Briefly speeking, OJ Lab Service runs with models which mainly shows in JSON and mapped by GORM in database.

Usage of Struct Tag

To reduce the complexity of model usage, we should design as fewer models as possible, and the relationship between models should be well designed.

Fields Control

In different situations, the model will carry different fields. ( For example, the password field should be hidden in most cases, but it should be shown when creating a new user. Also, to avoid security problems, the password field will not saved directly to database, but a hashed_password field will be saved instead. )

Model Associations

The model may have associations with other models. ( For example, the user model may have a role field, which is a list of role model. Also, the role model may have a user field, which is a list of user model. ) Usually, controlling these associations is very complex, but we can rely on GORM to make it easier.

Solutions

Thanks to the ability of struct tag in JSON and GORM, we nearly don't need to write any model conversion codes.

The user struct is defined as below:

type User struct {
	MetaFields
	Account        string  `gorm:"primaryKey" json:"account"`
	Name           string  `json:"name"`
	Password       *string `gorm:"-:all" json:"password,omitempty"`
	HashedPassword string  `gorm:"not null" json:"-"`
	Roles          []*Role `gorm:"many2many:user_roles;" json:"roles,omitempty"`
	Email          string  `gorm:"unique" json:"email,omitempty"`
	Mobile         string  `gorm:"unique" json:"mobile,omitempty"`
}

type Role struct {
	Name  string  `gorm:"primaryKey" json:"name"`
	Users []*User `gorm:"many2many:user_roles" json:"users,omitempty"`
}

You can try refering the following links to learn:

We don't get a very good JSON usage document for now. Simply search for 'go JSON struct tag' on the internet may help.

Model Naming

The struct and field naming will effect the table naming for GORM. Although GORM provides a way to customize table name, but it can be really confuse when struct convert to structs and got different table names.

So it will be better to keep the default naming rules and make table and struct names the same. Then we will get fewer struct being defined, and less conversion which may cause confusion.

Another thing which may cause confusion is when you are trying to define model like Tag, Form etc. These names only describe the model itself, but do not show any relationship between models, also there are some other models with the same name (like there might be many Tag models). So it will be better to add some prefix to the model name, but don't use the parent model name as prefix (like UserTag or ProblemTag). Try to be more specific for the real usage (like AlgorithmTag).

Run Judge V1

⚠️ This function is still under develop now, not promising the usage will be changed in the future.

Usage with Judger

You should run both oj-lab-services and judger in the same network.

By default judger will listen on :8000, if you change it, you should also change judger.host in oj-lab-services's config (an override config is more recommended, see override.example.toml).

Run judger

cargo run --bin judge-server

Run oj-lab-services

make run

Test APIs

Both judger and oj-lab-services provide a postman API collection in project root.

Import them into Postman and test.

Recommanded Setup

Should be able to run in any environment, but we recommand to use WSL with VSCode Remote. *unix environment will be more convenient in installation.

*unix

Install NVM

Visit NVM Installing and Updating for further steps.

Install Node.js by NVM

If it's the first time you use NVM, simply run: nvm install 18.

18 is the currently the latest big LTS version of Node.js, check Node.js Releases for more information.

If you have already installed Node.js by NVM, run nvm use 18 to enable Node.js 18.

You should also run nvm alias default 18 if you want to set Node.js 18 as default.

Mock by MSW

🥰 OJ Lab front support developing without backend by MSW.

Usage

To enable MSW mock server, run npx msw init public/ --save.

Check more information in MSW Getting Started.

Decision to Start Service Development

In the past few months, we all aims to build the most essentail part of an online judge system: The judge machine.

But recently I found it's really difficult to decide which functionality the service will be needed for the judger.

So we decide to restart the development of oj-lab-service, to find out what functionalities we will need in the future.

Milestones

  • Development environment prepare (Including installation guide)
    • Golang
    • Postgres (Dev container + Ways to setup and clean datas)
    • S3 Proxy (Store problem packages)
  • Problem manage service implementation
    • APIs to import problem packages (Store in S3 and DB)