├── .gitignore ├── LICENSE ├── README.md ├── chart ├── .helmignore ├── Chart.lock ├── Chart.yaml ├── templates │ ├── auth.yaml │ ├── executor.yaml │ ├── frontend.yaml │ ├── problems.yaml │ └── submissions.yaml └── values.yaml ├── cmd ├── auth │ ├── Dockerfile │ └── main.go ├── executor │ ├── Dockerfile │ ├── image.tar │ └── main.go ├── frontend │ ├── Dockerfile │ ├── auth.go │ ├── main.go │ ├── problems.go │ ├── static │ │ ├── css │ │ │ └── common.css │ │ └── js │ │ │ ├── edit.js │ │ │ ├── problem.js │ │ │ └── problems.js │ └── templates │ │ ├── create.tmpl │ │ ├── edit.tmpl │ │ ├── index.tmpl │ │ ├── login.tmpl │ │ ├── problem.tmpl │ │ ├── problems.tmpl │ │ └── register.tmpl ├── problems │ ├── Dockerfile │ ├── admin.go │ └── main.go └── submissions │ ├── Dockerfile │ └── main.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── images └── Noob Architecture.svg └── pkg ├── database ├── database.go ├── errors.go ├── problems.go └── users.go ├── message ├── pubsub.go ├── queue.go └── rabbit.go ├── model ├── problem.go ├── submission.go └── user.go ├── sessions ├── middleware.go └── sessions.go └── tracing └── tracing.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | *.swp 3 | .idea 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kevin Zheng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Noob 2 | A scalable distributed microservice architecture platform running on Kubernetes capable of judging and orchestrating the execution of user submitted code. 3 |
7 |
8 |
9 | #### Roadmap
10 | - Set up Kubernetes Cluster.
11 | * Set up rook helm chart for persistent volumes. (✅ 8/5/18)
12 | * ^ decided to do this w/o Helm.
13 | * Set up redis helm chart for sessions. (✅ 8/5/18)
14 | * Set up mongodb helm chart. (✅ 8/5/18)
15 | * Se up rabbitmq helm chart for code execution queue.
16 | - Set up auth microservice.
17 | * Add Dockerfile. (✅ 8/10/18)
18 | * Integrate into Kubernetes/Helm:
19 | * Create deployment. (✅ 8/11/18)
20 | * Create service. (✅ 8/11/18)
21 | * Set up /auth/ with nginx ingress. (✅ 8/19/18)
22 | * Connect to redis. (✅ 8/12/18)
23 | * Connect to mongodb. (✅ 8/13/18)
24 | * Endpoints:
25 | * Login - authenticate username + password with mongodb,
create session in redis.
(✅ 8/17/18)
26 | * Logout - destroy session in redis. (✅ 8/17/18)
27 | * Register - store username + hashed password in mongodb. (✅ 8/17/18)
28 | - Set up frontend microservice.
29 | * Add Dockerfile. (✅ 8/28/18)
30 | * Integrate into Kubernetes/Helm:
31 | * Create deployment. (✅ 8/28/18)
32 | * Create service. (✅ 8/28/18)
33 | * Set up / with nginx ingress. (✅ 8/28/18)
34 | * Create mock authentication page for signing up and logging in. (✅ 8/28/18)
35 | - Set up admin microservice.
36 | * Add Dockerfile. (✅ 9/5/18)
37 | * Integrate into Kubernetes/Helm:
38 | * Create deployment. (✅ 9/5/18)
39 | * Create service. (✅ 9/5/18)
40 | * Set up /admin/ nginx ingress. (✅ 9/5/18)
41 | * Endpoints:
42 | * Create Problem - store problem in mongodb (✅ 9/5/18)
43 | * Update Problem - update problem in mongodb (✅ 9/5/18)
44 | * Delete Problem - delete problem in mongodb (✅ 9/5/18)
45 | * Frontend UI
46 | - next: tbd…
47 |
48 | ### Development
49 | Some commands to know :P .
50 | **Accessing MongoDB**:
51 | ```
52 | $ kubectl get secret noob-mongodb -o jsonpath="{.data.mongodb-root-password}" | base64 --decode
53 | $ kubectl run -i -t --rm debug --image=ubuntu --restart=Never
54 | $ apt-get update && apt-get install mongodb
55 | $ mongo --host noob-mongodb --port 27017 -u root -p Alert: {{.Message}}
8 | {{end}} 9 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cmd/frontend/templates/edit.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 |Alert: {{.Message}}
8 | {{end}} 9 | {{if .User}} 10 |Hello {{.User}}.
11 | View Problems 12 |Alert: {{.Message}}
8 | {{end}} 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /cmd/frontend/templates/problem.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 |Alert: {{.Message}}
8 | {{end}} 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /cmd/problems/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build 2 | 3 | WORKDIR /go/src/github.com/kzh/noob/ 4 | ADD . /go/src/github.com/kzh/noob/ 5 | 6 | RUN go build -o main ./cmd/problems/ 7 | 8 | FROM alpine 9 | 10 | WORKDIR /app/ 11 | 12 | COPY --from=build /go/src/github.com/kzh/noob/main ./problems 13 | 14 | CMD ["./problems"] 15 | -------------------------------------------------------------------------------- /cmd/problems/admin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | 9 | noobdb "github.com/kzh/noob/pkg/database" 10 | "github.com/kzh/noob/pkg/model" 11 | noobsess "github.com/kzh/noob/pkg/sessions" 12 | ) 13 | 14 | func handleCreate(ctx *gin.Context) { 15 | var redirect string 16 | session := noobsess.Default(ctx) 17 | 18 | defer func() { 19 | session.Save() 20 | ctx.Redirect(http.StatusSeeOther, redirect) 21 | }() 22 | 23 | var prob model.ProblemData 24 | if err := ctx.ShouldBind(&prob); err != nil { 25 | redirect = "/" 26 | session.AddFlash("Invalid form data format") 27 | log.Println(err) 28 | return 29 | } 30 | 31 | problem, err := noobdb.CreateProblem(prob) 32 | if err != nil { 33 | session.AddFlash(err.Error()) 34 | redirect = "/" 35 | } 36 | 37 | redirect = "/problem/" + problem + "/" 38 | } 39 | 40 | func handleSelectIO(ctx *gin.Context) { 41 | id := ctx.Param("id") 42 | io, err := noobdb.IOProblem(id) 43 | if err == nil { 44 | ctx.JSON(http.StatusOK, io) 45 | return 46 | } 47 | 48 | status := http.StatusInternalServerError 49 | if err == noobdb.ErrNoSuchProblem { 50 | status = http.StatusNotFound 51 | } 52 | 53 | ctx.JSON(status, gin.H{ 54 | "error": err.Error(), 55 | }) 56 | } 57 | 58 | func handleEdit(ctx *gin.Context) { 59 | var redirect string 60 | session := noobsess.Default(ctx) 61 | 62 | defer func() { 63 | session.Save() 64 | ctx.Redirect(http.StatusSeeOther, redirect) 65 | }() 66 | 67 | var prob model.Problem 68 | if err := ctx.ShouldBind(&prob); err != nil { 69 | redirect = "/" 70 | session.AddFlash("Invalid form data format") 71 | log.Println(err) 72 | return 73 | } 74 | 75 | err := noobdb.EditProblem(prob) 76 | if err != nil { 77 | session.AddFlash(err.Error()) 78 | redirect = "/" 79 | } 80 | 81 | session.AddFlash("Success!") 82 | redirect = "/problem/" + prob.ID + "/edit/" 83 | } 84 | 85 | func handleDelete(ctx *gin.Context) { 86 | var redirect string 87 | session := noobsess.Default(ctx) 88 | 89 | defer func() { 90 | session.Save() 91 | ctx.Redirect(http.StatusSeeOther, redirect) 92 | }() 93 | 94 | var prob model.ProblemID 95 | if err := ctx.ShouldBind(&prob); err != nil { 96 | redirect = "/" 97 | session.AddFlash("Invalid form data format") 98 | log.Println(err) 99 | return 100 | } 101 | 102 | err := noobdb.DeleteProblem(prob) 103 | if err != nil { 104 | redirect = "/" 105 | } 106 | log.Println(prob) 107 | 108 | session.AddFlash("Success!") 109 | redirect = "/" 110 | } 111 | -------------------------------------------------------------------------------- /cmd/problems/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | 9 | noobdb "github.com/kzh/noob/pkg/database" 10 | noobsess "github.com/kzh/noob/pkg/sessions" 11 | ) 12 | 13 | func handleList(ctx *gin.Context) { 14 | problems, err := noobdb.Problems() 15 | if err == nil { 16 | ctx.JSON(http.StatusOK, problems) 17 | return 18 | } 19 | 20 | status := http.StatusInternalServerError 21 | ctx.JSON(status, gin.H{ 22 | "error": err.Error(), 23 | }) 24 | } 25 | 26 | func handleSelect(ctx *gin.Context) { 27 | id := ctx.Param("id") 28 | problem, err := noobdb.SnapProblem(id) 29 | if err == nil { 30 | ctx.JSON(http.StatusOK, problem) 31 | return 32 | } 33 | 34 | status := http.StatusInternalServerError 35 | if err == noobdb.ErrNoSuchProblem { 36 | status = http.StatusNotFound 37 | } 38 | 39 | ctx.JSON(status, gin.H{ 40 | "error": err.Error(), 41 | }) 42 | } 43 | 44 | func main() { 45 | log.Println("Noob: Problems MS is starting...") 46 | 47 | r := gin.Default() 48 | 49 | r.Use(noobsess.Sessions()) 50 | 51 | r.GET("/list", handleList) 52 | r.GET("/get/:id", handleSelect) 53 | 54 | admin := r.Group("/") 55 | admin.Use(noobsess.LoggedIn(true)) 56 | admin.Use(noobsess.Admin(true)) 57 | 58 | admin.POST("/create", handleCreate) 59 | admin.POST("/edit", handleEdit) 60 | admin.POST("/delete", handleDelete) 61 | 62 | adminNR := r.Group("/") 63 | adminNR.Use(noobsess.LoggedIn(false)) 64 | adminNR.Use(noobsess.Admin(false)) 65 | 66 | adminNR.GET("/get/:id/io", handleSelectIO) 67 | 68 | if err := r.Run(); err != nil { 69 | log.Println(err) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /cmd/submissions/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build 2 | 3 | WORKDIR /go/src/github.com/kzh/noob/ 4 | ADD . /go/src/github.com/kzh/noob/ 5 | 6 | RUN go build -o main ./cmd/submissions/ 7 | 8 | FROM alpine 9 | 10 | WORKDIR /app/ 11 | 12 | COPY --from=build /go/src/github.com/kzh/noob/main ./submissions 13 | 14 | CMD ["./submissions"] 15 | -------------------------------------------------------------------------------- /cmd/submissions/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/google/uuid" 8 | "github.com/gorilla/websocket" 9 | 10 | "github.com/kzh/noob/pkg/message" 11 | "github.com/kzh/noob/pkg/model" 12 | noobsess "github.com/kzh/noob/pkg/sessions" 13 | ) 14 | 15 | var upgrader = websocket.Upgrader{} 16 | 17 | func handleSubmit(ctx *gin.Context) { 18 | c, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil) 19 | if err != nil { 20 | return 21 | } 22 | defer c.Close() 23 | 24 | var submission model.Submission 25 | if err := c.ReadJSON(&submission); err != nil { 26 | return 27 | } 28 | submission.ID = uuid.New().String() 29 | 30 | if err := message.Schedule(submission); err != nil { 31 | return 32 | } 33 | 34 | results, err := message.Subscribe(submission.ID) 35 | if err != nil { 36 | return 37 | } 38 | 39 | log.Println("Results:") 40 | for result := range results { 41 | log.Println(string(result.Body)) 42 | err := c.WriteMessage(websocket.TextMessage, result.Body) 43 | if err != nil { 44 | return 45 | } 46 | } 47 | } 48 | 49 | func main() { 50 | log.Println("Noob: Submissions MS is starting...") 51 | 52 | r := gin.Default() 53 | r.Use(noobsess.Sessions()) 54 | r.Use(noobsess.LoggedIn(false)) 55 | 56 | r.GET("/submit", handleSubmit) 57 | 58 | if err := r.Run(); err != nil { 59 | log.Println(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | auth: 4 | build: 5 | context: . 6 | dockerfile: ./cmd/auth/Dockerfile 7 | container_name: uhkevin/noob-auth 8 | image: uhkevin/noob-auth 9 | ports: 10 | - "8080:8080" 11 | frontend: 12 | build: 13 | context: . 14 | dockerfile: ./cmd/frontend/Dockerfile 15 | container_name: uhkevin/noob-frontend 16 | image: uhkevin/noob-frontend 17 | ports: 18 | - "8080:8080" 19 | problems: 20 | build: 21 | context: . 22 | dockerfile: ./cmd/problems/Dockerfile 23 | container_name: uhkevin/noob-problems 24 | image: uhkevin/noob-problems 25 | ports: 26 | - "8080:8080" 27 | submissions: 28 | build: 29 | context: . 30 | dockerfile: ./cmd/submissions/Dockerfile 31 | container_name: uhkevin/noob-submissions 32 | image: uhkevin/noob-submissions 33 | ports: 34 | - "8080:8080" 35 | executor: 36 | build: 37 | context: . 38 | dockerfile: ./cmd/executor/Dockerfile 39 | container_name: uhkevin/noob-executor 40 | image: uhkevin/noob-executor 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kzh/noob 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/docker/distribution v2.7.1+incompatible // indirect 7 | github.com/docker/docker v1.13.1 8 | github.com/docker/go-connections v0.4.0 // indirect 9 | github.com/docker/go-units v0.4.0 // indirect 10 | github.com/gin-contrib/sessions v0.0.3 11 | github.com/gin-gonic/gin v1.6.2 12 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 13 | github.com/google/uuid v1.1.1 14 | github.com/gorilla/websocket v1.4.2 15 | github.com/opencontainers/go-digest v1.0.0-rc1 // indirect 16 | github.com/opentracing/opentracing-go v1.2.0 17 | github.com/pkg/errors v0.9.1 // indirect 18 | github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 19 | github.com/uber/jaeger-client-go v2.25.0+incompatible 20 | github.com/uber/jaeger-lib v2.2.0+incompatible // indirect 21 | go.uber.org/atomic v1.6.0 // indirect 22 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= 2 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= 3 | github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= 4 | github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= 9 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 10 | github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= 11 | github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 12 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 13 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 14 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 15 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 16 | github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuLP6fBI= 17 | github.com/gin-contrib/sessions v0.0.3/go.mod h1:8C/J6cad3Il1mWYYgtw0w+hqasmpvy25mPkXdOgeB9I= 18 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 19 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 20 | github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= 21 | github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= 22 | github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 23 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= 24 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= 25 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 26 | github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= 27 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 28 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 29 | github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= 30 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 31 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 32 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 33 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 34 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 35 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 36 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 37 | github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 38 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 39 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 40 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 41 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 42 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= 43 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 44 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 45 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 46 | github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 47 | github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU= 48 | github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 49 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 50 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 51 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 52 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 53 | github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw= 54 | github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= 55 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 56 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 57 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 58 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 59 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 60 | github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc= 61 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 62 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 63 | github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= 64 | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 65 | github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 66 | github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 67 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 68 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 69 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 70 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 71 | github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= 72 | github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 h1:2MR0pKUzlP3SGgj5NYJe/zRYDwOu9ku6YHy+Iw7l5DM= 73 | github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 74 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 75 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 76 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 77 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 78 | github.com/uber/jaeger-client-go v1.6.0 h1:3+zLlq+4npI5fg8IsgAje3YsP7TcEdNzJScyqFIzxEQ= 79 | github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= 80 | github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= 81 | github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo= 82 | github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= 83 | github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= 84 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 85 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 86 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 87 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 88 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 89 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 90 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 91 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y= 92 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 93 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 94 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 95 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 96 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 97 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 98 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 99 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 101 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 102 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 103 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 104 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 105 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 106 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 107 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 108 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 109 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 110 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 111 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 112 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 113 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 114 | gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 115 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 116 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 117 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 118 | -------------------------------------------------------------------------------- /pkg/database/database.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/globalsign/mgo" 9 | "github.com/globalsign/mgo/bson" 10 | ) 11 | 12 | var db *mgo.Database 13 | 14 | func init() { 15 | log.Println("Connecting to MongoDB...") 16 | 17 | // Connect to mongodb 18 | dbInfo := &mgo.DialInfo{ 19 | Addrs: []string{"noob-mongodb:27017"}, 20 | Database: "admin", 21 | Username: "root", 22 | Password: os.Getenv("MONGODB_PASSWORD"), 23 | } 24 | 25 | session, err := mgo.DialWithInfo(dbInfo) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | db = session.DB("noob") 31 | log.Println("Connected to MongoDB.") 32 | } 33 | 34 | func count(name string) (string, error) { 35 | counters := db.C("counters") 36 | 37 | query := bson.M{"_id": name} 38 | update := mgo.Change{ 39 | Update: bson.M{"$inc": bson.M{"count": 1}}, 40 | Upsert: true, 41 | ReturnNew: true, 42 | } 43 | 44 | var doc bson.M 45 | _, err := counters.Find(query).Apply(update, &doc) 46 | if err != nil { 47 | return "", err 48 | } 49 | 50 | id := strconv.Itoa(doc["count"].(int)) 51 | return id, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/database/errors.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | ErrInvalidCredential = errors.New("Invalid username or password.") 9 | ErrUnavailableUsername = errors.New("Username taken.") 10 | ErrInternalServer = errors.New("Internal server error.") 11 | ErrNoSuchProblem = errors.New("No such problem.") 12 | ) 13 | -------------------------------------------------------------------------------- /pkg/database/problems.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/globalsign/mgo" 7 | "github.com/globalsign/mgo/bson" 8 | 9 | . "github.com/kzh/noob/pkg/model" 10 | ) 11 | 12 | func CreateProblem(p ProblemData) (string, error) { 13 | problems := db.C("problems") 14 | 15 | id, err := count("problems") 16 | if err != nil { 17 | log.Println(err) 18 | return "", ErrInternalServer 19 | } 20 | 21 | rec := bson.M{ 22 | "_id": id, 23 | "name": p.Name, 24 | "description": p.Description, 25 | "inputs": p.In, 26 | "outputs": p.Out, 27 | } 28 | if err := problems.Insert(rec); err != nil { 29 | log.Println(err) 30 | return "", ErrInternalServer 31 | } 32 | 33 | return id, nil 34 | } 35 | 36 | func EditProblem(p Problem) error { 37 | problems := db.C("problems") 38 | 39 | query := bson.M{"_id": p.ID} 40 | update := bson.M{ 41 | "_id": p.ID, 42 | "name": p.Name, 43 | "description": p.Description, 44 | "inputs": p.In, 45 | "outputs": p.Out, 46 | } 47 | 48 | _, err := problems.Upsert( 49 | query, 50 | bson.M{ 51 | "$set": update, 52 | }, 53 | ) 54 | if err != nil { 55 | log.Println(err) 56 | return ErrInternalServer 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func DeleteProblem(pid ProblemID) error { 63 | problems := db.C("problems") 64 | 65 | query := bson.M{"_id": pid.ID} 66 | err := problems.Remove(query) 67 | if err == nil { 68 | return nil 69 | } else if err == mgo.ErrNotFound { 70 | return ErrNoSuchProblem 71 | } else { 72 | log.Println(err) 73 | return ErrInternalServer 74 | } 75 | } 76 | 77 | func Problems() ([]ProblemSnap, error) { 78 | problems := db.C("problems") 79 | 80 | query := problems.Find(bson.M{}) 81 | count, err := query.Count() 82 | if err != nil { 83 | log.Println(err) 84 | return nil, ErrInternalServer 85 | } 86 | 87 | res := make([]ProblemSnap, count) 88 | 89 | var ( 90 | problem ProblemSnap 91 | i int 92 | ) 93 | 94 | iter := query.Iter() 95 | for iter.Next(&problem) { 96 | res[i] = problem 97 | i++ 98 | } 99 | 100 | err = iter.Close() 101 | if err != nil { 102 | log.Println(err) 103 | } 104 | 105 | return res, nil 106 | } 107 | 108 | func problem(id string, format interface{}) error { 109 | problems := db.C("problems") 110 | 111 | err := problems.Find(bson.M{ 112 | "_id": id, 113 | }).One(format) 114 | 115 | if err == mgo.ErrNotFound { 116 | return ErrNoSuchProblem 117 | } else if err != nil { 118 | log.Println(err) 119 | return ErrInternalServer 120 | } 121 | 122 | return err 123 | } 124 | 125 | func FullProblem(id string) (Problem, error) { 126 | var p Problem 127 | err := problem(id, &p) 128 | return p, err 129 | } 130 | 131 | func SnapProblem(id string) (ProblemSnap, error) { 132 | var p ProblemSnap 133 | err := problem(id, &p) 134 | return p, err 135 | } 136 | 137 | func IOProblem(id string) (ProblemIO, error) { 138 | var io ProblemIO 139 | err := problem(id, &io) 140 | return io, err 141 | } 142 | -------------------------------------------------------------------------------- /pkg/database/users.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/globalsign/mgo/bson" 7 | "golang.org/x/crypto/bcrypt" 8 | 9 | . "github.com/kzh/noob/pkg/model" 10 | ) 11 | 12 | func Authenticate(cred Credential) (bson.M, error) { 13 | users := db.C("users") 14 | 15 | var rec bson.M 16 | query := bson.M{"username": cred.Username} 17 | if err := users.Find(query).One(&rec); err != nil { 18 | return nil, ErrInvalidCredential 19 | } 20 | 21 | hash := rec["password"].(string) 22 | if err := bcrypt.CompareHashAndPassword( 23 | []byte(hash), 24 | []byte(cred.Password), 25 | ); err != nil { 26 | return nil, ErrInvalidCredential 27 | } 28 | 29 | delete(rec, "password") 30 | return rec, nil 31 | } 32 | 33 | func Register(cred Credential, data bson.M) error { 34 | users := db.C("users") 35 | 36 | var rec bson.M 37 | query := bson.M{"username": cred.Username} 38 | if err := users.Find(query).One(&rec); err == nil { 39 | return ErrUnavailableUsername 40 | } 41 | 42 | hash, err := bcrypt.GenerateFromPassword( 43 | []byte(cred.Password), 44 | bcrypt.DefaultCost, 45 | ) 46 | if err != nil { 47 | log.Println(err) 48 | return ErrInternalServer 49 | } 50 | 51 | rec = bson.M{ 52 | "username": cred.Username, 53 | "password": string(hash), 54 | } 55 | 56 | for k, v := range data { 57 | rec[k] = v 58 | } 59 | 60 | if err := users.Insert(rec); err != nil { 61 | log.Println(err) 62 | return ErrInternalServer 63 | } 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/message/pubsub.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/streadway/amqp" 7 | 8 | "github.com/kzh/noob/pkg/model" 9 | ) 10 | 11 | func Subscribe(pubsub string) (<-chan amqp.Delivery, error) { 12 | ch, err := rabbitmq.Channel() 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | err = ch.ExchangeDeclare( 18 | pubsub, "fanout", 19 | true, false, false, false, nil, 20 | ) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | q, err := ch.QueueDeclare( 26 | "", 27 | false, false, true, false, nil, 28 | ) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | err = ch.QueueBind( 34 | q.Name, 35 | "", pubsub, false, nil, 36 | ) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | msgs, err := ch.Consume( 42 | q.Name, "", 43 | true, false, false, false, nil, 44 | ) 45 | return msgs, err 46 | } 47 | 48 | func Publish(pubsub string, res model.SubmissionResult) error { 49 | ch, err := rabbitmq.Channel() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | err = ch.ExchangeDeclare( 55 | pubsub, "fanout", 56 | true, false, false, false, nil, 57 | ) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | body, err := json.Marshal(res) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | publishing := amqp.Publishing{ 68 | DeliveryMode: amqp.Persistent, 69 | ContentType: "text/plain", 70 | Body: body, 71 | } 72 | return ch.Publish(pubsub, "", false, false, publishing) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/message/queue.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | "github.com/streadway/amqp" 8 | 9 | "github.com/kzh/noob/pkg/model" 10 | ) 11 | 12 | func Schedule(s model.Submission) error { 13 | ch, err := rabbitmq.Channel() 14 | if err != nil { 15 | return err 16 | } 17 | defer ch.Close() 18 | 19 | _, err = ch.QueueDeclare( 20 | "submissions", 21 | true, false, false, false, nil, 22 | ) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | body, err := json.Marshal(s) 28 | if err != nil { 29 | return err 30 | } 31 | log.Println(string(body)) 32 | 33 | publishing := amqp.Publishing{ 34 | DeliveryMode: amqp.Persistent, 35 | ContentType: "text/plain", 36 | Body: body, 37 | } 38 | return ch.Publish("", "submissions", false, false, publishing) 39 | } 40 | 41 | func Poll() (<-chan amqp.Delivery, error) { 42 | ch, err := rabbitmq.Channel() 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | _, err = ch.QueueDeclare( 48 | "submissions", 49 | true, false, false, false, nil, 50 | ) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | msgs, err := ch.Consume( 56 | "submissions", 57 | "", true, false, false, false, nil, 58 | ) 59 | return msgs, err 60 | } 61 | -------------------------------------------------------------------------------- /pkg/message/rabbit.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "log" 5 | "net/url" 6 | "os" 7 | 8 | "github.com/streadway/amqp" 9 | ) 10 | 11 | var rabbitmq *amqp.Connection 12 | 13 | func init() { 14 | log.Println("Connecting to RabbitMQ...") 15 | 16 | var addr url.URL 17 | addr.Scheme = "amqp" 18 | addr.User = url.UserPassword( 19 | "user", 20 | os.Getenv("RABBITMQ_PASSWORD"), 21 | ) 22 | addr.Host = "noob-rabbitmq:5672" 23 | addr.Path = "/" 24 | 25 | u := addr.String() 26 | 27 | var err error 28 | rabbitmq, err = amqp.Dial(u) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | log.Println("Connected to RabbitMQ.") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/model/problem.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type ProblemID struct { 4 | ID string `form:"id" binding:"required"` 5 | } 6 | 7 | type ProblemData struct { 8 | Name string `form:"name" binding:"required"` 9 | Description string `form:"description" binding:"required"` 10 | In string `form:"inputs" binding:"required"` 11 | Out string `form:"outputs" binding:"required"` 12 | } 13 | 14 | type Problem struct { 15 | ID string `form:"id" json:"id" bson:"_id"` 16 | Name string `form:"name" json:"name" bson:"name" binding:"required"` 17 | Description string `form:"description" json:"description" bson:"description" binding:"required"` 18 | In string `form:"inputs" json:"inputs" bson:"inputs" binding:"required"` 19 | Out string `form:"outputs" json:"outputs" bson:"outputs" binding:"required"` 20 | } 21 | 22 | type ProblemSnap struct { 23 | ID string `json:"id" bson:"_id"` 24 | Name string `json:"name" bson:"name"` 25 | Description string `json:"description" bson:"description"` 26 | } 27 | 28 | type ProblemIO struct { 29 | In string `json:"inputs" bson:"inputs"` 30 | Out string `json:"outputs" bson:"outputs"` 31 | } 32 | -------------------------------------------------------------------------------- /pkg/model/submission.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Submission struct { 4 | ID string 5 | ProblemID string `json:"problem"` 6 | Code string `json:"code"` 7 | } 8 | 9 | type SubmissionResult struct { 10 | Stage string `json:"stage"` 11 | Status string `json:"status"` 12 | } 13 | -------------------------------------------------------------------------------- /pkg/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Credential struct { 4 | Username string `form:"username" binding:"required"` 5 | Password string `form:"password" binding:"required"` 6 | } 7 | -------------------------------------------------------------------------------- /pkg/sessions/middleware.go: -------------------------------------------------------------------------------- 1 | package sessions 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | type guardian func(sess NoobSession) bool 9 | 10 | func guard(g guardian, message string, redirect bool) gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | session := Default(c) 13 | defer session.Save() 14 | 15 | if g(session) { 16 | c.Next() 17 | return 18 | } 19 | 20 | if redirect { 21 | session.AddFlash(message) 22 | c.Redirect(http.StatusSeeOther, "/") 23 | } else { 24 | c.JSON( 25 | http.StatusUnauthorized, 26 | gin.H{"error": message}, 27 | ) 28 | } 29 | 30 | c.Abort() 31 | } 32 | } 33 | 34 | func LoggedIn(redirect bool) gin.HandlerFunc { 35 | return guard( 36 | NoobSession.IsLoggedIn, 37 | "Not logged in.", 38 | redirect, 39 | ) 40 | } 41 | 42 | func Admin(redirect bool) gin.HandlerFunc { 43 | return guard( 44 | NoobSession.IsAdmin, 45 | "Insufficient permissions.", 46 | redirect, 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/sessions/sessions.go: -------------------------------------------------------------------------------- 1 | package sessions 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/gin-contrib/sessions" 8 | "github.com/gin-contrib/sessions/redis" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func Sessions() gin.HandlerFunc { 13 | log.Println("Connecting to Redis Sessions...") 14 | 15 | store, err := redis.NewStore( 16 | 10, 17 | "tcp", 18 | "noob-sessions-master:6379", 19 | os.Getenv("SESSIONS_PASSWORD"), 20 | []byte("NOOB_SESSION_SECRET"), 21 | //[]byte(os.Getenv("SESSION_SECRET")), 22 | ) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | log.Println("Connected to Redis Sessions.") 28 | return sessions.Sessions("noob", store) 29 | } 30 | 31 | type NoobSession struct { 32 | sessions.Session 33 | } 34 | 35 | func Default(ctx *gin.Context) NoobSession { 36 | return NoobSession{sessions.Default(ctx)} 37 | } 38 | 39 | func (s NoobSession) SetM(data map[string]interface{}) { 40 | for k, v := range data { 41 | s.Set(k, v) 42 | } 43 | } 44 | 45 | func (s NoobSession) IsLoggedIn() bool { 46 | _, ok := s.Get("username").(string) 47 | return ok 48 | } 49 | 50 | func (s NoobSession) IsAdmin() bool { 51 | role, ok := s.Get("role").(string) 52 | return ok && role == "admin" 53 | } 54 | 55 | func (s NoobSession) Username() string { 56 | usr, ok := s.Get("username").(string) 57 | if !ok { 58 | return "" 59 | } 60 | 61 | return usr 62 | } 63 | -------------------------------------------------------------------------------- /pkg/tracing/tracing.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "io" 5 | "log" 6 | 7 | opentracing "github.com/opentracing/opentracing-go" 8 | config "github.com/uber/jaeger-client-go/config" 9 | jaegerlog "github.com/uber/jaeger-client-go/log" 10 | ) 11 | 12 | func InitJaeger() (io.Closer, error) { 13 | log.Println("Connecting to Jaeger...") 14 | 15 | cfg, err := config.FromEnv() 16 | if err != nil { 17 | log.Printf("Could not parse Jaeger env vars: %s", err.Error()) 18 | return nil, err 19 | } 20 | 21 | tracer, closer, err := cfg.NewTracer( 22 | config.Logger(jaegerlog.StdLogger), 23 | ) 24 | if err != nil { 25 | log.Printf("Could not initialize jaeger tracer: %s", err.Error()) 26 | return nil, err 27 | } 28 | 29 | opentracing.SetGlobalTracer(tracer) 30 | 31 | log.Println("Connected to Jaeger.") 32 | return closer, nil 33 | } 34 | --------------------------------------------------------------------------------