SYZOJ 的界面可好看了,要不搭建一个?

oimaster

2022-06-27 11:23:16

Tech. & Eng.

声明:在洛谷博客中,shellymlmysql 的高亮支持并不好。美元符号因为有 Bug 所以无法显示,于是在本文中所有的美元符号被换成了人民币¥(日元也行,同样符号)。

我支持洛谷。遗憾的是,洛谷在写这篇文章的时候还没有开源,于是,我们无法搭建以洛谷驱动的 OJ。

欢迎大家在评论区中留下文章的勘误等问题。我会挑选一些放在正文中。

以下是正文。

最近,想要在自己的服务器上搭建一个 OJ。在 Github 上搜索一下,各种各样的 OJ 到处都是。

最终,我选择了 SYZOJ。上过 LOJ 的应该都知道,SYZOJ 的 UI 特别好看。如果有时间的话,的确可以自己挑别的 OJ 系统二次开发一个 Semantic UI(SYZOJ 的 UI 框架)的界面,不过直接装 SYZOJ 肯定更让人舒服。

另外,虽然 HUSTOJ 最近推出了 SYZOJ 的主题,但是我认为还是有少些地方(提交记录)不太像。

很高兴看到您们的评论。

LOJ 曾经由 SYZOJ 驱动,现在由 Lyrio(aka. SYZOJ-NG,大概是指 SYZOJ New Generation?) 驱动。对于 LOJ 的老用户,能知道 SYZOJ 的 UI。

实际上,Lyrio 和 SYZOJ 均使用了 Semantic UI 框架,整体感官没多大区别。不过,从组件的位置以及一些新功能来看,区别还是蛮大的(例如题目页面)。

SYZOJ 的部署指南曾经十分详细,但是大概一年前突然又删掉了很多。那么为了方便大家部署,我就在这片博客里写一点指南。

我会尽可能将文章写得易懂,傻瓜也能部署的级别。

部署

您需要准备

这一步实际上对于大部分人来说是最困难的

很高兴看到您的评论 @planet_over_for_ever。

由于 Freenom 不稳定,时间会导致具体方法变化。请您自行搜索。

SYZOJ 的 docker 容器不知道为什么,我的服务器安装出现了错误。我认为自己配置一步一步安装更加保守,且易于开发。

安装依赖

让我们开始吧。以下所有命令都需要使用 root 权限执行。您可以在每条命令前面加上 sudo,也可以使用 sudo su 来切换到 root 账户。

很高兴看到您的评论。@HelloOS

您也可以使用 su 命令切换到 root。这两条命令的区别在于,sudo su 是当前用户暂时切换到 root,需要输入当前用户密码。su 是切换到 root 账号,需要输入 root 密码。

首先,您需要运行下面的命令来安装 Node.js 16、Yarn 1、MariaDB 10.3 与 Redis 5 等工具。这些可以在文档中找到。

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
curl -fsSL https://deb.nodesource.com/setup_current.x | bash -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
apt install -y software-properties-common
add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/10.3/ubuntu focal main'
add-apt-repository ppa:chris-lea/redis-server # Ubuntu 20.04(22.04) 不需要执行这条
apt update
apt install -y git mariadb-server redis-server nodejs yarn p7zip-full clang-format

基于文章发布时已经有了 Ubuntu 22.04,我专门做了一次测试。没有问题。如果有疑问可以私信我或者评论。

注意:安装时会出现让您填写数据库初始密码的界面,请直接按回车跳过。

接下来,下载文件。境内的服务器可能会略微有点慢,不过速度总体还行。

rm -rf /opt/syzoj /etc/systemd/system/syzoj*
mkdir -p /opt/syzoj
cd /opt/syzoj
git clone https://github.com/syzoj/syzoj web
cd web
yarn

配置 OJ

接下来,我们要创建配置。

mkdir -p /opt/syzoj/config
cp /opt/syzoj/web/config-example.json /opt/syzoj/config/web.json
ln -s ../config/web.json /opt/syzoj/web/config.json

接下来,您需要编辑一下配置。首先,让我们打开这个文件。

vim /opt/syzoj/web/config.json

打开后,您需要修改的有:

另外,生成随机字符串的快捷指令是 echo ¥(dd if=/dev/urandom | base64 -w0 | dd bs=1 count=20 2>/dev/null),如果遇到了困难可以请服务器随机生成。

接下来,要创建一些存储临时文件(如 session)和数据(如上传的文件)的文件夹。

mv /opt/syzoj/web/uploads /opt/syzoj/data
ln -s ../data /opt/syzoj/web/uploads
mkdir /opt/syzoj/sessions
ln -s ../sessions /opt/syzoj/web/sessions

创建 syzoj 账户

因为以 root 账户运行网页端特别不安全,所以我们需要创建 syzoj 账户。

adduser --disabled-password --gecos "" syzoj # 以用户名 syzoj 为例

但是此时的网页端还无法读取到我们刚才创建的几个目录,我们要加上权限。

chown -R syzoj:syzoj /opt/syzoj/data /opt/syzoj/sessions /opt/syzoj/config/web.json

创建数据库

从这里开始,SYZOJ 的官方 Wiki 就开始省略了……不过没关系。

首先,让我们进入数据库。请运行 mysql。如果您第一步时使用了密码,那么请自己修改命令连接数据库。

然后创建数据库。

CREATE DATABASE `syzoj` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

接下来,我们要赋予权限。请把 <password> 替换成您在配置文件步骤中填在 db 的随机字符串。

GRANT ALL PRIVILEGES ON `syzoj`.* TO "syzoj"@"localhost" IDENTIFIED BY "<password>";
FLUSH PRIVILEGES;

跑起来!

现在,我们已经完成了配置,让我们启动服务!这一段的官方文档写得及其简陋,也是卡掉了大部分安装 SYZOJ 的玩家。

首先,让我们创建并编辑服务文件。请使用您喜欢的编辑器。我在这里使用 Vim。

vim /etc/systemd/system/syzoj-web.service

如果是 Vim 的话,按下 i 键进入插入模式,然后粘贴这些内容。

[Unit]
Description=SYZOJ web service
After=network.target mysql.service rc-local.service
Requires=mysql.service rc-local.service

[Service]
Type=simple
WorkingDirectory=/opt/syzoj/web
User=syzoj
Group=syzoj
ExecStart=/usr/bin/env NODE_ENV=production /usr/bin/node /opt/syzoj/web/app.js -c /opt/syzoj/config/web.json
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

按下 ESC,输入 :wq,按下 Enter,退出,启动它。

systemctl start syzoj-web.service

记得设为开机自启。

systemctl enable syzoj-web.service

此时,访问网站是没有作用的。我们需要 Nginx 反向代理。

在配置 Nginx 之前,请保证您的域名通过 A 解析到了您的服务器。

Nginx 配置

不要急!先安装 Nginx。

apt install -y nginx

然后创建并编辑配置文件。

vim /etc/nginx/sites-enabled/syzoj

请输入以下内容。

map ¥http_upgrade ¥connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    listen [::]:80;

    server_name oimasterakioi.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For ¥proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header Connection ¥connection_upgrade;
        proxy_pass http://127.0.0.1:5283;
    }
}

请将 oimasterakioi.com 替换成您自己的域名。

接下来,启动服务。

systemctl reload nginx

赋予管理员权限

等待一小会儿,如果之前的步骤全都正确的话,现在应该已经能看到界面了!请立即注册一个账号。

接下来,如果不出意外的话,您应该能看到您的账号 ID 为 1。我们要给它管理员权限。

全站管理员的权限是最高的,为了安全,不能在网页端进行设置。让我们进入数据库。

mysql

然后,赋予其全站管理员权限。

UPDATE `syzoj`.`user` SET `is_admin` = 1 WHERE `id` = 1;

因为 SYZOJ 运行时有缓存(cache),所以修改后的结果并不能直接显现出来。我们需要重启网页端。

systemctl restart syzoj-web.service

接下来,您应该可以进入后台了!现在一切功能都是可以用的,除了——评测机。

准备沙箱

跟大部分的 OJ 一样,我们需要打开一些 Linux 内核中不默认开启的特性。首先,请打开 Ubuntu 的默认引导器——Grub 的配置文件。

vim /etc/default/grub

找到 GRUB_CMDLINE_LINUX_DEFAULT 一行,在原有的字符串后面添加 cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y。例如,修改完后的结果可能是:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y"

然后重启修改配置。部分云服务器可能修改完后 VNC 连接很卡,但是 ssh 功能应该是正常的。

update-grub && reboot

下载沙箱

这一步境内的服务器很慢,因为包含 SYZOJ 网站端所有的语言支持和 Ubuntu 18.04。境外的服务器大约需要 1-2 分钟左右,国内的服务器可能消耗时间较长。

wget -O /sandbox-rootfs.tar.xz https://github.com/syzoj/sandbox-rootfs/releases/download/210521/sandbox-rootfs-210521.tar.xz

如果服务器在国内,又不愿意卡在这一步,不如使用 screen 或者别的方法进行后台下载。

下载完成后,解压文件。

mkdir -p /opt/syzoj/sandbox/rootfs
cd /opt/syzoj/sandbox/
tar xvf /sandbox-rootfs.tar.xz

然后建立沙箱所需文件夹。

mkdir -p /opt/syzoj/sandbox/{bin,tmp1}

安装依赖

这一段也比较困难,官方文档中描述较少。

先安装几个软件。

apt install build-essential libboost-all-dev
apt install rabbitmq-server redis-server

装 Node.js 10。

mkdir -p /opt/syzoj
wget -O /tmp/node-v10.24.1-linux-x64.tar.xz https://nodejs.org/dist/latest-v10.x/node-v10.24.1-linux-x64.tar.xz
tar xf /tmp/node-v10.24.1-linux-x64.tar.xz -C /tmp
mv /tmp/node-v10.24.1-linux-x64 /opt/syzoj/node10

# 如果没有部署过网站端,则安装 yarn,但是我们刚才部署过了,所以可以跳
PATH=/opt/syzoj/node10/bin:¥PATH /opt/syzoj/node10/bin/npm install yarn -g

下载评测端程序

没什么好说的。

cd /opt/syzoj
git clone https://github.com/syzoj/judge-v3
mv judge-v3 judge
cd judge
PATH=/opt/syzoj/node10/bin:$PATH yarn
PATH=/opt/syzoj/node10/bin:$PATH yarn build

配置评测机

cd /opt/syzoj
cp judge/daemon-config-example.json config/daemon.json
cp judge/runner-shared-config-example.json config/runner-shared.json
cp judge/runner-instance-config-example.json config/runner-instance.json

然后这里配置文件需要编辑一下。

vim config/daemon.json

其中,ServerToken 请改成在配置网页端时的 judge_token

注册服务

vim /etc/systemd/system/syzoj-judge-daemon.service

填入以下内容。

[Unit]
Description=SYZOJ judge daemon service
After=network.target rabbitmq-server.service redis-server.service
Requires=rabbitmq-server.service redis-server.service

[Service]
Type=simple
WorkingDirectory=/opt/syzoj/judge
User=syzoj
Group=syzoj
ExecStart=/opt/syzoj/node10/bin/node /opt/syzoj/judge/lib/daemon/index.js -c /opt/syzoj/config/daemon.json

[Install]
WantedBy=multi-user.target

退出,注册另一个服务。

vim /etc/systemd/system/syzoj-judge-runner.service
[Unit]
Description=SYZOJ judge runner service
After=network.target rabbitmq-server.service redis-server.service
Requires=rabbitmq-server.service redis-server.service

[Service]
Type=simple
WorkingDirectory=/opt/syzoj/judge
User=root
Group=root
ExecStart=/opt/syzoj/node10/bin/node /opt/syzoj/judge/lib/runner/index.js -s /opt/syzoj/config/runner-shared.json -i /opt/syzoj/config/runner-instance.json

[Install]
WantedBy=multi-user.target

启动它们,并且设置为开机自启。

systemctl start syzoj-judge-runner.service
systemctl enable syzoj-judge-runner.service
systemctl start syzoj-judge-daemon.service
systemctl enable syzoj-judge-daemon.service

恭喜,完成!

其他

从这里开始,就是一些奇奇怪怪、可有可无的功能了。如果想要您的 OJ 更好,不如看看。注意,下面几项难度逐渐增加。

同时因为这是可选功能,所以叙述将会更加简略。

邮箱注册

当您公开后,没过多久,就总能看到有心智不成熟的人注册一大堆垃圾账号。

解决办法:邮箱验证。

请自行去您的邮箱平台上打开 SMTP 的功能,并且获取 SMTP 密码。对于部分国外邮箱,这些功能都是默认开启的,且可以直接拿邮箱登录密码进行操作。

以网易邮箱为例子:

进入后,点击新增授权密码,进行验证。其他邮箱同理。

完成后,记录下您的密码,进入网站的 后台管理 - 配置文件,找到 email,改成

  "email": {
    "method": "smtp",
    "options": {
        "host": "smtp.163.com",
        "port": 465,
        "username": "[email protected]",
        "password": "xxx",
        "allowUnauthorizedTls": false
    }
  }

然后将上方 register_mail 改为 true 即可!

配置 Logo

历时 3 年,花费 200 万人民币的 Logo 制作完成!现在如何把它摆上去?

简单,让我们上传到服务器,然后进行配置即可。

cd /opt/syzoj/web/static

这里是静态文件目录。将自己的 Logo 上传到这里。为了叙述方便,不妨假设该 Logo 为 xxx.png

接下来同样进入 后台管理 - 配置文件,找到 logo,改成

  "logo": {
    "url": "/xxx.png",
    "width": xxxxx,
    "height": 36
  }

为了显示正常,请把 height 改成 36,同时 width 可以等比例缩小。

今日运势

今日运势的运势类型实在是太少了!我们需要自己添加。

vim /opt/syzoj/web/divine.json

然后用您的 JSON 知识进行添加。如果您不会 JSON,也可以仿照上面的格式。

完成后,重启 SYZOJ web 服务。

二次开发 web

对于大多数用户,界面我觉得不用二次开发了。

实际上,SYZOJ 的界面功能较少。您可以按照如下的方案自行开发。

进入 /opt/syzoj/web/views/,找到对应的文件,进行修改。最后记得重启 web 服务端。

因为谷歌分析代码变化,想要谷歌分析功能的也需要二次开发。

新功能

对于一些新功能,您需要在 views 里提供 ejs,并在 modules 文件夹中编辑相应的路由。请自行学习相关语法。

对于部分内容,您需要修改 models,请自行学习 TypeScript。记得在启动前先进行编译得到 models-built

数据库玩砸了

玩砸技巧:新建题目,将题目 ID 设为 10000,然后删除它。接下来再创建一个新题,就会意外地发现新题的 ID 为 10001。

如果了解数据库,就知道显然是 AUTO_INCREMENT 的问题。

假设您现在有效的题目 ID 为 1 \sim n,且您那些 10000 后面的题目不需要。

步骤:

  1. 使用 delete 命令批量删除,或者是手动在网页端一个一个删
  2. 在数据库中 alter table problem AUTO_INCREMENT=xxx,将 xxx 替换成 n+1

如果 10000 后面的题目有用,只是想要消除空档,那么请自行学习相关语法。

不过,最好的解决方法是,不要删除题目

删除题目?

实际上删除题目也是可以的。您可以看到文末的 BetaOJ,提供了一种效率低,但是可重复利用题号的方案。

权限?

BetaOJ。

想要在主页直接显示公告,而不是讨论?

BetaOJ 小黑板功能。

跳过评测?(0 分处理)

models 文件夹中管评测的重写,让它把一个提交记录设为 0 分并且 Skipped 状态(或者别的,您愿意就行)。

一个简单的方法是,把「重测」相关功能 copy 一下,然后在重置提交记录信息后删除「交给评测机评测」相关代码。

如果还有问题,例如如果分布式评测、配置静态资源 CDN 等,请上 官方 Wiki。相信这样的人应该可以读懂了。

我自己开发了一个

BetaOJ 是我稍做了一些改动,仓促写好的。可惜的是,因为有些功能需要更改评测机代码,所以并未公开。

感谢大家的捧场。

特别感谢 & 拜一拜 piggy123 同学。感谢您对我的 OJ 作出的贡献!

感谢前排应援,@MarchKid_Joe @AIMEE11 @Emptyhanded!

为什么您会「泪目」@NotaKoala?应该是我因为您来观看而感动流泪才对。

我看到您的评论。@Milmon

我也算是 Hydro 的老用户了(?)我支持 Hydro 的开发。遗憾的是,在写这篇文章时,Hydro 的功能并不是很丰富。对于数据配置等功能,完全没有任何图形化的帮助。

我希望更多的人能将讨论中心放在 SYZOJ 的搭建上,而不是衡量、比较多个 OJ —— 这不是这里该干的事情。

如果您想要部署 Hydro,可以自行查阅他们的文档。