feat: another big update

1. add consumer
2. add createVersion
3. add upload
4. fix runner
5. add rejudge

Co-authored-by: cxy004 <cxy004@qq.com>
Co-authored-by: wzt <w.zhongtao@qq.com>
This commit is contained in:
Paul Pan 2022-10-23 17:29:35 +08:00
parent d42ee0ce54
commit 26a81652b3
52 changed files with 729 additions and 184 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
### Project ### Project
/tmp
/server /server
/runner /runner
my.secrets my.secrets

View File

@ -21,6 +21,13 @@ Database:
MaxIdleConns: 60 MaxIdleConns: 60
ConnMaxLifetime: 60 ConnMaxLifetime: 60
Storage:
Endpoint: '127.0.0.1:9000'
UseSSL: false
AccessKey: 'EHd5Zj56QrTivhFI'
SecretKey: 'FUHy4RW1mn0Kbr5pibDZ6R2F9116FZKY'
Bucket: 'woj'
Metrics: Metrics:
Namespace: 'OJ' Namespace: 'OJ'
Subsystem: 'server' Subsystem: 'server'

13
go.mod
View File

@ -11,13 +11,14 @@ require (
github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang-jwt/jwt/v4 v4.4.2
github.com/hibiken/asynq v0.23.0 github.com/hibiken/asynq v0.23.0
github.com/jackc/pgtype v1.11.0 github.com/jackc/pgtype v1.11.0
github.com/minio/minio-go/v7 v7.0.42
github.com/prometheus/client_golang v1.13.0 github.com/prometheus/client_golang v1.13.0
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
github.com/swaggo/gin-swagger v1.5.3 github.com/swaggo/gin-swagger v1.5.3
github.com/swaggo/swag v1.8.5 github.com/swaggo/swag v1.8.5
github.com/urfave/cli/v2 v2.14.1 github.com/urfave/cli/v2 v2.14.1
go.uber.org/zap v1.23.0 go.uber.org/zap v1.23.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/postgres v1.3.9 gorm.io/driver/postgres v1.3.9
@ -33,6 +34,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect
@ -55,10 +57,14 @@ require (
github.com/jinzhu/now v1.1.4 // indirect github.com/jinzhu/now v1.1.4 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/mailru/easyjson v0.7.6 // indirect github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect
@ -66,16 +72,19 @@ require (
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.7.0 // indirect go.uber.org/multierr v1.7.0 // indirect
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

28
go.sum
View File

@ -76,6 +76,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -271,6 +273,12 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -305,6 +313,12 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.42 h1:fP56plNR/Tkw/+Xczw9NL5TGxe5gJDvgd8LidNR3BEI=
github.com/minio/minio-go/v7 v7.0.42/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -371,6 +385,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -385,6 +401,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@ -467,8 +485,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -541,8 +560,9 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -616,6 +636,8 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@ -781,6 +803,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,36 @@
package consumer
import (
"context"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/service/problem"
"github.com/WHUPRJ/woj-server/internal/service/status"
"github.com/WHUPRJ/woj-server/internal/service/task"
"github.com/hibiken/asynq"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
ProblemUpdate(_ context.Context, t *asynq.Task) error
SubmitUpdate(_ context.Context, t *asynq.Task) error
}
type handler struct {
log *zap.Logger
problemService problem.Service
statusService status.Service
taskService task.Service
}
func NewConsumer(g *global.Global) Handler {
hnd := &handler{
log: g.Log,
problemService: problem.NewService(g),
statusService: status.NewService(g),
taskService: task.NewService(g),
}
return hnd
}

View File

@ -0,0 +1,40 @@
package consumer
import (
"context"
"encoding/json"
"fmt"
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/hibiken/asynq"
"github.com/jackc/pgtype"
"go.uber.org/zap"
)
func (h *handler) ProblemUpdate(_ context.Context, t *asynq.Task) error {
p := new(model.ProblemUpdatePayload)
if err := json.Unmarshal(t.Payload(), &p); err != nil {
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
}
if p.Status != e.Success {
h.log.Warn("RunnerError", zap.Any("payload", p))
return nil
}
status := h.problemService.UpdateVersion(
p.ProblemVersionID,
map[string]interface{}{
"Context": pgtype.JSON{
Bytes: []byte(p.Context),
Status: pgtype.Present,
},
"IsEnabled": true,
},
)
if status != e.Success {
return fmt.Errorf(status.String())
}
return nil
}

View File

@ -0,0 +1,37 @@
package consumer
import (
"context"
"encoding/json"
"fmt"
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/WHUPRJ/woj-server/internal/service/status"
"github.com/hibiken/asynq"
"go.uber.org/zap"
)
func (h *handler) SubmitUpdate(_ context.Context, t *asynq.Task) error {
p := new(model.SubmitUpdatePayload)
if err := json.Unmarshal(t.Payload(), &p); err != nil {
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
}
if p.Status != e.Success {
h.log.Warn("RunnerError", zap.Any("payload", p))
return nil
}
createData := &status.CreateData{
SubmissionID: p.SubmissionID,
ProblemVersionID: p.ProblemVersionID,
Context: p.Context,
Point: p.Point,
}
_, eStatus := h.statusService.Create(createData)
if eStatus != e.Success {
return fmt.Errorf(eStatus.String())
}
return nil
}

View File

@ -0,0 +1,67 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/WHUPRJ/woj-server/internal/service/problem"
"github.com/gin-gonic/gin"
)
type createVersionRequest struct {
ProblemID uint `form:"pid" binding:"required"`
StorageKey string `form:"storage_key" binding:"required"`
}
// CreateVersion
// @Summary create a problem version
// @Description create a problem version
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int true "problem id"
// @Param storage_key formData string true "storage key"
// @Response 200 {object} e.Response ""
// @Security Authentication
// @Router /v1/problem/create_version [post]
func (h *handler) CreateVersion(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
e.Pong(c, e.UserUnauthenticated, nil)
return
}
// uid := claim.(*global.Claim).UID
role := claim.(*global.Claim).Role
req := new(createVersionRequest)
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
// guest can not submit
if role < model.RoleAdmin {
e.Pong(c, e.UserUnauthorized, nil)
return
}
// TODO: check pid exist
createVersionData := &problem.CreateVersionData{
ProblemID: req.ProblemID,
StorageKey: req.StorageKey,
}
pv, status := h.problemService.CreateVersion(createVersionData)
if status != e.Success {
e.Pong(c, status, nil)
return
}
payload := &model.ProblemBuildPayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
}
_, status = h.taskService.ProblemBuild(payload)
e.Pong(c, status, nil)
}

View File

@ -37,9 +37,11 @@ func (h *handler) Details(c *gin.Context) {
} }
pv, status := h.problemService.QueryLatestVersion(req.Pid) pv, status := h.problemService.QueryLatestVersion(req.Pid)
e.Pong(c, status, gin.H{ if status != e.Success {
e.Pong(c, status, nil)
}
e.Pong(c, e.Success, gin.H{
"problem": p, "problem": p,
"context": pv.Context, "context": pv.Context.Get(),
}) })
return
} }

View File

@ -3,6 +3,8 @@ package problem
import ( import (
"github.com/WHUPRJ/woj-server/internal/global" "github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/service/problem" "github.com/WHUPRJ/woj-server/internal/service/problem"
"github.com/WHUPRJ/woj-server/internal/service/storage"
"github.com/WHUPRJ/woj-server/internal/service/task"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -13,12 +15,15 @@ type Handler interface {
Details(c *gin.Context) Details(c *gin.Context)
Search(c *gin.Context) Search(c *gin.Context)
Update(c *gin.Context) Update(c *gin.Context)
Upload(c *gin.Context)
} }
type handler struct { type handler struct {
log *zap.Logger log *zap.Logger
jwtService global.JwtService jwtService global.JwtService
problemService problem.Service problemService problem.Service
taskService task.Service
storageService storage.Service
} }
func RouteRegister(g *global.Global, group *gin.RouterGroup) { func RouteRegister(g *global.Global, group *gin.RouterGroup) {
@ -26,9 +31,13 @@ func RouteRegister(g *global.Global, group *gin.RouterGroup) {
log: g.Log, log: g.Log,
jwtService: g.Jwt, jwtService: g.Jwt,
problemService: problem.NewService(g), problemService: problem.NewService(g),
taskService: task.NewService(g),
storageService: storage.NewService(g),
} }
group.POST("/details", app.jwtService.Handler(false), app.Details)
group.POST("/search", app.Search) group.POST("/search", app.Search)
group.POST("/details", app.jwtService.Handler(false), app.Details)
group.POST("/update", app.jwtService.Handler(true), app.Update) group.POST("/update", app.jwtService.Handler(true), app.Update)
group.POST("/upload", app.jwtService.Handler(true), app.Upload)
group.POST("/create_version", app.jwtService.Handler(true), app.CreateVersion)
} }

View File

@ -28,12 +28,12 @@ func (h *handler) Search(c *gin.Context) {
// TODO: pagination // TODO: pagination
if req.Search == "" { if req.Search == "" {
// TODO: query without LIKE // TODO: query without LIKE
problem, status := h.problemService.QueryFuzz(req.Search, true, true) problems, status := h.problemService.QueryFuzz(req.Search, true, true)
e.Pong(c, status, problem) e.Pong(c, status, problems)
return return
} else { } else {
problem, status := h.problemService.QueryFuzz(req.Search, true, true) problems, status := h.problemService.QueryFuzz(req.Search, true, true)
e.Pong(c, status, problem) e.Pong(c, status, problems)
return return
} }
} }

View File

@ -0,0 +1,44 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/WHUPRJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
"time"
)
// Upload
// @Summary get upload url
// @Description get upload url
// @Produce json
// @Response 200 {object} e.Response "upload url and key"
// @Security Authentication
// @Router /v1/problem/upload [post]
func (h *handler) Upload(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
e.Pong(c, e.UserUnauthenticated, nil)
return
}
role := claim.(*global.Claim).Role
if role < model.RoleAdmin {
e.Pong(c, e.UserUnauthorized, nil)
return
}
key := utils.RandomString(16)
url, status := h.storageService.Upload(key, time.Second*60*60)
if status != e.Success {
e.Pong(c, status, nil)
return
}
e.Pong(c, e.Success, gin.H{
"key": key,
"url": url,
})
}

View File

@ -4,14 +4,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model" "github.com/WHUPRJ/woj-server/internal/model"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"go.uber.org/zap" "go.uber.org/zap"
"time"
) )
func (h *handler) Build(_ context.Context, t *asynq.Task) error { func (h *handler) Build(_ context.Context, t *asynq.Task) error {
// TODO: configure timeout with context
var p model.ProblemBuildPayload var p model.ProblemBuildPayload
if err := json.Unmarshal(t.Payload(), &p); err != nil { if err := json.Unmarshal(t.Payload(), &p); err != nil {
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry) return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
@ -19,16 +19,32 @@ func (h *handler) Build(_ context.Context, t *asynq.Task) error {
h.log.Info("build", zap.Any("payload", p)) h.log.Info("build", zap.Any("payload", p))
config, status := h.runnerService.NewProblem(p.ProblemVersionID, p.ProblemFile) status, ctx := func() (e.Status, string) {
url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
if status != e.Success {
return e.InternalError, "{}"
}
for i := range config.Languages { config, status := h.runnerService.NewProblem(p.ProblemVersionID, url, true)
config.Languages[i].Type = "" if status != e.Success {
config.Languages[i].Script = "" return e.InternalError, "{}"
config.Languages[i].Cmp = "" }
}
b, _ := json.Marshal(config) for i := range config.Languages {
h.taskService.ProblemUpdate(status, p.ProblemVersionID, string(b)) config.Languages[i].Type = ""
config.Languages[i].Script = ""
config.Languages[i].Cmp = ""
}
b, _ := json.Marshal(config)
return e.Success, string(b)
}()
h.taskService.ProblemUpdate(&model.ProblemUpdatePayload{
Status: status,
ProblemVersionID: p.ProblemVersionID,
Context: ctx,
})
return nil return nil
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/WHUPRJ/woj-server/internal/e" "github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global" "github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/service/runner" "github.com/WHUPRJ/woj-server/internal/service/runner"
"github.com/WHUPRJ/woj-server/internal/service/storage"
"github.com/WHUPRJ/woj-server/internal/service/task" "github.com/WHUPRJ/woj-server/internal/service/task"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"go.uber.org/zap" "go.uber.org/zap"
@ -19,16 +20,18 @@ type Handler interface {
} }
type handler struct { type handler struct {
log *zap.Logger log *zap.Logger
runnerService runner.Service runnerService runner.Service
taskService task.Service taskService task.Service
storageService storage.Service
} }
func NewRunner(g *global.Global) (Handler, error) { func NewRunner(g *global.Global) (Handler, error) {
hnd := &handler{ hnd := &handler{
log: g.Log, log: g.Log,
runnerService: runner.NewService(g), runnerService: runner.NewService(g),
taskService: task.NewService(g), taskService: task.NewService(g),
storageService: storage.NewService(g),
} }
status := hnd.runnerService.EnsureDeps(false) status := hnd.runnerService.EnsureDeps(false)

View File

@ -11,6 +11,7 @@ import (
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"go.uber.org/zap" "go.uber.org/zap"
"path/filepath" "path/filepath"
"time"
) )
func (h *handler) Judge(_ context.Context, t *asynq.Task) error { func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
@ -22,52 +23,55 @@ func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
user := utils.RandomString(16) user := utils.RandomString(16)
h.log.Info("judge", zap.Any("payload", p), zap.String("user", user)) h.log.Info("judge", zap.Any("payload", p), zap.String("user", user))
// common status, point, ctx := func() (e.Status, int32, runner.JudgeStatus) {
systemError := runner.JudgeStatus{Message: "System Error"} systemError := runner.JudgeStatus{Message: "System Error"}
// write code // 1. write user code
userCode := filepath.Join(runner.UserDir, user, fmt.Sprintf("%s.%s", user, p.Submission.Language)) userCode := filepath.Join(runner.UserDir, user, fmt.Sprintf("%s.%s", user, p.Submission.Language))
if !utils.FileTouch(userCode) { if !utils.FileTouch(userCode) {
h.log.Info("Touch file failed", zap.String("userCode", userCode)) return e.InternalError, 0, systemError
h.taskService.SubmitUpdate(e.InternalError, p.ProblemVersionId, 0, systemError) }
return nil err := utils.FileWrite(userCode, []byte(p.Submission.Code))
} if err != nil {
err := utils.FileWrite(userCode, []byte(p.Submission.Code)) return e.InternalError, 0, systemError
if err != nil {
h.log.Info("Write file failed", zap.String("code", p.Submission.Code))
h.taskService.SubmitUpdate(e.InternalError, p.ProblemVersionId, 0, systemError)
return nil
}
// compile
result, status := h.runnerService.Compile(p.ProblemVersionId, user, p.Submission.Language)
if status == e.RunnerProblemNotExist {
_, status := h.runnerService.NewProblem(p.ProblemVersionId, p.StorageKey)
if status != e.Success {
h.log.Warn("download problem failed",
zap.Any("status", status),
zap.Uint("pvid", p.ProblemVersionId),
zap.String("storageKey", p.StorageKey))
h.taskService.SubmitUpdate(status, p.ProblemVersionId, 0, systemError)
return nil
} }
} else if status != e.Success {
h.taskService.SubmitUpdate(status, p.Submission.ID, 0, result)
return nil
}
// config // 2. check problem
config, err := h.runnerService.ParseConfig(p.ProblemVersionId, true) if !h.runnerService.ProblemExists(p.ProblemVersionID) {
if err != nil { url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
h.log.Info("parse config failed", zap.Error(err), zap.Uint("pvid", p.ProblemVersionId)) if status != e.Success {
h.taskService.SubmitUpdate(e.InternalError, p.ProblemVersionId, 0, systemError) return e.InternalError, 0, systemError
return nil }
}
// run _, status = h.runnerService.NewProblem(p.ProblemVersionID, url, false)
var points int32 if status != e.Success {
result, points, status = h.runnerService.RunAndJudge(p.ProblemVersionId, user, p.Submission.Language, &config) return e.InternalError, 0, systemError
h.taskService.SubmitUpdate(status, p.Submission.ID, points, result) }
}
// 3. compile
compileResult, status := h.runnerService.Compile(p.ProblemVersionID, user, p.Submission.Language)
if status != e.Success {
return e.Success, 0, compileResult
}
// 4. config
config, err := h.runnerService.ParseConfig(p.ProblemVersionID, true)
if err != nil {
return e.InternalError, 0, systemError
}
// 5. run and judge
result, point, status := h.runnerService.RunAndJudge(p.ProblemVersionID, user, p.Submission.Language, &config)
return utils.If(status != e.Success, e.InternalError, e.Success).(e.Status), point, result
}()
h.taskService.SubmitUpdate(&model.SubmitUpdatePayload{
Status: status,
SubmissionID: p.Submission.ID,
ProblemVersionID: p.ProblemVersionID,
Point: point,
}, ctx)
return nil return nil
} }

View File

@ -6,9 +6,17 @@ import (
) )
type queryRequest struct { type queryRequest struct {
SubmissionID uint `form:"sid"` SubmissionID uint `form:"sid" binding:"required"`
} }
// Query
// @Summary query submissions by via submission id
// @Description query submissions by via submission id
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param sid formData uint true "submission id"
// @Response 200 {object} e.Response "model.status"
// @Router /v1/status/query [post]
func (h *handler) Query(c *gin.Context) { func (h *handler) Query(c *gin.Context) {
req := new(queryRequest) req := new(queryRequest)
@ -20,5 +28,4 @@ func (h *handler) Query(c *gin.Context) {
status, eStatus := h.statusService.Query(req.SubmissionID, true) status, eStatus := h.statusService.Query(req.SubmissionID, true)
e.Pong(c, eStatus, status) e.Pong(c, eStatus, status)
return
} }

View File

@ -8,11 +8,21 @@ import (
) )
type queryByVersionRequest struct { type queryByVersionRequest struct {
ProblemVersionID uint `form:"pvid"` ProblemVersionID uint `form:"pvid" binding:"required"`
Offset int `form:"offset"` Offset int `form:"offset"`
Limit int `form:"limit"` Limit int `form:"limit" binding:"required"`
} }
// QueryByProblemVersion
// @Summary query submissions by problem version (admin only)
// @Description query submissions by problem version (admin only)
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pvid formData uint true "problem version id"
// @Param offset formData int true "start position"
// @Param limit formData int true "limit number of records"
// @Response 200 {object} e.Response "[]*model.status"
// @Router /v1/status/query/problem_version [post]
func (h *handler) QueryByProblemVersion(c *gin.Context) { func (h *handler) QueryByProblemVersion(c *gin.Context) {
claim, exist := c.Get("claim") claim, exist := c.Get("claim")
@ -26,7 +36,7 @@ func (h *handler) QueryByProblemVersion(c *gin.Context) {
req := new(queryByVersionRequest) req := new(queryByVersionRequest)
if err := c.ShouldBind(req); err != nil { if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, nil) e.Pong(c, e.InvalidParameter, err.Error())
return return
} }
@ -38,5 +48,4 @@ func (h *handler) QueryByProblemVersion(c *gin.Context) {
statuses, eStatus := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit) statuses, eStatus := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit)
e.Pong(c, eStatus, statuses) e.Pong(c, eStatus, statuses)
return
} }

View File

@ -3,6 +3,7 @@ package submission
import ( import (
"github.com/WHUPRJ/woj-server/internal/e" "github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global" "github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/WHUPRJ/woj-server/internal/service/submission" "github.com/WHUPRJ/woj-server/internal/service/submission"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -10,7 +11,7 @@ import (
type createRequest struct { type createRequest struct {
Pid uint `form:"pid" binding:"required"` Pid uint `form:"pid" binding:"required"`
Language string `form:"language" binding:"required"` Language string `form:"language" binding:"required"`
Code string `form:"statement" binding:"required"` Code string `form:"code" binding:"required"`
} }
// Create // Create
@ -33,6 +34,7 @@ func (h *handler) Create(c *gin.Context) {
uid := claim.(*global.Claim).UID uid := claim.(*global.Claim).UID
role := claim.(*global.Claim).Role
req := new(createRequest) req := new(createRequest)
if err := c.ShouldBind(req); err != nil { if err := c.ShouldBind(req); err != nil {
@ -40,6 +42,12 @@ func (h *handler) Create(c *gin.Context) {
return return
} }
// guest can not submit
if role < model.RoleGeneral {
e.Pong(c, e.UserUnauthorized, nil)
return
}
createData := &submission.CreateData{ createData := &submission.CreateData{
ProblemID: req.Pid, ProblemID: req.Pid,
UserID: uid, UserID: uid,
@ -58,7 +66,12 @@ func (h *handler) Create(c *gin.Context) {
return return
} }
_, status = h.taskService.SubmitJudge(pv.ID, pv.StorageKey, *s) payload := &model.SubmitJudgePayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
Submission: *s,
}
_, status = h.taskService.SubmitJudge(payload)
e.Pong(c, status, nil) e.Pong(c, status, nil)
return
} }

View File

@ -15,6 +15,7 @@ var _ Handler = (*handler)(nil)
type Handler interface { type Handler interface {
Create(c *gin.Context) Create(c *gin.Context)
Query(c *gin.Context) Query(c *gin.Context)
Rejudge(c *gin.Context)
} }
type handler struct { type handler struct {
@ -38,4 +39,5 @@ func RouteRegister(g *global.Global, group *gin.RouterGroup) {
group.POST("/create", app.jwtService.Handler(true), app.Create) group.POST("/create", app.jwtService.Handler(true), app.Create)
group.POST("/query", app.Query) group.POST("/query", app.Query)
group.POST("/rejudge", app.jwtService.Handler(true), app.Rejudge)
} }

View File

@ -10,7 +10,7 @@ type queryRequest struct {
Pid uint `form:"pid"` Pid uint `form:"pid"`
Uid uint `form:"uid"` Uid uint `form:"uid"`
Offset int `form:"offset"` Offset int `form:"offset"`
Limit int `form:"limit"` Limit int `form:"limit" binding:"required"`
} }
type queryResponse struct { type queryResponse struct {
@ -23,10 +23,10 @@ type queryResponse struct {
// @Description Query submissions // @Description Query submissions
// @Accept application/x-www-form-urlencoded // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param pid formData uint false "problem id" // @Param pid formData uint true "problem id"
// @Param uid formData uint false "user id" // @Param uid formData uint true "user id"
// @Param offset formData int false "start position" // @Param offset formData int true "start position"
// @Param limit formData int false "limit number of records" // @Param limit formData int true "limit number of records"
// @Response 200 {object} e.Response "queryResponse" // @Response 200 {object} e.Response "queryResponse"
// @Router /v1/submission/query [post] // @Router /v1/submission/query [post]
@ -67,7 +67,5 @@ func (h *handler) Query(c *gin.Context) {
response = append(response, newResponse) response = append(response, newResponse)
} }
e.Pong(c, status, submissions) e.Pong(c, status, response)
return
} }

View File

@ -0,0 +1,63 @@
package submission
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/model"
"github.com/gin-gonic/gin"
)
type rejudgeRequest struct {
Sid uint `form:"sid" binding:"required"`
}
// Rejudge
// @Summary rejudge a submission
// @Description rejudge a submission
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param sid formData int true "submission id"
// @Response 200 {object} e.Response ""
// @Security Authentication
// @Router /v1/submission/rejudge [post]
func (h *handler) Rejudge(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
e.Pong(c, e.UserUnauthenticated, nil)
return
}
role := claim.(*global.Claim).Role
req := new(rejudgeRequest)
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
// only admin can rejudge
if role < model.RoleAdmin {
e.Pong(c, e.UserUnauthorized, nil)
return
}
s, status := h.submissionService.QueryBySid(req.Sid, false)
if status != e.Success {
e.Pong(c, status, nil)
return
}
pv, status := h.problemService.QueryLatestVersion(s.ProblemID)
if status != e.Success {
e.Pong(c, status, nil)
return
}
_, status = h.taskService.SubmitJudge(&model.SubmitJudgePayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
Submission: *s,
})
e.Pong(c, status, nil)
}

View File

@ -55,5 +55,4 @@ func (h *handler) Create(c *gin.Context) {
} }
token, status := h.jwtService.SignClaim(claim) token, status := h.jwtService.SignClaim(claim)
e.Pong(c, status, token) e.Pong(c, status, token)
return
} }

View File

@ -23,5 +23,4 @@ func (h *handler) Logout(c *gin.Context) {
_, status := h.userService.IncrVersion(claim.(*global.Claim).UID) _, status := h.userService.IncrVersion(claim.(*global.Claim).UID)
e.Pong(c, status, nil) e.Pong(c, status, nil)
return
} }

View File

@ -47,6 +47,8 @@ func (h *handler) Profile(c *gin.Context) {
} }
user, status := h.userService.Profile(req.UID) user, status := h.userService.Profile(req.UID)
// TODO: >= admin can see is_enable
e.Pong(c, status, user) e.Pong(c, status, user)
return
} }

View File

@ -3,6 +3,7 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/WHUPRJ/woj-server/internal/api/consumer"
"github.com/WHUPRJ/woj-server/internal/global" "github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/model" "github.com/WHUPRJ/woj-server/internal/model"
"github.com/WHUPRJ/woj-server/internal/repo/postgresql" "github.com/WHUPRJ/woj-server/internal/repo/postgresql"
@ -52,9 +53,11 @@ func RunServer(g *global.Global) error {
// Create Queue // Create Queue
queueMux := asynq.NewServeMux() queueMux := asynq.NewServeMux()
// TODO: fill {
queueMux.HandleFunc(model.TypeProblemUpdate, func(ctx context.Context, t *asynq.Task) error { return nil }) handler := consumer.NewConsumer(g)
queueMux.HandleFunc(model.TypeSubmitUpdate, func(ctx context.Context, t *asynq.Task) error { return nil }) queueMux.HandleFunc(model.TypeProblemUpdate, handler.ProblemUpdate)
queueMux.HandleFunc(model.TypeSubmitUpdate, handler.SubmitUpdate)
}
queueSrv := asynq.NewServer( queueSrv := asynq.NewServer(
asynq.RedisClientOpt{ asynq.RedisClientOpt{
Addr: g.Conf.Redis.Address, Addr: g.Conf.Redis.Address,

View File

@ -37,6 +37,7 @@ const (
ProblemNotAvailable ProblemNotAvailable
ProblemVersionNotFound ProblemVersionNotFound
ProblemVersionNotAvailable ProblemVersionNotAvailable
SubmissionNotFound
StatusNotFound StatusNotFound
) )
@ -58,6 +59,11 @@ const (
RunnerJudgeFailed RunnerJudgeFailed
) )
const (
StorageUploadFailed Status = 800 + iota
StorageGetFailed
)
var msgText = map[Status]string{ var msgText = map[Status]string{
Success: "Success", Success: "Success",
Unknown: "Unknown error", Unknown: "Unknown error",
@ -88,6 +94,8 @@ var msgText = map[Status]string{
ProblemVersionNotFound: "Problem Version Not Found", ProblemVersionNotFound: "Problem Version Not Found",
ProblemVersionNotAvailable: "Problem Version Not Available", ProblemVersionNotAvailable: "Problem Version Not Available",
SubmissionNotFound: "Submission Not Found",
StatusNotFound: "Status Not Found", StatusNotFound: "Status Not Found",
TaskEnqueueFailed: "Task Enqueue Failed", TaskEnqueueFailed: "Task Enqueue Failed",
@ -103,4 +111,7 @@ var msgText = map[Status]string{
RunnerUserCompileFailed: "Runner User Compile Failed", RunnerUserCompileFailed: "Runner User Compile Failed",
RunnerRunFailed: "Runner Run Failed", RunnerRunFailed: "Runner Run Failed",
RunnerJudgeFailed: "Runner Judge Failed", RunnerJudgeFailed: "Runner Judge Failed",
StorageUploadFailed: "Storage Upload Failed",
StorageGetFailed: "Storage Get Failed",
} }

View File

@ -24,3 +24,12 @@ func Pong(c *gin.Context, status Status, body interface{}) {
c.Set("err", status) c.Set("err", status)
c.JSON(http.StatusOK, Wrap(status, body)) c.JSON(http.StatusOK, Wrap(status, body))
} }
type Endpoint func(*gin.Context) (Status, interface{})
func PongWrapper(handler Endpoint) func(*gin.Context) {
return func(c *gin.Context) {
status, body := handler(c)
Pong(c, status, body)
}
}

View File

@ -26,6 +26,14 @@ type ConfigDatabase struct {
ConnMaxLifetime int `yaml:"ConnMaxLifetime"` ConnMaxLifetime int `yaml:"ConnMaxLifetime"`
} }
type ConfigStorage struct {
Endpoint string `yaml:"Endpoint"`
UseSSL bool `yaml:"UseSSL"`
AccessKey string `yaml:"AccessKey"`
SecretKey string `yaml:"SecretKey"`
Bucket string `yaml:"Bucket"`
}
type ConfigMetrics struct { type ConfigMetrics struct {
Namespace string `yaml:"Namespace"` Namespace string `yaml:"Namespace"`
Subsystem string `yaml:"Subsystem"` Subsystem string `yaml:"Subsystem"`
@ -35,6 +43,7 @@ type Config struct {
WebServer ConfigWebServer `yaml:"WebServer"` WebServer ConfigWebServer `yaml:"WebServer"`
Redis ConfigRedis `yaml:"Redis"` Redis ConfigRedis `yaml:"Redis"`
Database ConfigDatabase `yaml:"Database"` Database ConfigDatabase `yaml:"Database"`
Storage ConfigStorage `yaml:"Storage"`
Metrics ConfigMetrics `yaml:"Metrics"` Metrics ConfigMetrics `yaml:"Metrics"`
Development bool `yaml:"Development"` Development bool `yaml:"Development"`
} }

View File

@ -18,7 +18,7 @@ const (
type ProblemBuildPayload struct { type ProblemBuildPayload struct {
ProblemVersionID uint ProblemVersionID uint
ProblemFile string StorageKey string
} }
type ProblemUpdatePayload struct { type ProblemUpdatePayload struct {
@ -28,14 +28,15 @@ type ProblemUpdatePayload struct {
} }
type SubmitJudgePayload struct { type SubmitJudgePayload struct {
ProblemVersionId uint ProblemVersionID uint
StorageKey string StorageKey string
Submission Submission Submission Submission
} }
type SubmitUpdatePayload struct { type SubmitUpdatePayload struct {
Status e.Status Status e.Status
Sid uint SubmissionID uint
Point int32 ProblemVersionID uint
Context string Point int32
Context string
} }

View File

@ -83,7 +83,9 @@ func (r *Repo) migrateDatabase() {
_ = r.db.AutoMigrate(&model.User{}) _ = r.db.AutoMigrate(&model.User{})
_ = r.db.AutoMigrate(&model.Problem{}) _ = r.db.AutoMigrate(&model.Problem{})
_ = r.db.AutoMigrate(&model.ProblemVersion{})
_ = r.db.AutoMigrate(&model.Submission{}) _ = r.db.AutoMigrate(&model.Submission{})
_ = r.db.AutoMigrate(&model.Status{})
} }
// checkAlive deprecated // checkAlive deprecated

View File

@ -3,6 +3,7 @@ package problem
import ( import (
"github.com/WHUPRJ/woj-server/internal/e" "github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model" "github.com/WHUPRJ/woj-server/internal/model"
"github.com/jackc/pgtype"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -14,6 +15,7 @@ type CreateVersionData struct {
func (s *service) CreateVersion(data *CreateVersionData) (*model.ProblemVersion, e.Status) { func (s *service) CreateVersion(data *CreateVersionData) (*model.ProblemVersion, e.Status) {
problemVersion := &model.ProblemVersion{ problemVersion := &model.ProblemVersion{
ProblemID: data.ProblemID, ProblemID: data.ProblemID,
Context: pgtype.JSON{Status: pgtype.Null},
StorageKey: data.StorageKey, StorageKey: data.StorageKey,
} }

View File

@ -17,7 +17,7 @@ type Service interface {
QueryFuzz(search string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) QueryFuzz(search string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status)
CreateVersion(data *CreateVersionData) (*model.ProblemVersion, e.Status) CreateVersion(data *CreateVersionData) (*model.ProblemVersion, e.Status)
UpdateVersion(problemVersion *model.ProblemVersion) (*model.ProblemVersion, e.Status) UpdateVersion(pvid uint, values interface{}) e.Status
QueryVersion(pvid uint, shouldEnable bool) (*model.ProblemVersion, e.Status) QueryVersion(pvid uint, shouldEnable bool) (*model.ProblemVersion, e.Status)
QueryLatestVersion(pid uint) (*model.ProblemVersion, e.Status) QueryLatestVersion(pid uint) (*model.ProblemVersion, e.Status)
} }

View File

@ -6,12 +6,12 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
func (s *service) UpdateVersion(problemVersion *model.ProblemVersion) (*model.ProblemVersion, e.Status) { func (s *service) UpdateVersion(pvid uint, values interface{}) e.Status {
err := s.db.Save(problemVersion).Error err := s.db.Model(&model.ProblemVersion{}).Where("id = ?", pvid).Updates(values).Error
if err != nil { if err != nil {
s.log.Warn("DatabaseError", zap.Error(err), zap.Any("problemVersion", problemVersion)) s.log.Warn("DatabaseError", zap.Error(err), zap.Any("pvid", pvid), zap.Any("values", values))
return nil, e.DatabaseError return e.DatabaseError
} }
return problemVersion, e.Success return e.Success
} }

View File

@ -13,9 +13,9 @@ import (
var ( var (
Prefix = "./resource/runner" Prefix = "./resource/runner"
ProblemDir = "./problems/" ProblemDir = "./problem/"
ScriptsDir = "./scripts/" ScriptsDir = "./scripts/"
UserDir = "./users/" UserDir = "./user/"
TmpDir = "./tmp/" TmpDir = "./tmp/"
) )
@ -40,7 +40,7 @@ func (s *service) execute(script string, args ...string) error {
} }
func (s *service) checkAndExecute(version uint, user string, lang string, script string, fail e.Status) e.Status { func (s *service) checkAndExecute(version uint, user string, lang string, script string, fail e.Status) e.Status {
if !s.problemExists(version) { if !s.ProblemExists(version) {
s.log.Info("problem not exists", zap.Uint("version", version)) s.log.Info("problem not exists", zap.Uint("version", version))
return e.RunnerProblemNotExist return e.RunnerProblemNotExist
} }
@ -50,7 +50,7 @@ func (s *service) checkAndExecute(version uint, user string, lang string, script
return e.RunnerUserNotExist return e.RunnerUserNotExist
} }
err := s.execute(script, fmt.Sprintf("%d", version), fmt.Sprintf("%s", user), lang) err := s.execute(script, fmt.Sprintf("%d", version), user, lang)
if err != nil { if err != nil {
s.log.Info("execute failed", s.log.Info("execute failed",
@ -64,12 +64,12 @@ func (s *service) checkAndExecute(version uint, user string, lang string, script
return e.Success return e.Success
} }
func (s *service) problemExists(version uint) bool { func (s *service) ProblemExists(version uint) bool {
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", version)) problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", version))
return utils.FileExist(problemPath) return utils.FileExist(problemPath)
} }
func (s *service) userExists(user string, file string) bool { func (s *service) userExists(user string, file string) bool {
userPath := filepath.Join(UserDir, fmt.Sprintf("%s", user), file) userPath := filepath.Join(UserDir, user, file)
return utils.FileExist(userPath) return utils.FileExist(userPath)
} }

View File

@ -9,22 +9,22 @@ import (
) )
func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, e.Status) { func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, e.Status) {
target := filepath.Join(UserDir, fmt.Sprintf("%s", user), fmt.Sprintf("%s.out", user)) target := filepath.Join(UserDir, user, fmt.Sprintf("%s.out", user))
_ = os.Remove(target) _ = os.Remove(target)
status := s.checkAndExecute(version, user, lang, "problem_compile.sh", e.RunnerUserCompileFailed) status := s.checkAndExecute(version, user, lang, "problem_compile.sh", e.RunnerUserCompileFailed)
log := filepath.Join(UserDir, fmt.Sprintf("%s.compile.log", user)) log := filepath.Join(UserDir, user, fmt.Sprintf("%s.compile.log", user))
msg, err := utils.FileRead(log) msg, err := utils.FileRead(log)
msg = utils.If(err == nil, msg, nil).([]byte) msg = utils.If(err == nil, msg, nil).([]byte)
msgText := string(msg) msgText := string(msg)
if utils.FileExist(target) { if !utils.FileExist(target) || utils.FileEmpty(target) {
return JudgeStatus{}, e.Success
} else {
return JudgeStatus{ return JudgeStatus{
Message: "compile failed", Message: "compile failed",
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}}, Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
utils.If(status == e.Success, e.RunnerUserCompileFailed, status).(e.Status) utils.If(status == e.Success, e.RunnerUserCompileFailed, status).(e.Status)
} }
return JudgeStatus{}, e.Success
} }

View File

@ -31,7 +31,7 @@ func (s *service) download(version uint, url string) e.Status {
} }
func (s *service) prebuild(version uint, force bool) e.Status { func (s *service) prebuild(version uint, force bool) e.Status {
if !s.problemExists(version) { if !s.ProblemExists(version) {
return e.RunnerProblemNotExist return e.RunnerProblemNotExist
} }
@ -52,10 +52,17 @@ func (s *service) prebuild(version uint, force bool) e.Status {
return e.Success return e.Success
} }
func (s *service) NewProblem(version uint, url string) (Config, e.Status) { func (s *service) NewProblem(version uint, url string, force bool) (Config, e.Status) {
status := s.download(version, url) if force {
if status != e.Success { problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", version))
return Config{}, status _ = os.RemoveAll(problemPath)
}
if !s.ProblemExists(version) {
status := s.download(version, url)
if status != e.Success {
return Config{}, status
}
} }
cfg, err := s.ParseConfig(version, false) cfg, err := s.ParseConfig(version, false)
@ -63,7 +70,7 @@ func (s *service) NewProblem(version uint, url string) (Config, e.Status) {
return Config{}, e.RunnerProblemParseFailed return Config{}, e.RunnerProblemParseFailed
} }
status = s.prebuild(version, true) status := s.prebuild(version, true)
if status != e.Success { if status != e.Success {
return Config{}, status return Config{}, status
} }

View File

@ -12,7 +12,7 @@ type Service interface {
// EnsureDeps build docker images // EnsureDeps build docker images
EnsureDeps(force bool) e.Status EnsureDeps(force bool) e.Status
// NewProblem = Download + Parse + Prebuild // NewProblem = Download + Parse + Prebuild
NewProblem(version uint, url string) (Config, e.Status) NewProblem(version uint, url string, force bool) (Config, e.Status)
// Compile compile user submission // Compile compile user submission
Compile(version uint, user string, lang string) (JudgeStatus, e.Status) Compile(version uint, user string, lang string) (JudgeStatus, e.Status)
@ -21,6 +21,8 @@ type Service interface {
// ParseConfig parse config file // ParseConfig parse config file
ParseConfig(version uint, skipCheck bool) (Config, error) ParseConfig(version uint, skipCheck bool) (Config, error)
// ProblemExists check if problem exists
ProblemExists(version uint) bool
} }
type service struct { type service struct {

View File

@ -202,7 +202,7 @@ func (s *service) checkResults(user string, config *Config) (JudgeStatus, int32)
} }
var results []TaskStatus var results []TaskStatus
dir := filepath.Join(UserDir, fmt.Sprintf("%s", user)) dir := filepath.Join(UserDir, user)
var sum int32 = 0 var sum int32 = 0
for i := 1; i <= len(config.Tasks); i++ { for i := 1; i <= len(config.Tasks); i++ {
@ -213,14 +213,15 @@ func (s *service) checkResults(user string, config *Config) (JudgeStatus, int32)
result.getInfoText(info). result.getInfoText(info).
getInfo(). getInfo().
checkExit().
checkTime(config). checkTime(config).
checkMemory(config). checkMemory(config).
checkExit().
getJudgeText(judge). getJudgeText(judge).
getJudge(). getJudge().
checkJudge(&pts) checkJudge(&pts)
sum += result.Points sum += result.Points
results = append(results, result)
} }
return JudgeStatus{Message: "", Tasks: results}, sum return JudgeStatus{Message: "", Tasks: results}, sum

View File

@ -10,17 +10,20 @@ import (
type CreateData struct { type CreateData struct {
SubmissionID uint SubmissionID uint
ProblemVersionID uint ProblemVersionID uint
Context pgtype.JSON Context string
Point int32 Point int32
} }
func (s service) Create(data *model.Status) (*model.Status, e.Status) { func (s service) Create(data *CreateData) (*model.Status, e.Status) {
status := &model.Status{ status := &model.Status{
SubmissionID: data.SubmissionID, SubmissionID: data.SubmissionID,
ProblemVersionID: data.ProblemVersionID, ProblemVersionID: data.ProblemVersionID,
Context: data.Context, Context: pgtype.JSON{
Point: data.Point, Bytes: []byte(data.Context),
IsEnabled: true, Status: pgtype.Present,
},
Point: data.Point,
IsEnabled: true,
} }
err := s.db.Create(status).Error err := s.db.Create(status).Error

View File

@ -1,11 +0,0 @@
package status
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model"
)
func (s service) Rejudge(statusID uint) ([]*model.Status, e.Status) {
//TODO implement me
panic("implement me")
}

View File

@ -11,10 +11,9 @@ import (
var _ Service = (*service)(nil) var _ Service = (*service)(nil)
type Service interface { type Service interface {
Create(*model.Status) (*model.Status, e.Status) Create(data *CreateData) (*model.Status, e.Status)
Query(sid uint, associations bool) (*model.Status, e.Status) Query(sid uint, associations bool) (*model.Status, e.Status)
QueryByVersion(pvid uint, offset int, limit int) ([]*model.Status, e.Status) QueryByVersion(pvid uint, offset int, limit int) ([]*model.Status, e.Status)
Rejudge(statusID uint) ([]*model.Status, e.Status)
} }
type service struct { type service struct {

View File

@ -0,0 +1,30 @@
package storage
import (
"context"
"github.com/WHUPRJ/woj-server/internal/e"
"go.uber.org/zap"
"net/url"
"time"
)
func (s *service) Get(objectName string, expiry time.Duration) (string, e.Status) {
preSignedURL, err := s.client.PresignedGetObject(
context.Background(),
s.bucket,
objectName,
expiry,
url.Values{},
)
if err != nil {
s.log.Warn("failed to generate pre-signed get url",
zap.Error(err),
zap.String("objectName", objectName),
zap.Duration("expiry", expiry),
)
return "", e.StorageGetFailed
}
return preSignedURL.String(), e.Success
}

View File

@ -0,0 +1,41 @@
package storage
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"go.uber.org/zap"
"time"
)
var _ Service = (*service)(nil)
type Service interface {
Upload(objectName string, expiry time.Duration) (string, e.Status)
Get(objectName string, expiry time.Duration) (string, e.Status)
}
type service struct {
log *zap.Logger
client *minio.Client
bucket string
}
func NewService(g *global.Global) Service {
minioClient, err := minio.New(g.Conf.Storage.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(g.Conf.Storage.AccessKey, g.Conf.Storage.SecretKey, ""),
Secure: g.Conf.Storage.UseSSL,
})
if err != nil {
g.Log.Fatal("failed to create minio client", zap.Error(err))
return nil
}
return &service{
log: g.Log,
client: minioClient,
bucket: g.Conf.Storage.Bucket,
}
}

View File

@ -0,0 +1,28 @@
package storage
import (
"context"
"github.com/WHUPRJ/woj-server/internal/e"
"go.uber.org/zap"
"time"
)
func (s *service) Upload(objectName string, expiry time.Duration) (string, e.Status) {
preSignedURL, err := s.client.PresignedPutObject(
context.Background(),
s.bucket,
objectName,
expiry,
)
if err != nil {
s.log.Warn("failed to generate pre-signed upload url",
zap.Error(err),
zap.String("objectName", objectName),
zap.Duration("expiry", expiry),
)
return "", e.StorageUploadFailed
}
return preSignedURL.String(), e.Success
}

View File

@ -1,9 +1,11 @@
package submission package submission
import ( import (
"errors"
"github.com/WHUPRJ/woj-server/internal/e" "github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/model" "github.com/WHUPRJ/woj-server/internal/model"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
) )
@ -31,3 +33,25 @@ func (s *service) Query(pid uint, uid uint, offset int, limit int) ([]*model.Sub
} }
return submissions, e.Success return submissions, e.Success
} }
func (s *service) QueryBySid(sid uint, associations bool) (*model.Submission, e.Status) {
submission := new(model.Submission)
query := s.db
if associations {
query = query.Preload(clause.Associations)
}
err := query.First(&submission, sid).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, e.SubmissionNotFound
}
if err != nil {
s.log.Warn("DatabaseError", zap.Error(err), zap.Any("sid", sid))
return nil, e.DatabaseError
}
return submission, e.Success
}

View File

@ -13,6 +13,7 @@ var _ Service = (*service)(nil)
type Service interface { type Service interface {
Create(data *CreateData) (*model.Submission, e.Status) Create(data *CreateData) (*model.Submission, e.Status)
Query(pid uint, uid uint, offset int, limit int) ([]*model.Submission, e.Status) Query(pid uint, uid uint, offset int, limit int) ([]*model.Submission, e.Status)
QueryBySid(sid uint, associations bool) (*model.Submission, e.Status)
} }
type service struct { type service struct {

View File

@ -15,8 +15,6 @@ func (s *service) submit(typename string, payload []byte, queue string) (*asynq.
return nil, e.TaskEnqueueFailed return nil, e.TaskEnqueueFailed
} }
s.log.Debug("Successfully enqueued task", zap.Any("info", info))
return info, e.Success return info, e.Success
} }

View File

@ -7,16 +7,13 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
func (s *service) ProblemBuild(pvId uint, file string) (string, e.Status) { func (s *service) ProblemBuild(data *model.ProblemBuildPayload) (string, e.Status) {
payload, err := json.Marshal(model.ProblemBuildPayload{ payload, err := json.Marshal(data)
ProblemVersionID: pvId,
ProblemFile: file,
})
if err != nil { if err != nil {
s.log.Warn("json marshal error", s.log.Warn("json marshal error",
zap.Error(err), zap.Error(err),
zap.Any("ProblemVersionID", pvId), zap.Any("data", data),
zap.String("ProblemFile", file)) )
return "", e.InternalError return "", e.InternalError
} }
@ -25,18 +22,13 @@ func (s *service) ProblemBuild(pvId uint, file string) (string, e.Status) {
return info.ID, status return info.ID, status
} }
func (s *service) ProblemUpdate(status e.Status, pvId uint, ctx string) (string, e.Status) { func (s *service) ProblemUpdate(data *model.ProblemUpdatePayload) (string, e.Status) {
payload, err := json.Marshal(model.ProblemUpdatePayload{ payload, err := json.Marshal(data)
Status: status,
ProblemVersionID: pvId,
Context: ctx,
})
if err != nil { if err != nil {
s.log.Warn("json marshal error", s.log.Warn("json marshal error",
zap.Error(err), zap.Error(err),
zap.Any("Status", status), zap.Any("data", data),
zap.Any("ProblemVersionID", pvId), )
zap.Any("Context", ctx))
return "", e.InternalError return "", e.InternalError
} }

View File

@ -12,10 +12,10 @@ import (
var _ Service = (*service)(nil) var _ Service = (*service)(nil)
type Service interface { type Service interface {
ProblemBuild(pvId uint, file string) (string, e.Status) ProblemBuild(data *model.ProblemBuildPayload) (string, e.Status)
ProblemUpdate(status e.Status, pvId uint, ctx string) (string, e.Status) ProblemUpdate(data *model.ProblemUpdatePayload) (string, e.Status)
SubmitJudge(pvid uint, storageKey string, submission model.Submission) (string, e.Status) SubmitJudge(data *model.SubmitJudgePayload) (string, e.Status)
SubmitUpdate(status e.Status, sid uint, point int32, ctx runner.JudgeStatus) (string, e.Status) SubmitUpdate(data *model.SubmitUpdatePayload, ctx runner.JudgeStatus) (string, e.Status)
GetTaskInfo(string, string) (*asynq.TaskInfo, e.Status) GetTaskInfo(string, string) (*asynq.TaskInfo, e.Status)
} }

View File

@ -8,15 +8,13 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
func (s *service) SubmitJudge(pvid uint, storageKey string, submission model.Submission) (string, e.Status) { func (s *service) SubmitJudge(data *model.SubmitJudgePayload) (string, e.Status) {
payload, err := json.Marshal( payload, err := json.Marshal(data)
model.SubmitJudgePayload{
ProblemVersionId: pvid,
StorageKey: storageKey,
Submission: submission,
})
if err != nil { if err != nil {
s.log.Warn("json marshal error", zap.Error(err), zap.Any("Submission", submission)) s.log.Warn("json marshal error",
zap.Error(err),
zap.Any("data", data),
)
return "", e.InternalError return "", e.InternalError
} }
@ -25,7 +23,7 @@ func (s *service) SubmitJudge(pvid uint, storageKey string, submission model.Sub
return info.ID, status return info.ID, status
} }
func (s *service) SubmitUpdate(status e.Status, sid uint, point int32, ctx runner.JudgeStatus) (string, e.Status) { func (s *service) SubmitUpdate(data *model.SubmitUpdatePayload, ctx runner.JudgeStatus) (string, e.Status) {
ctxText, err := json.Marshal(ctx) ctxText, err := json.Marshal(ctx)
if err != nil { if err != nil {
s.log.Warn("json marshal error", s.log.Warn("json marshal error",
@ -34,18 +32,14 @@ func (s *service) SubmitUpdate(status e.Status, sid uint, point int32, ctx runne
return "", e.InternalError return "", e.InternalError
} }
payload, err := json.Marshal(model.SubmitUpdatePayload{ data.Context = string(ctxText)
Status: status, payload, err := json.Marshal(data)
Sid: sid,
Point: point,
Context: string(ctxText),
})
if err != nil { if err != nil {
s.log.Warn("json marshal error", s.log.Warn("json marshal error",
zap.Error(err), zap.Error(err),
zap.Any("Status", status), zap.Any("data", data),
zap.Int32("Point", point), zap.Any("Context", ctx),
zap.Any("Context", ctx)) )
return "", e.InternalError return "", e.InternalError
} }

View File

@ -23,6 +23,14 @@ func FileExist(filePath string) bool {
return If(err == nil || os.IsExist(err), true, false).(bool) return If(err == nil || os.IsExist(err), true, false).(bool)
} }
func FileEmpty(filePath string) bool {
stat, err := os.Stat(filePath)
if err != nil {
return true
}
return stat.Size() == 0
}
func FileTouch(filePath string) bool { func FileTouch(filePath string) bool {
base := filepath.Dir(filePath) base := filepath.Dir(filePath)
_ = os.MkdirAll(base, 0755) _ = os.MkdirAll(base, 0755)

View File

@ -31,7 +31,7 @@ log_info "NProcLimit: $Info_Limit_NProc"
# launcher will add 2 more seconds # launcher will add 2 more seconds
# here add 3 more seconds # here add 3 more seconds
TIMEOUT=$(((LIMIT_TIME + 1000) / 1000 + 3)) TIMEOUT=$(((LIMIT_TIME + 1000) / 1000 + 4))
log_info "Timeout: $TIMEOUT" log_info "Timeout: $TIMEOUT"
for test_num in $(seq "$Info_Num"); do for test_num in $(seq "$Info_Num"); do