Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
dff37906c6 | |||
50911af656 | |||
1243203085 | |||
7ae2775c3f | |||
fcd74b54cc | |||
b090a80f03 | |||
3a6a62fb7c | |||
3739ff011e | |||
046d95c5b3 | |||
090c794c24 | |||
4373ca4ba9 | |||
f77dd14ac9 | |||
727e358cf9 | |||
8250686477 | |||
1f96b5facb | |||
22086e772c | |||
c99608aaff | |||
fdc1f1aadf | |||
3c0028f308 | |||
a2c6981d36 | |||
d37cc9cdf2 | |||
0eaf9aa164 | |||
d37964764e | |||
55b5516782 | |||
4c5d9e420d | |||
dee563e12a | |||
5ef2de6f50 | |||
95e9b4d8b3 | |||
eea57786ff | |||
64d6fe6059 | |||
7f1054d509 | |||
ecaff95ca7 | |||
7c2e8f00ad | |||
1a747bee80 | |||
a9a4eaa20d | |||
796935da86 | |||
6677a698be | |||
aea340fe97 | |||
2a04efb10b | |||
119bb79a88 | |||
c630b44ad3 |
@ -1,3 +1,36 @@
|
|||||||
|
# Stage 1: Build
|
||||||
|
FROM docker.io/golang:1.23 AS builder
|
||||||
|
|
||||||
|
# Set the working directory in the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy go.mod and go.sum files
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# Download dependencies
|
||||||
|
RUN --mount=type=secret,id=netrc,dst=/root/.netrc go mod download
|
||||||
|
|
||||||
|
# Copy the entire project
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the Go application
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o service ./cmd/maps-service/service.go
|
||||||
|
|
||||||
|
# Stage 2: Run
|
||||||
FROM alpine
|
FROM alpine
|
||||||
COPY build/server /
|
|
||||||
ENTRYPOINT ["/server"]
|
# Set up a non-root user for security
|
||||||
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Set the working directory in the container
|
||||||
|
WORKDIR /home/appuser
|
||||||
|
|
||||||
|
# Copy the built application from the builder stage
|
||||||
|
COPY --from=builder /app/service .
|
||||||
|
|
||||||
|
# Expose application port (adjust if needed)
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
ENTRYPOINT ["./service"]
|
||||||
|
8
Makefile
Normal file
8
Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.PHONY: maps-service web validation
|
||||||
|
maps-service:
|
||||||
|
DOCKER_BUILDKIT=1 docker build . -f Containerfile -t maps-service \
|
||||||
|
--secret id=netrc,src=/home/quat/.netrc
|
||||||
|
web:
|
||||||
|
docker build web -f web/Containerfile -t maps-service-web
|
||||||
|
validation:
|
||||||
|
docker build validation -f validation/Containerfile -t maps-service-validation
|
@ -28,8 +28,13 @@ Prerequisite: bun installed
|
|||||||
|
|
||||||
1. `cd web`
|
1. `cd web`
|
||||||
2. `bun install`
|
2. `bun install`
|
||||||
|
|
||||||
|
#### For development:
|
||||||
|
3. `bun run dev`
|
||||||
|
|
||||||
|
#### For production:
|
||||||
3. `bun run build`
|
3. `bun run build`
|
||||||
4. `bun run start`
|
4. `bun run start` (optionally start a node server)
|
||||||
|
|
||||||
### Script Validation
|
### Script Validation
|
||||||
|
|
||||||
|
111
compose.yaml
111
compose.yaml
@ -7,28 +7,113 @@ services:
|
|||||||
nats:
|
nats:
|
||||||
image: docker.io/nats:latest
|
image: docker.io/nats:latest
|
||||||
container_name: nats
|
container_name: nats
|
||||||
|
command: ["-js"] #"-DVV"
|
||||||
networks:
|
networks:
|
||||||
- maps-service-network
|
- maps-service-network
|
||||||
|
|
||||||
maps-service:
|
mapsservice:
|
||||||
build:
|
image:
|
||||||
context: .
|
maps-service
|
||||||
dockerfile: Containerfile
|
container_name: mapsservice
|
||||||
container_name: maps-service
|
command: [
|
||||||
|
# debug
|
||||||
|
"--debug","serve",
|
||||||
|
# http service port
|
||||||
|
"--port","8082",
|
||||||
|
# postgres
|
||||||
|
"--pg-host","10.0.0.29",
|
||||||
|
"--pg-port","5432",
|
||||||
|
"--pg-db","maps",
|
||||||
|
"--pg-user","quat",
|
||||||
|
"--pg-password","happypostgresuser",
|
||||||
|
# other hosts
|
||||||
|
"--nats-host","nats:4222",
|
||||||
|
"--auth-rpc-host","authrpc:8081"
|
||||||
|
]
|
||||||
|
depends_on:
|
||||||
|
- authrpc
|
||||||
|
- nats
|
||||||
networks:
|
networks:
|
||||||
- maps-service-network
|
- maps-service-network
|
||||||
|
ports:
|
||||||
|
- "8082:8082"
|
||||||
|
|
||||||
web:
|
web:
|
||||||
build:
|
image:
|
||||||
context: web
|
maps-service-web
|
||||||
dockerfile: Containerfile
|
networks:
|
||||||
|
- maps-service-network
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
|
||||||
|
validation:
|
||||||
|
image:
|
||||||
|
maps-service-validation
|
||||||
|
container_name: validation
|
||||||
|
environment:
|
||||||
|
- RBXCOOKIE
|
||||||
|
- API_HOST=http://mapsservice:8082
|
||||||
|
- NATS_HOST=nats:4222
|
||||||
|
- DATA_HOST=http://dataservice:9000
|
||||||
|
depends_on:
|
||||||
|
- nats
|
||||||
|
# note: this races the mapsservice which creates a nats stream
|
||||||
|
# the validation will panic if the nats stream is not created
|
||||||
|
- mapsservice
|
||||||
|
- dataservice
|
||||||
networks:
|
networks:
|
||||||
- maps-service-network
|
- maps-service-network
|
||||||
|
|
||||||
validation:
|
dataservice:
|
||||||
build:
|
image: registry.itzana.me/strafesnet/data-service:master
|
||||||
context: validation
|
container_name: dataservice
|
||||||
dockerfile: Containerfile
|
environment:
|
||||||
container_name: validation
|
- DEBUG=true
|
||||||
|
- PG_HOST=10.0.0.29
|
||||||
|
- PG_PORT=5432
|
||||||
|
- PG_USER=quat
|
||||||
|
- PG_DB=data
|
||||||
|
- PG_PASS=happypostgresuser
|
||||||
networks:
|
networks:
|
||||||
- maps-service-network
|
- maps-service-network
|
||||||
|
|
||||||
|
authredis:
|
||||||
|
image: docker.io/redis:latest
|
||||||
|
container_name: authredis
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
command: ["redis-server", "--appendonly", "yes"]
|
||||||
|
networks:
|
||||||
|
- maps-service-network
|
||||||
|
|
||||||
|
authrpc:
|
||||||
|
image: registry.itzana.me/strafesnet/auth-service:master
|
||||||
|
container_name: authrpc
|
||||||
|
command: ["serve", "rpc"]
|
||||||
|
environment:
|
||||||
|
- REDIS_ADDR=authredis:6379
|
||||||
|
env_file:
|
||||||
|
- ../auth-compose/auth-service.env
|
||||||
|
depends_on:
|
||||||
|
- authredis
|
||||||
|
networks:
|
||||||
|
- maps-service-network
|
||||||
|
logging:
|
||||||
|
driver: "none"
|
||||||
|
|
||||||
|
auth-web:
|
||||||
|
image: registry.itzana.me/strafesnet/auth-service:master
|
||||||
|
command: ["serve", "web"]
|
||||||
|
environment:
|
||||||
|
- REDIS_ADDR=authredis:6379
|
||||||
|
env_file:
|
||||||
|
- ../auth-compose/auth-service.env
|
||||||
|
depends_on:
|
||||||
|
- authredis
|
||||||
|
networks:
|
||||||
|
- maps-service-network
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis-data:
|
||||||
|
@ -241,9 +241,9 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Error"
|
$ref: "#/components/schemas/Error"
|
||||||
/submissions/{SubmissionID}/status/validate:
|
/submissions/{SubmissionID}/status/validator-validated:
|
||||||
post:
|
post:
|
||||||
summary: Role Validator changes status from Validating -> Validated
|
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
|
||||||
operationId: actionSubmissionValidate
|
operationId: actionSubmissionValidate
|
||||||
tags:
|
tags:
|
||||||
- Submissions
|
- Submissions
|
||||||
@ -258,9 +258,9 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Error"
|
$ref: "#/components/schemas/Error"
|
||||||
/submissions/{SubmissionID}/status/publish:
|
/submissions/{SubmissionID}/status/validator-published:
|
||||||
post:
|
post:
|
||||||
summary: Role Validator changes status from Publishing -> Published
|
summary: (Internal endpoint) Role Validator changes status from Publishing -> Published
|
||||||
operationId: actionSubmissionPublish
|
operationId: actionSubmissionPublish
|
||||||
tags:
|
tags:
|
||||||
- Submissions
|
- Submissions
|
||||||
|
@ -26,9 +26,9 @@ import (
|
|||||||
type Invoker interface {
|
type Invoker interface {
|
||||||
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Publishing -> Published.
|
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/publish
|
// POST /submissions/{SubmissionID}/status/validator-published
|
||||||
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
|
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
|
||||||
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
@ -68,9 +68,9 @@ type Invoker interface {
|
|||||||
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
|
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
|
||||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validate
|
// POST /submissions/{SubmissionID}/status/validator-validated
|
||||||
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error
|
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error
|
||||||
// CreateScript invokes createScript operation.
|
// CreateScript invokes createScript operation.
|
||||||
//
|
//
|
||||||
@ -214,9 +214,9 @@ func (c *Client) requestURL(ctx context.Context) *url.URL {
|
|||||||
|
|
||||||
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Publishing -> Published.
|
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/publish
|
// POST /submissions/{SubmissionID}/status/validator-published
|
||||||
func (c *Client) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
|
func (c *Client) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
|
||||||
_, err := c.sendActionSubmissionPublish(ctx, params)
|
_, err := c.sendActionSubmissionPublish(ctx, params)
|
||||||
return err
|
return err
|
||||||
@ -226,7 +226,7 @@ func (c *Client) sendActionSubmissionPublish(ctx context.Context, params ActionS
|
|||||||
otelAttrs := []attribute.KeyValue{
|
otelAttrs := []attribute.KeyValue{
|
||||||
otelogen.OperationID("actionSubmissionPublish"),
|
otelogen.OperationID("actionSubmissionPublish"),
|
||||||
semconv.HTTPRequestMethodKey.String("POST"),
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/publish"),
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-published"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run stopwatch.
|
// Run stopwatch.
|
||||||
@ -278,7 +278,7 @@ func (c *Client) sendActionSubmissionPublish(ctx context.Context, params ActionS
|
|||||||
}
|
}
|
||||||
pathParts[1] = encoded
|
pathParts[1] = encoded
|
||||||
}
|
}
|
||||||
pathParts[2] = "/status/publish"
|
pathParts[2] = "/status/validator-published"
|
||||||
uri.AddPathParts(u, pathParts[:]...)
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
stage = "EncodeRequest"
|
stage = "EncodeRequest"
|
||||||
@ -1049,9 +1049,9 @@ func (c *Client) sendActionSubmissionTriggerValidate(ctx context.Context, params
|
|||||||
|
|
||||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validate
|
// POST /submissions/{SubmissionID}/status/validator-validated
|
||||||
func (c *Client) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
|
func (c *Client) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
|
||||||
_, err := c.sendActionSubmissionValidate(ctx, params)
|
_, err := c.sendActionSubmissionValidate(ctx, params)
|
||||||
return err
|
return err
|
||||||
@ -1061,7 +1061,7 @@ func (c *Client) sendActionSubmissionValidate(ctx context.Context, params Action
|
|||||||
otelAttrs := []attribute.KeyValue{
|
otelAttrs := []attribute.KeyValue{
|
||||||
otelogen.OperationID("actionSubmissionValidate"),
|
otelogen.OperationID("actionSubmissionValidate"),
|
||||||
semconv.HTTPRequestMethodKey.String("POST"),
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validate"),
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-validated"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run stopwatch.
|
// Run stopwatch.
|
||||||
@ -1113,7 +1113,7 @@ func (c *Client) sendActionSubmissionValidate(ctx context.Context, params Action
|
|||||||
}
|
}
|
||||||
pathParts[1] = encoded
|
pathParts[1] = encoded
|
||||||
}
|
}
|
||||||
pathParts[2] = "/status/validate"
|
pathParts[2] = "/status/validator-validated"
|
||||||
uri.AddPathParts(u, pathParts[:]...)
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
stage = "EncodeRequest"
|
stage = "EncodeRequest"
|
||||||
|
@ -32,16 +32,16 @@ func (c *codeRecorder) WriteHeader(status int) {
|
|||||||
|
|
||||||
// handleActionSubmissionPublishRequest handles actionSubmissionPublish operation.
|
// handleActionSubmissionPublishRequest handles actionSubmissionPublish operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Publishing -> Published.
|
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/publish
|
// POST /submissions/{SubmissionID}/status/validator-published
|
||||||
func (s *Server) handleActionSubmissionPublishRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleActionSubmissionPublishRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
statusWriter := &codeRecorder{ResponseWriter: w}
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
w = statusWriter
|
w = statusWriter
|
||||||
otelAttrs := []attribute.KeyValue{
|
otelAttrs := []attribute.KeyValue{
|
||||||
otelogen.OperationID("actionSubmissionPublish"),
|
otelogen.OperationID("actionSubmissionPublish"),
|
||||||
semconv.HTTPRequestMethodKey.String("POST"),
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/publish"),
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-published"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a span for this request.
|
// Start a span for this request.
|
||||||
@ -120,7 +120,7 @@ func (s *Server) handleActionSubmissionPublishRequest(args [1]string, argsEscape
|
|||||||
mreq := middleware.Request{
|
mreq := middleware.Request{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
OperationName: ActionSubmissionPublishOperation,
|
OperationName: ActionSubmissionPublishOperation,
|
||||||
OperationSummary: "Role Validator changes status from Publishing -> Published",
|
OperationSummary: "(Internal endpoint) Role Validator changes status from Publishing -> Published",
|
||||||
OperationID: "actionSubmissionPublish",
|
OperationID: "actionSubmissionPublish",
|
||||||
Body: nil,
|
Body: nil,
|
||||||
Params: middleware.Parameters{
|
Params: middleware.Parameters{
|
||||||
@ -1351,16 +1351,16 @@ func (s *Server) handleActionSubmissionTriggerValidateRequest(args [1]string, ar
|
|||||||
|
|
||||||
// handleActionSubmissionValidateRequest handles actionSubmissionValidate operation.
|
// handleActionSubmissionValidateRequest handles actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validate
|
// POST /submissions/{SubmissionID}/status/validator-validated
|
||||||
func (s *Server) handleActionSubmissionValidateRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleActionSubmissionValidateRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
statusWriter := &codeRecorder{ResponseWriter: w}
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
w = statusWriter
|
w = statusWriter
|
||||||
otelAttrs := []attribute.KeyValue{
|
otelAttrs := []attribute.KeyValue{
|
||||||
otelogen.OperationID("actionSubmissionValidate"),
|
otelogen.OperationID("actionSubmissionValidate"),
|
||||||
semconv.HTTPRequestMethodKey.String("POST"),
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validate"),
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-validated"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a span for this request.
|
// Start a span for this request.
|
||||||
@ -1439,7 +1439,7 @@ func (s *Server) handleActionSubmissionValidateRequest(args [1]string, argsEscap
|
|||||||
mreq := middleware.Request{
|
mreq := middleware.Request{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
OperationName: ActionSubmissionValidateOperation,
|
OperationName: ActionSubmissionValidateOperation,
|
||||||
OperationSummary: "Role Validator changes status from Validating -> Validated",
|
OperationSummary: "(Internal endpoint) Role Validator changes status from Validating -> Validated",
|
||||||
OperationID: "actionSubmissionValidate",
|
OperationID: "actionSubmissionValidate",
|
||||||
Body: nil,
|
Body: nil,
|
||||||
Params: middleware.Parameters{
|
Params: middleware.Parameters{
|
||||||
|
@ -356,29 +356,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
case 'p': // Prefix: "publish"
|
|
||||||
origElem := elem
|
|
||||||
if l := len("publish"); len(elem) >= l && elem[0:l] == "publish" {
|
|
||||||
elem = elem[l:]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(elem) == 0 {
|
|
||||||
// Leaf node.
|
|
||||||
switch r.Method {
|
|
||||||
case "POST":
|
|
||||||
s.handleActionSubmissionPublishRequest([1]string{
|
|
||||||
args[0],
|
|
||||||
}, elemIsEscaped, w, r)
|
|
||||||
default:
|
|
||||||
s.notAllowed(w, r, "POST")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
elem = origElem
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
origElem := elem
|
origElem := elem
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
@ -547,26 +524,64 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
elem = origElem
|
elem = origElem
|
||||||
case 'v': // Prefix: "validate"
|
case 'v': // Prefix: "validator-"
|
||||||
origElem := elem
|
origElem := elem
|
||||||
if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" {
|
if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
|
||||||
elem = elem[l:]
|
elem = elem[l:]
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(elem) == 0 {
|
if len(elem) == 0 {
|
||||||
// Leaf node.
|
break
|
||||||
switch r.Method {
|
}
|
||||||
case "POST":
|
switch elem[0] {
|
||||||
s.handleActionSubmissionValidateRequest([1]string{
|
case 'p': // Prefix: "published"
|
||||||
args[0],
|
origElem := elem
|
||||||
}, elemIsEscaped, w, r)
|
if l := len("published"); len(elem) >= l && elem[0:l] == "published" {
|
||||||
default:
|
elem = elem[l:]
|
||||||
s.notAllowed(w, r, "POST")
|
} else {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionSubmissionPublishRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = origElem
|
||||||
|
case 'v': // Prefix: "validated"
|
||||||
|
origElem := elem
|
||||||
|
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionSubmissionValidateRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = origElem
|
||||||
}
|
}
|
||||||
|
|
||||||
elem = origElem
|
elem = origElem
|
||||||
@ -1018,31 +1033,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
case 'p': // Prefix: "publish"
|
|
||||||
origElem := elem
|
|
||||||
if l := len("publish"); len(elem) >= l && elem[0:l] == "publish" {
|
|
||||||
elem = elem[l:]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(elem) == 0 {
|
|
||||||
// Leaf node.
|
|
||||||
switch method {
|
|
||||||
case "POST":
|
|
||||||
r.name = ActionSubmissionPublishOperation
|
|
||||||
r.summary = "Role Validator changes status from Publishing -> Published"
|
|
||||||
r.operationID = "actionSubmissionPublish"
|
|
||||||
r.pathPattern = "/submissions/{SubmissionID}/status/publish"
|
|
||||||
r.args = args
|
|
||||||
r.count = 1
|
|
||||||
return r, true
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
elem = origElem
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
origElem := elem
|
origElem := elem
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
@ -1223,28 +1213,68 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
elem = origElem
|
elem = origElem
|
||||||
case 'v': // Prefix: "validate"
|
case 'v': // Prefix: "validator-"
|
||||||
origElem := elem
|
origElem := elem
|
||||||
if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" {
|
if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
|
||||||
elem = elem[l:]
|
elem = elem[l:]
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(elem) == 0 {
|
if len(elem) == 0 {
|
||||||
// Leaf node.
|
break
|
||||||
switch method {
|
}
|
||||||
case "POST":
|
switch elem[0] {
|
||||||
r.name = ActionSubmissionValidateOperation
|
case 'p': // Prefix: "published"
|
||||||
r.summary = "Role Validator changes status from Validating -> Validated"
|
origElem := elem
|
||||||
r.operationID = "actionSubmissionValidate"
|
if l := len("published"); len(elem) >= l && elem[0:l] == "published" {
|
||||||
r.pathPattern = "/submissions/{SubmissionID}/status/validate"
|
elem = elem[l:]
|
||||||
r.args = args
|
} else {
|
||||||
r.count = 1
|
break
|
||||||
return r, true
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionSubmissionPublishOperation
|
||||||
|
r.summary = "(Internal endpoint) Role Validator changes status from Publishing -> Published"
|
||||||
|
r.operationID = "actionSubmissionPublish"
|
||||||
|
r.pathPattern = "/submissions/{SubmissionID}/status/validator-published"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = origElem
|
||||||
|
case 'v': // Prefix: "validated"
|
||||||
|
origElem := elem
|
||||||
|
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionSubmissionValidateOperation
|
||||||
|
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Validated"
|
||||||
|
r.operationID = "actionSubmissionValidate"
|
||||||
|
r.pathPattern = "/submissions/{SubmissionID}/status/validator-validated"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = origElem
|
||||||
}
|
}
|
||||||
|
|
||||||
elem = origElem
|
elem = origElem
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
type Handler interface {
|
type Handler interface {
|
||||||
// ActionSubmissionPublish implements actionSubmissionPublish operation.
|
// ActionSubmissionPublish implements actionSubmissionPublish operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Publishing -> Published.
|
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/publish
|
// POST /submissions/{SubmissionID}/status/validator-published
|
||||||
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
|
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
|
||||||
// ActionSubmissionReject implements actionSubmissionReject operation.
|
// ActionSubmissionReject implements actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
@ -52,9 +52,9 @@ type Handler interface {
|
|||||||
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
|
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
|
||||||
// ActionSubmissionValidate implements actionSubmissionValidate operation.
|
// ActionSubmissionValidate implements actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validate
|
// POST /submissions/{SubmissionID}/status/validator-validated
|
||||||
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error
|
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error
|
||||||
// CreateScript implements createScript operation.
|
// CreateScript implements createScript operation.
|
||||||
//
|
//
|
||||||
|
@ -15,9 +15,9 @@ var _ Handler = UnimplementedHandler{}
|
|||||||
|
|
||||||
// ActionSubmissionPublish implements actionSubmissionPublish operation.
|
// ActionSubmissionPublish implements actionSubmissionPublish operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Publishing -> Published.
|
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/publish
|
// POST /submissions/{SubmissionID}/status/validator-published
|
||||||
func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
|
func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
|
||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
@ -78,9 +78,9 @@ func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context,
|
|||||||
|
|
||||||
// ActionSubmissionValidate implements actionSubmissionValidate operation.
|
// ActionSubmissionValidate implements actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
|
||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validate
|
// POST /submissions/{SubmissionID}/status/validator-validated
|
||||||
func (UnimplementedHandler) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
|
func (UnimplementedHandler) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
|
||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,16 @@ package cmds
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.itzana.me/strafesnet/go-grpc/auth"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/service"
|
"git.itzana.me/strafesnet/maps-service/pkg/service"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"net/http"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"git.itzana.me/strafesnet/go-grpc/auth"
|
"net/http"
|
||||||
"github.com/nats-io/nats.go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServeCommand() *cli.Command {
|
func NewServeCommand() *cli.Command {
|
||||||
@ -87,9 +87,23 @@ func serve(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Fatal("failed to connect nats")
|
log.WithError(err).Fatal("failed to connect nats")
|
||||||
}
|
}
|
||||||
|
js, err := nc.JetStream()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Fatal("failed to start jetstream")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.AddStream(&nats.StreamConfig{
|
||||||
|
Name: "maptest",
|
||||||
|
Subjects: []string{"maptest.>"},
|
||||||
|
Retention: nats.WorkQueuePolicy,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Fatal("failed to add stream")
|
||||||
|
}
|
||||||
|
|
||||||
svc := &service.Service{
|
svc := &service.Service{
|
||||||
DB: db,
|
DB: db,
|
||||||
Nats: nc,
|
Nats: js,
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
conn, err := grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
@ -7,7 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNotExist = errors.New("resource does not exist")
|
ErrNotExist = errors.New("resource does not exist")
|
||||||
|
ErroNoRowsAffected = errors.New("query did not affect any rows")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Datastore interface {
|
type Datastore interface {
|
||||||
|
@ -53,7 +53,7 @@ func (env *Scripts) Update(ctx context.Context, id int64, values datastore.Optio
|
|||||||
|
|
||||||
// the update can only occur if the status matches one of the provided values.
|
// the update can only occur if the status matches one of the provided values.
|
||||||
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
|
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
|
||||||
if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?",statuses).Updates(values.Map()).Error; err != nil {
|
if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?", statuses).Updates(values.Map()).Error; err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
return datastore.ErrNotExist
|
return datastore.ErrNotExist
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,6 @@ import (
|
|||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorStatus = errors.New("Status is not in allowed statuses")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Submissions struct {
|
type Submissions struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
@ -58,7 +54,7 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
|
|||||||
|
|
||||||
// the update can only occur if the status matches one of the provided values.
|
// the update can only occur if the status matches one of the provided values.
|
||||||
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
|
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
|
||||||
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?",statuses).Updates(values.Map()).Error; err != nil {
|
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
return datastore.ErrNotExist
|
return datastore.ErrNotExist
|
||||||
}
|
}
|
||||||
@ -75,7 +71,7 @@ func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64,
|
|||||||
result := env.db.Model(&submission).
|
result := env.db.Model(&submission).
|
||||||
Clauses(clause.Returning{}).
|
Clauses(clause.Returning{}).
|
||||||
Where("id = ?", id).
|
Where("id = ?", id).
|
||||||
Where("status_id IN ?",statuses).
|
Where("status_id IN ?", statuses).
|
||||||
Updates(values.Map())
|
Updates(values.Map())
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
if result.Error == gorm.ErrRecordNotFound {
|
if result.Error == gorm.ErrRecordNotFound {
|
||||||
@ -85,7 +81,7 @@ func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if result.RowsAffected == 0 {
|
if result.RowsAffected == 0 {
|
||||||
return submission, ErrorStatus
|
return submission, datastore.ErroNoRowsAffected
|
||||||
}
|
}
|
||||||
|
|
||||||
return submission, nil
|
return submission, nil
|
||||||
|
@ -5,7 +5,7 @@ package model
|
|||||||
|
|
||||||
// Requests are sent from maps-service to validator
|
// Requests are sent from maps-service to validator
|
||||||
|
|
||||||
type ValidateRequest struct{
|
type ValidateRequest struct {
|
||||||
// submission_id is passed back in the response message
|
// submission_id is passed back in the response message
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
ModelID uint64
|
ModelID uint64
|
||||||
@ -14,16 +14,17 @@ type ValidateRequest struct{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new map
|
// Create a new map
|
||||||
type PublishNewRequest struct{
|
type PublishNewRequest struct {
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
ModelID uint64
|
ModelID uint64
|
||||||
ModelVersion uint64
|
ModelVersion uint64
|
||||||
Creator string
|
Creator string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
GameID uint32
|
||||||
//games HashSet<GameID>
|
//games HashSet<GameID>
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublishFixRequest struct{
|
type PublishFixRequest struct {
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
ModelID uint64
|
ModelID uint64
|
||||||
ModelVersion uint64
|
ModelVersion uint64
|
||||||
|
@ -4,15 +4,15 @@ import "time"
|
|||||||
|
|
||||||
type Policy int32
|
type Policy int32
|
||||||
|
|
||||||
const(
|
const (
|
||||||
ScriptPolicyAllowed Policy=0
|
ScriptPolicyAllowed Policy = 0
|
||||||
ScriptPolicyBlocked Policy=1
|
ScriptPolicyBlocked Policy = 1
|
||||||
ScriptPolicyDelete Policy=2
|
ScriptPolicyDelete Policy = 2
|
||||||
ScriptPolicyReplace Policy=3
|
ScriptPolicyReplace Policy = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type ScriptPolicy struct {
|
type ScriptPolicy struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
// Hash of the source code that leads to this policy.
|
// Hash of the source code that leads to this policy.
|
||||||
// If this is a replacement mapping, the original source may not be pointed to by any policy.
|
// If this is a replacement mapping, the original source may not be pointed to by any policy.
|
||||||
// The original source should still exist in the scripts table, which can be located by the same hash.
|
// The original source should still exist in the scripts table, which can be located by the same hash.
|
||||||
@ -20,8 +20,8 @@ type ScriptPolicy struct {
|
|||||||
// The ID of the replacement source (ScriptPolicyReplace)
|
// The ID of the replacement source (ScriptPolicyReplace)
|
||||||
// or verbatim source (ScriptPolicyAllowed)
|
// or verbatim source (ScriptPolicyAllowed)
|
||||||
// or 0 (other)
|
// or 0 (other)
|
||||||
ToScriptID int64
|
ToScriptID int64
|
||||||
Policy Policy
|
Policy Policy
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ package model
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Script struct {
|
type Script struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
Hash uint64
|
Hash uint64
|
||||||
Source string
|
Source string
|
||||||
SubmissionID int64 // which submission did this script first appear in
|
SubmissionID int64 // which submission did this script first appear in
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
@ -4,31 +4,31 @@ import "time"
|
|||||||
|
|
||||||
type Status int32
|
type Status int32
|
||||||
|
|
||||||
const(
|
const (
|
||||||
StatusPublished Status=8
|
StatusPublished Status = 8
|
||||||
StatusRejected Status=7
|
StatusRejected Status = 7
|
||||||
|
|
||||||
StatusPublishing Status=6
|
StatusPublishing Status = 6
|
||||||
StatusValidated Status=5
|
StatusValidated Status = 5
|
||||||
StatusValidating Status=4
|
StatusValidating Status = 4
|
||||||
StatusAccepted Status=3
|
StatusAccepted Status = 3
|
||||||
|
|
||||||
StatusChangesRequested Status=2
|
StatusChangesRequested Status = 2
|
||||||
StatusSubmitted Status=1
|
StatusSubmitted Status = 1
|
||||||
StatusUnderConstruction Status=0
|
StatusUnderConstruction Status = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
type Submission struct {
|
type Submission struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
DisplayName string
|
DisplayName string
|
||||||
Creator string
|
Creator string
|
||||||
GameID int32
|
GameID int32
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
Submitter uint64 // UserID
|
Submitter uint64 // UserID
|
||||||
AssetID uint64
|
AssetID uint64
|
||||||
AssetVersion uint64
|
AssetVersion uint64
|
||||||
Completed bool // Has this version of the map been completed at least once on maptest
|
Completed bool // Has this version of the map been completed at least once on maptest
|
||||||
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
||||||
StatusID Status
|
StatusID Status
|
||||||
}
|
}
|
||||||
|
@ -15,18 +15,18 @@ import (
|
|||||||
// Create a new script policy.
|
// Create a new script policy.
|
||||||
//
|
//
|
||||||
// POST /script-policy
|
// POST /script-policy
|
||||||
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error){
|
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return nil, ErrPermissionDenied
|
return nil, ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
from_script, err := svc.DB.Scripts().Get(ctx,req.FromScriptID)
|
from_script, err := svc.DB.Scripts().Get(ctx, req.FromScriptID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,64 +38,67 @@ func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolic
|
|||||||
ToScriptID: req.ToScriptID,
|
ToScriptID: req.ToScriptID,
|
||||||
Policy: model.Policy(req.Policy),
|
Policy: model.Policy(req.Policy),
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ID{
|
return &api.ID{
|
||||||
ID:script.ID,
|
ID: script.ID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteScriptPolicy implements deleteScriptPolicy operation.
|
// DeleteScriptPolicy implements deleteScriptPolicy operation.
|
||||||
//
|
//
|
||||||
// Delete the specified script policy by ID.
|
// Delete the specified script policy by ID.
|
||||||
//
|
//
|
||||||
// DELETE /script-policy/id/{ScriptPolicyID}
|
// DELETE /script-policy/id/{ScriptPolicyID}
|
||||||
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error{
|
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID)
|
return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScriptPolicy implements getScriptPolicy operation.
|
// GetScriptPolicy implements getScriptPolicy operation.
|
||||||
//
|
//
|
||||||
// Get the specified script policy by ID.
|
// Get the specified script policy by ID.
|
||||||
//
|
//
|
||||||
// GET /script-policy/id/{ScriptPolicyID}
|
// GET /script-policy/id/{ScriptPolicyID}
|
||||||
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error){
|
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
|
||||||
_, ok := ctx.Value("UserInfo").(UserInfo)
|
_, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read permission for script policy only requires you to be logged in
|
// Read permission for script policy only requires you to be logged in
|
||||||
|
|
||||||
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
|
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ScriptPolicy{
|
return &api.ScriptPolicy{
|
||||||
ID: policy.ID,
|
ID: policy.ID,
|
||||||
FromScriptHash: fmt.Sprintf("%x",policy.FromScriptHash),
|
FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
|
||||||
ToScriptID: policy.ToScriptID,
|
ToScriptID: policy.ToScriptID,
|
||||||
Policy: int32(policy.Policy),
|
Policy: int32(policy.Policy),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
|
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
|
||||||
//
|
//
|
||||||
// Get the policy for the given hash of script source code.
|
// Get the policy for the given hash of script source code.
|
||||||
//
|
//
|
||||||
// GET /script-policy/hash/{FromScriptHash}
|
// GET /script-policy/hash/{FromScriptHash}
|
||||||
func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetScriptPolicyFromHashParams) (*api.ScriptPolicy, error){
|
func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetScriptPolicyFromHashParams) (*api.ScriptPolicy, error) {
|
||||||
_, ok := ctx.Value("UserInfo").(UserInfo)
|
_, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,50 +106,51 @@ func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetS
|
|||||||
|
|
||||||
// parse hash from hex
|
// parse hash from hex
|
||||||
hash, err := strconv.ParseUint(params.FromScriptHash, 16, 64)
|
hash, err := strconv.ParseUint(params.FromScriptHash, 16, 64)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
policy, err := svc.DB.ScriptPolicy().GetFromHash(ctx, hash)
|
policy, err := svc.DB.ScriptPolicy().GetFromHash(ctx, hash)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ScriptPolicy{
|
return &api.ScriptPolicy{
|
||||||
ID: policy.ID,
|
ID: policy.ID,
|
||||||
FromScriptHash: fmt.Sprintf("%x",policy.FromScriptHash),
|
FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
|
||||||
ToScriptID: policy.ToScriptID,
|
ToScriptID: policy.ToScriptID,
|
||||||
Policy: int32(policy.Policy),
|
Policy: int32(policy.Policy),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateScriptPolicy implements updateScriptPolicy operation.
|
// UpdateScriptPolicy implements updateScriptPolicy operation.
|
||||||
//
|
//
|
||||||
// Update the specified script policy by ID.
|
// Update the specified script policy by ID.
|
||||||
//
|
//
|
||||||
// PATCH /script-policy/id/{ScriptPolicyID}
|
// PATCH /script-policy/id/{ScriptPolicyID}
|
||||||
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error{
|
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
pmap := datastore.Optional()
|
pmap := datastore.Optional()
|
||||||
if from_script_id,ok:=req.FromScriptID.Get();ok{
|
if from_script_id, ok := req.FromScriptID.Get(); ok {
|
||||||
from_script, err := svc.DB.Scripts().Get(ctx,from_script_id)
|
from_script, err := svc.DB.Scripts().Get(ctx, from_script_id)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pmap.Add("from_script_hash",from_script.Hash)
|
pmap.Add("from_script_hash", from_script.Hash)
|
||||||
}
|
}
|
||||||
if to_script_id,ok:=req.ToScriptID.Get();ok{
|
if to_script_id, ok := req.ToScriptID.Get(); ok {
|
||||||
pmap.Add("to_script_id",to_script_id)
|
pmap.Add("to_script_id", to_script_id)
|
||||||
}
|
}
|
||||||
if policy,ok:=req.Policy.Get();ok{
|
if policy, ok := req.Policy.Get(); ok {
|
||||||
pmap.Add("policy",policy)
|
pmap.Add("policy", policy)
|
||||||
}
|
}
|
||||||
return svc.DB.ScriptPolicy().Update(ctx, req.ID, pmap)
|
return svc.DB.ScriptPolicy().Update(ctx, req.ID, pmap)
|
||||||
}
|
}
|
||||||
|
@ -15,94 +15,97 @@ import (
|
|||||||
// Create a new script.
|
// Create a new script.
|
||||||
//
|
//
|
||||||
// POST /scripts
|
// POST /scripts
|
||||||
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error){
|
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return nil, ErrPermissionDenied
|
return nil, ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
script, err := svc.DB.Scripts().Create(ctx, model.Script{
|
script, err := svc.DB.Scripts().Create(ctx, model.Script{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Hash: siphash.Hash(0,0,[]byte(req.Source)),
|
Hash: siphash.Hash(0, 0, []byte(req.Source)),
|
||||||
Source: req.Source,
|
Source: req.Source,
|
||||||
SubmissionID: req.SubmissionID.Or(0),
|
SubmissionID: req.SubmissionID.Or(0),
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ID{
|
return &api.ID{
|
||||||
ID:script.ID,
|
ID: script.ID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteScript implements deleteScript operation.
|
// DeleteScript implements deleteScript operation.
|
||||||
//
|
//
|
||||||
// Delete the specified script by ID.
|
// Delete the specified script by ID.
|
||||||
//
|
//
|
||||||
// DELETE /scripts/{ScriptID}
|
// DELETE /scripts/{ScriptID}
|
||||||
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error{
|
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc.DB.Scripts().Delete(ctx, params.ScriptID)
|
return svc.DB.Scripts().Delete(ctx, params.ScriptID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScript implements getScript operation.
|
// GetScript implements getScript operation.
|
||||||
//
|
//
|
||||||
// Get the specified script by ID.
|
// Get the specified script by ID.
|
||||||
//
|
//
|
||||||
// GET /scripts/{ScriptID}
|
// GET /scripts/{ScriptID}
|
||||||
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error){
|
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
|
||||||
_, ok := ctx.Value("UserInfo").(UserInfo)
|
_, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read permission for scripts only requires you to be logged in
|
// Read permission for scripts only requires you to be logged in
|
||||||
|
|
||||||
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
|
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.Script{
|
return &api.Script{
|
||||||
ID: script.ID,
|
ID: script.ID,
|
||||||
Hash: fmt.Sprintf("%x",script.Hash),
|
Hash: fmt.Sprintf("%x", script.Hash),
|
||||||
Source: script.Source,
|
Source: script.Source,
|
||||||
SubmissionID: script.SubmissionID,
|
SubmissionID: script.SubmissionID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateScript implements updateScript operation.
|
// UpdateScript implements updateScript operation.
|
||||||
//
|
//
|
||||||
// Update the specified script by ID.
|
// Update the specified script by ID.
|
||||||
//
|
//
|
||||||
// PATCH /scripts/{ScriptID}
|
// PATCH /scripts/{ScriptID}
|
||||||
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error{
|
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userInfo.Roles.ScriptWrite{
|
if !userInfo.Roles.ScriptWrite {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
pmap := datastore.Optional()
|
pmap := datastore.Optional()
|
||||||
if source,ok:=req.Source.Get();ok{
|
if source, ok := req.Source.Get(); ok {
|
||||||
pmap.Add("source",source)
|
pmap.Add("source", source)
|
||||||
pmap.Add("hash",siphash.Hash(0,0,[]byte(source)))
|
pmap.Add("hash", siphash.Hash(0, 0, []byte(source)))
|
||||||
}
|
}
|
||||||
if SubmissionID,ok:=req.SubmissionID.Get();ok{
|
if SubmissionID, ok := req.SubmissionID.Get(); ok {
|
||||||
pmap.Add("submission_id",SubmissionID)
|
pmap.Add("submission_id", SubmissionID)
|
||||||
}
|
}
|
||||||
return svc.DB.Scripts().Update(ctx, req.ID, pmap)
|
return svc.DB.Scripts().Update(ctx, req.ID, pmap)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"context"
|
"context"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"errors"
|
||||||
"git.itzana.me/strafesnet/go-grpc/auth"
|
"git.itzana.me/strafesnet/go-grpc/auth"
|
||||||
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -16,7 +16,7 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// has SubmissionPublish
|
// has SubmissionPublish
|
||||||
RoleMapAdmin int32 = 128
|
RoleMapAdmin int32 = 128
|
||||||
// has SubmissionReview
|
// has SubmissionReview
|
||||||
RoleMapCouncil int32 = 64
|
RoleMapCouncil int32 = 64
|
||||||
)
|
)
|
||||||
@ -24,19 +24,19 @@ var (
|
|||||||
type Roles struct {
|
type Roles struct {
|
||||||
// human roles
|
// human roles
|
||||||
SubmissionPublish bool
|
SubmissionPublish bool
|
||||||
SubmissionReview bool
|
SubmissionReview bool
|
||||||
ScriptWrite bool
|
ScriptWrite bool
|
||||||
// automated roles
|
// automated roles
|
||||||
Maptest bool
|
Maptest bool
|
||||||
Validator bool
|
Validator bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
Roles Roles
|
Roles Roles
|
||||||
UserID uint64
|
UserID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (usr UserInfo) IsSubmitter(submitter uint64) bool{
|
func (usr UserInfo) IsSubmitter(submitter uint64) bool {
|
||||||
return usr.UserID == submitter
|
return usr.UserID == submitter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ type SecurityHandler struct {
|
|||||||
Client auth.AuthServiceClient
|
Client auth.AuthServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error){
|
func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error) {
|
||||||
sessionId := t.GetAPIKey()
|
sessionId := t.GetAPIKey()
|
||||||
if sessionId == "" {
|
if sessionId == "" {
|
||||||
return nil, ErrMissingSessionID
|
return nil, ErrMissingSessionID
|
||||||
@ -53,41 +53,41 @@ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName a
|
|||||||
session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
|
session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
|
||||||
SessionID: sessionId,
|
SessionID: sessionId,
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{
|
role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{
|
||||||
SessionID: sessionId,
|
SessionID: sessionId,
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
|
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
|
||||||
SessionID: sessionId,
|
SessionID: sessionId,
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !validate.Valid{
|
if !validate.Valid {
|
||||||
return nil, ErrInvalidSession
|
return nil, ErrInvalidSession
|
||||||
}
|
}
|
||||||
|
|
||||||
roles := Roles{}
|
roles := Roles{}
|
||||||
|
|
||||||
// fix this when roblox udpates group roles
|
// fix this when roblox udpates group roles
|
||||||
for _,r := range role.Roles{
|
for _, r := range role.Roles {
|
||||||
if RoleMapAdmin<=r.Rank{
|
if RoleMapAdmin <= r.Rank {
|
||||||
roles.SubmissionPublish = true
|
roles.SubmissionPublish = true
|
||||||
}
|
}
|
||||||
if RoleMapCouncil<=r.Rank{
|
if RoleMapCouncil <= r.Rank {
|
||||||
roles.SubmissionReview = true
|
roles.SubmissionReview = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
|
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
|
||||||
Roles: roles,
|
Roles: roles,
|
||||||
UserID: session.UserID,
|
UserID: session.UserID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
DB datastore.Datastore
|
DB datastore.Datastore
|
||||||
Nats *nats.Conn
|
Nats nats.JetStreamContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewError creates *ErrorStatusCode from error returned by handler.
|
// NewError creates *ErrorStatusCode from error returned by handler.
|
||||||
@ -25,16 +25,16 @@ type Service struct {
|
|||||||
// Used for common default response.
|
// Used for common default response.
|
||||||
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
|
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
|
||||||
status := 500
|
status := 500
|
||||||
if errors.Is(err,ErrPermissionDenied){
|
if errors.Is(err, ErrPermissionDenied) {
|
||||||
status = 403
|
status = 403
|
||||||
}
|
}
|
||||||
if errors.Is(err,ErrUserInfo){
|
if errors.Is(err, ErrUserInfo) {
|
||||||
status = 401
|
status = 401
|
||||||
}
|
}
|
||||||
return &api.ErrorStatusCode{
|
return &api.ErrorStatusCode{
|
||||||
StatusCode: status,
|
StatusCode: status,
|
||||||
Response: api.Error{
|
Response: api.Error{
|
||||||
Code: int64(status),
|
Code: int64(status),
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,16 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrInvalidSourceStatus current submission status cannot change to destination status
|
|
||||||
ErrInvalidSourceStatus = errors.New("Invalid source status")
|
|
||||||
)
|
|
||||||
|
|
||||||
// POST /submissions
|
// POST /submissions
|
||||||
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
|
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return nil, ErrUserInfo
|
return nil, ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +28,11 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
|
|||||||
TargetAssetID: uint64(request.TargetAssetID.Value),
|
TargetAssetID: uint64(request.TargetAssetID.Value),
|
||||||
StatusID: model.StatusUnderConstruction,
|
StatusID: model.StatusUnderConstruction,
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &api.ID{
|
return &api.ID{
|
||||||
ID:submission.ID,
|
ID: submission.ID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,22 +43,22 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
|
|||||||
// GET /submissions/{SubmissionID}
|
// GET /submissions/{SubmissionID}
|
||||||
func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionParams) (*api.Submission, error) {
|
func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionParams) (*api.Submission, error) {
|
||||||
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &api.Submission{
|
return &api.Submission{
|
||||||
ID: submission.ID,
|
ID: submission.ID,
|
||||||
DisplayName: submission.DisplayName,
|
DisplayName: submission.DisplayName,
|
||||||
Creator: submission.Creator,
|
Creator: submission.Creator,
|
||||||
GameID: submission.GameID,
|
GameID: submission.GameID,
|
||||||
CreatedAt: submission.CreatedAt.Unix(),
|
CreatedAt: submission.CreatedAt.Unix(),
|
||||||
UpdatedAt: submission.UpdatedAt.Unix(),
|
UpdatedAt: submission.UpdatedAt.Unix(),
|
||||||
Submitter: int64(submission.Submitter),
|
Submitter: int64(submission.Submitter),
|
||||||
AssetID: int64(submission.AssetID),
|
AssetID: int64(submission.AssetID),
|
||||||
AssetVersion: int64(submission.AssetVersion),
|
AssetVersion: int64(submission.AssetVersion),
|
||||||
Completed: submission.Completed,
|
Completed: submission.Completed,
|
||||||
TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
|
TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
|
||||||
StatusID: int32(submission.StatusID),
|
StatusID: int32(submission.StatusID),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,25 +80,25 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
|
|||||||
Number: request.Page.GetPage(),
|
Number: request.Page.GetPage(),
|
||||||
Size: request.Page.GetLimit(),
|
Size: request.Page.GetLimit(),
|
||||||
})
|
})
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []api.Submission
|
var resp []api.Submission
|
||||||
for i := 0; i < len(items); i++ {
|
for i := 0; i < len(items); i++ {
|
||||||
resp = append(resp, api.Submission{
|
resp = append(resp, api.Submission{
|
||||||
ID: items[i].ID,
|
ID: items[i].ID,
|
||||||
DisplayName: items[i].DisplayName,
|
DisplayName: items[i].DisplayName,
|
||||||
Creator: items[i].Creator,
|
Creator: items[i].Creator,
|
||||||
GameID: items[i].GameID,
|
GameID: items[i].GameID,
|
||||||
CreatedAt: items[i].CreatedAt.Unix(),
|
CreatedAt: items[i].CreatedAt.Unix(),
|
||||||
UpdatedAt: items[i].UpdatedAt.Unix(),
|
UpdatedAt: items[i].UpdatedAt.Unix(),
|
||||||
Submitter: int64(items[i].Submitter),
|
Submitter: int64(items[i].Submitter),
|
||||||
AssetID: int64(items[i].AssetID),
|
AssetID: int64(items[i].AssetID),
|
||||||
AssetVersion: int64(items[i].AssetVersion),
|
AssetVersion: int64(items[i].AssetVersion),
|
||||||
Completed: items[i].Completed,
|
Completed: items[i].Completed,
|
||||||
TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
|
TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
|
||||||
StatusID: int32(items[i].StatusID),
|
StatusID: int32(items[i].StatusID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,12 +112,12 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
|
|||||||
// POST /submissions/{SubmissionID}/completed
|
// POST /submissions/{SubmissionID}/completed
|
||||||
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
|
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
|
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
|
||||||
if !userInfo.Roles.Maptest{
|
if !userInfo.Roles.Maptest {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,18 +134,18 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
|
|||||||
// POST /submissions/{SubmissionID}/model
|
// POST /submissions/{SubmissionID}/model
|
||||||
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
|
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// read submission (this could be done with a transaction WHERE clause)
|
// read submission (this could be done with a transaction WHERE clause)
|
||||||
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !userInfo.IsSubmitter(submission.Submitter){
|
if !userInfo.IsSubmitter(submission.Submitter) {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +154,8 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
|
|||||||
pmap.AddNotNil("asset_id", params.ModelID)
|
pmap.AddNotNil("asset_id", params.ModelID)
|
||||||
pmap.AddNotNil("asset_version", params.VersionID)
|
pmap.AddNotNil("asset_version", params.VersionID)
|
||||||
//always reset completed when model changes
|
//always reset completed when model changes
|
||||||
pmap.Add("completed",false)
|
pmap.Add("completed", false)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested,model.StatusSubmitted,model.StatusUnderConstruction}, pmap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested, model.StatusSubmitted, model.StatusUnderConstruction}, pmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
|
||||||
@ -174,9 +168,10 @@ func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.Acti
|
|||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusPublished)
|
smap.Add("status_id", model.StatusPublished)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@ -184,20 +179,21 @@ func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.Acti
|
|||||||
// POST /submissions/{SubmissionID}/status/reject
|
// POST /submissions/{SubmissionID}/status/reject
|
||||||
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
|
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !userInfo.Roles.SubmissionReview{
|
if !userInfo.Roles.SubmissionReview {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusRejected)
|
smap.Add("status_id", model.StatusRejected)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
|
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
|
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
|
||||||
@ -205,20 +201,21 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
|
|||||||
// POST /submissions/{SubmissionID}/status/request-changes
|
// POST /submissions/{SubmissionID}/status/request-changes
|
||||||
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
|
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !userInfo.Roles.SubmissionReview{
|
if !userInfo.Roles.SubmissionReview {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusChangesRequested)
|
smap.Add("status_id", model.StatusChangesRequested)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated,model.StatusAccepted,model.StatusSubmitted}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated, model.StatusAccepted, model.StatusSubmitted}, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
|
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
|
||||||
//
|
//
|
||||||
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
|
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
|
||||||
@ -226,26 +223,27 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
|
|||||||
// POST /submissions/{SubmissionID}/status/revoke
|
// POST /submissions/{SubmissionID}/status/revoke
|
||||||
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
|
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// read submission (this could be done with a transaction WHERE clause)
|
// read submission (this could be done with a transaction WHERE clause)
|
||||||
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !userInfo.IsSubmitter(submission.Submitter){
|
if !userInfo.IsSubmitter(submission.Submitter) {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusUnderConstruction)
|
smap.Add("status_id", model.StatusUnderConstruction)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusChangesRequested}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
|
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
|
||||||
//
|
//
|
||||||
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
|
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
|
||||||
@ -253,26 +251,27 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
|
|||||||
// POST /submissions/{SubmissionID}/status/submit
|
// POST /submissions/{SubmissionID}/status/submit
|
||||||
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
|
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// read submission (this could be done with a transaction WHERE clause)
|
// read submission (this could be done with a transaction WHERE clause)
|
||||||
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !userInfo.IsSubmitter(submission.Submitter){
|
if !userInfo.IsSubmitter(submission.Submitter) {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusSubmitted)
|
smap.Add("status_id", model.StatusSubmitted)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction,model.StatusChangesRequested}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation.
|
// ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation.
|
||||||
//
|
//
|
||||||
// Role Admin changes status from Validated -> Publishing.
|
// Role Admin changes status from Validated -> Publishing.
|
||||||
@ -280,25 +279,25 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
|
|||||||
// POST /submissions/{SubmissionID}/status/trigger-publish
|
// POST /submissions/{SubmissionID}/status/trigger-publish
|
||||||
func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error {
|
func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !userInfo.Roles.SubmissionPublish{
|
if !userInfo.Roles.SubmissionPublish {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusPublishing)
|
smap.Add("status_id", model.StatusPublishing)
|
||||||
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
|
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// sentinel value because we are not using rust
|
// sentinel value because we are not using rust
|
||||||
if submission.TargetAssetID == 0{
|
if submission.TargetAssetID == 0 {
|
||||||
// this is a new map
|
// this is a new map
|
||||||
publish_new_request := model.PublishNewRequest{
|
publish_new_request := model.PublishNewRequest{
|
||||||
SubmissionID: submission.ID,
|
SubmissionID: submission.ID,
|
||||||
@ -306,15 +305,16 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
|
|||||||
ModelVersion: submission.AssetVersion,
|
ModelVersion: submission.AssetVersion,
|
||||||
Creator: submission.Creator,
|
Creator: submission.Creator,
|
||||||
DisplayName: submission.DisplayName,
|
DisplayName: submission.DisplayName,
|
||||||
|
GameID: uint32(submission.GameID),
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(publish_new_request)
|
j, err := json.Marshal(publish_new_request)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.Nats.Publish("publish_new", []byte(j))
|
svc.Nats.Publish("maptest.submissions.publishnew", []byte(j))
|
||||||
}else{
|
} else {
|
||||||
// this is a map fix
|
// this is a map fix
|
||||||
publish_fix_request := model.PublishFixRequest{
|
publish_fix_request := model.PublishFixRequest{
|
||||||
SubmissionID: submission.ID,
|
SubmissionID: submission.ID,
|
||||||
@ -324,15 +324,16 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
|
|||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(publish_fix_request)
|
j, err := json.Marshal(publish_fix_request)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.Nats.Publish("publish_fix", []byte(j))
|
svc.Nats.Publish("maptest.submissions.publishfix", []byte(j))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
|
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
|
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
|
||||||
@ -340,20 +341,20 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
|
|||||||
// POST /submissions/{SubmissionID}/status/trigger-validate
|
// POST /submissions/{SubmissionID}/status/trigger-validate
|
||||||
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
|
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
|
||||||
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
|
||||||
if !ok{
|
if !ok {
|
||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !userInfo.Roles.SubmissionReview{
|
if !userInfo.Roles.SubmissionReview {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusValidating)
|
smap.Add("status_id", model.StatusValidating)
|
||||||
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusAccepted}, smap)
|
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusAccepted}, smap)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,14 +366,15 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
|
|||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(validate_request)
|
j, err := json.Marshal(validate_request)
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.Nats.Publish("validate", []byte(j))
|
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// Role Validator changes status from Validating -> Validated.
|
||||||
@ -383,6 +385,6 @@ func (svc *Service) ActionSubmissionValidate(ctx context.Context, params api.Act
|
|||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id",model.StatusValidated)
|
smap.Add("status_id", model.StatusValidated)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
|
||||||
}
|
}
|
||||||
|
2
validation/.cargo/config.toml
Normal file
2
validation/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[registries.strafesnet]
|
||||||
|
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
400
validation/Cargo.lock
generated
400
validation/Cargo.lock
generated
@ -41,6 +41,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api"
|
name = "api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -99,6 +105,39 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream-impl",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream-impl"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@ -111,6 +150,53 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum"
|
||||||
|
version = "0.7.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"itoa",
|
||||||
|
"matchit",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustversion",
|
||||||
|
"serde",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower 0.5.2",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-core"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"mime",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustversion",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.74"
|
version = "0.3.74"
|
||||||
@ -201,9 +287,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.2"
|
version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
|
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@ -216,9 +302,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.38"
|
version = "0.4.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
@ -391,6 +477,12 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@ -418,9 +510,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fiat-crypto"
|
name = "fiat-crypto"
|
||||||
@ -596,13 +688,19 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap 2.7.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.2"
|
||||||
@ -611,9 +709,9 @@ checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
@ -649,6 +747,12 @@ version = "1.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
|
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
@ -662,6 +766,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -686,6 +791,19 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-timeout"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
|
||||||
|
dependencies = [
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -883,6 +1001,16 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
@ -890,7 +1018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -899,6 +1027,15 @@ version = "2.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
|
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
@ -907,9 +1044,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.74"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
|
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@ -923,9 +1060,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.167"
|
version = "0.2.168"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@ -976,12 +1113,20 @@ dependencies = [
|
|||||||
"rbx_dom_weak",
|
"rbx_dom_weak",
|
||||||
"rbx_reflection_database",
|
"rbx_reflection_database",
|
||||||
"rbx_xml",
|
"rbx_xml",
|
||||||
|
"rust-grpc",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tonic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@ -1257,6 +1402,40 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"prost-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-derive"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"itertools",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-types"
|
||||||
|
version = "0.13.3-serde1"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "8819b8aaed4d56560ff473ee45247539098b6fba5c8748a429903c499d5bc4a9"
|
||||||
|
dependencies = [
|
||||||
|
"prost",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
@ -1298,9 +1477,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_asset"
|
name = "rbx_asset"
|
||||||
version = "0.2.3"
|
version = "0.2.5"
|
||||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
checksum = "15542e7184e4b18e578308308cee72abcb2bfcb1d6c162f1eea50ec6cff7e3ac"
|
checksum = "dcf243f46bd41b3880a27278177a3f9996f95ab231d9a04345ad9dd381c3a54a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"flate2",
|
"flate2",
|
||||||
@ -1497,6 +1676,18 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-grpc"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "9d241d8d3ce1f24ce54ead69478676d138526bc0d44bb51bea2d87e5d8e3af22"
|
||||||
|
dependencies = [
|
||||||
|
"prost",
|
||||||
|
"prost-types",
|
||||||
|
"serde",
|
||||||
|
"tonic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@ -1514,22 +1705,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.41"
|
version = "0.38.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.19"
|
version = "0.23.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
@ -1575,9 +1766,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.10.0"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
@ -1590,6 +1781,12 @@ dependencies = [
|
|||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -1643,24 +1840,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.215"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.215"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1728,6 +1925,15 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signatory"
|
name = "signatory"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
@ -1896,9 +2102,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.36"
|
version = "0.3.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
@ -1917,9 +2123,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.18"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
@ -1937,15 +2143,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.41.1"
|
version = "1.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@ -1974,20 +2181,30 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.0"
|
version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.12"
|
version = "0.7.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -2017,6 +2234,76 @@ dependencies = [
|
|||||||
"tokio-util",
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tonic"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
|
"async-trait",
|
||||||
|
"axum",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytes",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-timeout",
|
||||||
|
"hyper-util",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project",
|
||||||
|
"prost",
|
||||||
|
"socket2",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tower 0.4.13",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -2147,9 +2434,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.97"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
|
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -2158,13 +2445,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.97"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
|
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@ -2173,9 +2459,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.47"
|
version = "0.4.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d"
|
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@ -2186,9 +2472,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.97"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
|
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@ -2196,9 +2482,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.97"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
|
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2209,15 +2495,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.97"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.74"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c"
|
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@ -2358,9 +2644,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.23"
|
version = "0.8.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f"
|
checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
|
@ -7,12 +7,14 @@ edition = "2021"
|
|||||||
api = { path = "api" }
|
api = { path = "api" }
|
||||||
async-nats = "0.38.0"
|
async-nats = "0.38.0"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
rbx_asset = { version = "0.2.3", registry = "strafesnet" }
|
rbx_asset = { version = "0.2.5", registry = "strafesnet" }
|
||||||
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
|
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
|
||||||
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
|
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
|
||||||
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
|
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
|
||||||
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
|
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
|
||||||
|
rust-grpc = { version = "1.0.3", registry = "strafesnet" }
|
||||||
serde = { version = "1.0.215", features = ["derive"] }
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
serde_json = "1.0.133"
|
serde_json = "1.0.133"
|
||||||
siphasher = "1.0.1"
|
siphasher = "1.0.1"
|
||||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "fs"] }
|
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "fs", "signal"] }
|
||||||
|
tonic = "0.12.3"
|
||||||
|
@ -11,6 +11,7 @@ RUN cargo chef prepare --recipe-path recipe.json
|
|||||||
|
|
||||||
FROM chef AS builder
|
FROM chef AS builder
|
||||||
COPY --from=planner /app/recipe.json recipe.json
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
COPY api ./api
|
||||||
# Notice that we are specifying the --target flag!
|
# Notice that we are specifying the --target flag!
|
||||||
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
|
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
|
||||||
COPY . .
|
COPY . .
|
||||||
|
@ -66,9 +66,9 @@ pub type ReqwestError=reqwest::Error;
|
|||||||
|
|
||||||
// there are lots of action endpoints and they all follow the same pattern
|
// there are lots of action endpoints and they all follow the same pattern
|
||||||
macro_rules! action{
|
macro_rules! action{
|
||||||
($fname:ident,$action:ident)=>{
|
($fname:ident,$action:expr)=>{
|
||||||
pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
|
pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
|
||||||
let url_raw=format!(concat!("{}/submissions/{}/status/",stringify!($action)),self.base_url,config.0);
|
let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.base_url,config.0);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
|
||||||
|
|
||||||
self.post(url).await.map_err(Error::Reqwest)?
|
self.post(url).await.map_err(Error::Reqwest)?
|
||||||
@ -125,6 +125,6 @@ impl Context{
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
action!(action_submission_validate,validate);
|
action!(action_submission_validate,"validator-validated");
|
||||||
action!(action_submission_publish,publish);
|
action!(action_submission_publish,"validator-published");
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
mod nats_types;
|
mod nats_types;
|
||||||
mod validator;
|
mod validator;
|
||||||
mod publish_new;
|
mod publish_new;
|
||||||
mod publish_fix;
|
mod publish_fix;
|
||||||
|
mod message_handler;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum StartupError{
|
pub enum StartupError{
|
||||||
API(api::ReqwestError),
|
API(api::ReqwestError),
|
||||||
NatsConnect(async_nats::ConnectError),
|
NatsConnect(async_nats::ConnectError),
|
||||||
NatsSubscribe(async_nats::SubscribeError),
|
NatsGetStream(async_nats::jetstream::context::GetStreamError),
|
||||||
|
NatsConsumer(async_nats::jetstream::stream::ConsumerError),
|
||||||
|
NatsStream(async_nats::jetstream::consumer::StreamError),
|
||||||
|
GRPCConnect(tonic::transport::Error),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for StartupError{
|
impl std::fmt::Display for StartupError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -17,8 +23,13 @@ impl std::fmt::Display for StartupError{
|
|||||||
}
|
}
|
||||||
impl std::error::Error for StartupError{}
|
impl std::error::Error for StartupError{}
|
||||||
|
|
||||||
|
// annoying mile-long type
|
||||||
|
pub type MapsServiceClient=rust_grpc::maps::maps_service_client::MapsServiceClient<tonic::transport::channel::Channel>;
|
||||||
|
|
||||||
pub const GROUP_STRAFESNET:u64=6980477;
|
pub const GROUP_STRAFESNET:u64=6980477;
|
||||||
|
|
||||||
|
pub const PARALLEL_REQUESTS:usize=16;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main()->Result<(),StartupError>{
|
async fn main()->Result<(),StartupError>{
|
||||||
// talk to roblox through STRAFESNET_CI2 account
|
// talk to roblox through STRAFESNET_CI2 account
|
||||||
@ -31,23 +42,57 @@ async fn main()->Result<(),StartupError>{
|
|||||||
|
|
||||||
// nats
|
// nats
|
||||||
let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required");
|
let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required");
|
||||||
let nasty=async_nats::connect(nats_host).await.map_err(StartupError::NatsConnect)?;
|
let nats_fut=async{
|
||||||
|
let nasty=async_nats::connect(nats_host).await.map_err(StartupError::NatsConnect)?;
|
||||||
|
// use nats jetstream
|
||||||
|
async_nats::jetstream::new(nasty)
|
||||||
|
.get_stream("maptest").await.map_err(StartupError::NatsGetStream)?
|
||||||
|
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
|
||||||
|
name:Some("validation".to_owned()),
|
||||||
|
durable_name:Some("validation".to_owned()),
|
||||||
|
filter_subject:"maptest.submissions.>".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}).await.map_err(StartupError::NatsConsumer)?
|
||||||
|
.messages().await.map_err(StartupError::NatsStream)
|
||||||
|
};
|
||||||
|
|
||||||
// connect to nats
|
// data-service grpc for creating map entries
|
||||||
let (publish_new,publish_fix,validator)=tokio::try_join!(
|
let data_host=std::env::var("DATA_HOST").expect("DATA_HOST env required");
|
||||||
publish_new::Publisher::new(nasty.clone(),cookie_context.clone(),api.clone()),
|
let message_handler_fut=async{
|
||||||
publish_fix::Publisher::new(nasty.clone(),cookie_context.clone()),
|
let maps_grpc=crate::MapsServiceClient::connect(data_host).await.map_err(StartupError::GRPCConnect)?;
|
||||||
// clone nats here because it's dropped within the function scope,
|
Ok(message_handler::MessageHandler::new(cookie_context,api,maps_grpc))
|
||||||
// meanining the last reference is dropped...
|
};
|
||||||
validator::Validator::new(nasty.clone(),cookie_context,api)
|
|
||||||
).map_err(StartupError::NatsSubscribe)?;
|
|
||||||
|
|
||||||
// publisher threads
|
// Create a signal listener for SIGTERM
|
||||||
tokio::spawn(publish_new.run());
|
let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener");
|
||||||
tokio::spawn(publish_fix.run());
|
|
||||||
|
|
||||||
// run validator on the main thread indefinitely
|
// run futures
|
||||||
validator.run().await;
|
let (mut messages,message_handler)=tokio::try_join!(nats_fut,message_handler_fut)?;
|
||||||
|
|
||||||
|
// process up to PARALLEL_REQUESTS in parallel
|
||||||
|
let main_loop=async move{
|
||||||
|
static SEM:tokio::sync::Semaphore=tokio::sync::Semaphore::const_new(PARALLEL_REQUESTS);
|
||||||
|
// use memory leak to make static lifetime
|
||||||
|
let message_handler=Box::leak(Box::new(message_handler));
|
||||||
|
// acquire a permit before attempting to receive a message, exit if either fails
|
||||||
|
while let (Ok(permit),Some(message_result))=(SEM.acquire().await,messages.next().await){
|
||||||
|
// handle the message on a new thread (mainly to decode the model file)
|
||||||
|
tokio::spawn(async{
|
||||||
|
match message_handler.handle_message_result(message_result).await{
|
||||||
|
Ok(())=>println!("[Validation] Success, hooray!"),
|
||||||
|
Err(e)=>println!("[Validation] There was an error, oopsie! {e}"),
|
||||||
|
}
|
||||||
|
// explicitly call drop to make the move semantics and permit release more obvious
|
||||||
|
core::mem::drop(permit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// race sigkill and main loop termination and then die
|
||||||
|
tokio::select!{
|
||||||
|
_=sig_term.recv()=>(),
|
||||||
|
_=main_loop=>(),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
48
validation/src/message_handler.rs
Normal file
48
validation/src/message_handler.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum HandleMessageError{
|
||||||
|
Messages(async_nats::jetstream::consumer::pull::MessagesError),
|
||||||
|
DoubleAck(async_nats::Error),
|
||||||
|
UnknownSubject(String),
|
||||||
|
PublishNew(crate::publish_new::PublishError),
|
||||||
|
PublishFix(crate::publish_fix::PublishError),
|
||||||
|
Validation(crate::validator::ValidateError),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for HandleMessageError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for HandleMessageError{}
|
||||||
|
|
||||||
|
pub type MessageResult=Result<async_nats::jetstream::Message,async_nats::jetstream::consumer::pull::MessagesError>;
|
||||||
|
|
||||||
|
pub struct MessageHandler{
|
||||||
|
publish_new:crate::publish_new::Publisher,
|
||||||
|
publish_fix:crate::publish_fix::Publisher,
|
||||||
|
validator:crate::validator::Validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageHandler{
|
||||||
|
pub fn new(
|
||||||
|
cookie_context:rbx_asset::cookie::CookieContext,
|
||||||
|
api:api::Context,
|
||||||
|
maps_grpc:crate::MapsServiceClient,
|
||||||
|
)->Self{
|
||||||
|
Self{
|
||||||
|
publish_new:crate::publish_new::Publisher::new(cookie_context.clone(),api.clone(),maps_grpc),
|
||||||
|
publish_fix:crate::publish_fix::Publisher::new(cookie_context.clone(),api.clone()),
|
||||||
|
validator:crate::validator::Validator::new(cookie_context,api),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{
|
||||||
|
let message=message_result.map_err(HandleMessageError::Messages)?;
|
||||||
|
message.double_ack().await.map_err(HandleMessageError::DoubleAck)?;
|
||||||
|
match message.subject.as_str(){
|
||||||
|
"maptest.submissions.publishnew"=>self.publish_new.publish(message).await.map_err(HandleMessageError::PublishNew),
|
||||||
|
"maptest.submissions.publishfix"=>self.publish_fix.publish(message).await.map_err(HandleMessageError::PublishFix),
|
||||||
|
"maptest.submissions.validate"=>self.validator.validate(message).await.map_err(HandleMessageError::Validation),
|
||||||
|
other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ pub struct PublishNewRequest{
|
|||||||
pub ModelVersion:u64,
|
pub ModelVersion:u64,
|
||||||
pub Creator:String,
|
pub Creator:String,
|
||||||
pub DisplayName:String,
|
pub DisplayName:String,
|
||||||
|
pub GameID:u32,
|
||||||
//games:HashSet<GameID>,
|
//games:HashSet<GameID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
use futures::StreamExt;
|
use crate::nats_types::PublishFixRequest;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PublishError{
|
pub enum PublishError{
|
||||||
|
Get(rbx_asset::cookie::GetError),
|
||||||
|
Json(serde_json::Error),
|
||||||
|
Upload(rbx_asset::cookie::UploadError),
|
||||||
|
ApiActionSubmissionPublish(api::Error),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for PublishError{
|
impl std::fmt::Display for PublishError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -11,25 +16,47 @@ impl std::fmt::Display for PublishError{
|
|||||||
impl std::error::Error for PublishError{}
|
impl std::error::Error for PublishError{}
|
||||||
|
|
||||||
pub struct Publisher{
|
pub struct Publisher{
|
||||||
subscriber:async_nats::Subscriber,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
|
api:api::Context,
|
||||||
}
|
}
|
||||||
impl Publisher{
|
impl Publisher{
|
||||||
pub async fn new(
|
pub const fn new(
|
||||||
nats:async_nats::Client,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
)->Result<Self,async_nats::SubscribeError>{
|
api:api::Context,
|
||||||
Ok(Self{
|
)->Self{
|
||||||
subscriber:nats.subscribe("publish_fix").await?,
|
Self{
|
||||||
roblox_cookie,
|
roblox_cookie,
|
||||||
})
|
api,
|
||||||
}
|
|
||||||
pub async fn run(mut self){
|
|
||||||
while let Some(message)=self.subscriber.next().await{
|
|
||||||
self.publish(message).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn publish(&self,message:async_nats::Message){
|
pub async fn publish(&self,message:async_nats::jetstream::Message)->Result<(),PublishError>{
|
||||||
println!("publish {:?}",message);
|
println!("publish_fix {:?}",message.message.payload);
|
||||||
|
// decode json
|
||||||
|
let publish_info:PublishFixRequest=serde_json::from_slice(&message.payload).map_err(PublishError::Json)?;
|
||||||
|
|
||||||
|
// download the map model version
|
||||||
|
let model_data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
|
||||||
|
asset_id:publish_info.ModelID,
|
||||||
|
version:Some(publish_info.ModelVersion),
|
||||||
|
}).await.map_err(PublishError::Get)?;
|
||||||
|
|
||||||
|
// upload the map to the strafesnet group
|
||||||
|
let _upload_response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
|
||||||
|
assetid:publish_info.TargetAssetID,
|
||||||
|
groupId:Some(crate::GROUP_STRAFESNET),
|
||||||
|
name:None,
|
||||||
|
description:None,
|
||||||
|
ispublic:None,
|
||||||
|
allowComments:None,
|
||||||
|
},model_data).await.map_err(PublishError::Upload)?;
|
||||||
|
|
||||||
|
// that's it, the database entry does not need to be changed.
|
||||||
|
|
||||||
|
// mark submission as published
|
||||||
|
self.api.action_submission_publish(
|
||||||
|
api::SubmissionID(publish_info.SubmissionID)
|
||||||
|
).await.map_err(PublishError::ApiActionSubmissionPublish)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use futures::StreamExt;
|
|
||||||
|
|
||||||
use crate::nats_types::PublishNewRequest;
|
use crate::nats_types::PublishNewRequest;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PublishError{
|
pub enum PublishError{
|
||||||
Get(rbx_asset::cookie::GetError),
|
Get(rbx_asset::cookie::GetError),
|
||||||
Json(serde_json::Error),
|
Json(serde_json::Error),
|
||||||
Create(rbx_asset::cookie::CreateError),
|
Create(rbx_asset::cookie::CreateError),
|
||||||
|
SystemTime(std::time::SystemTimeError),
|
||||||
|
MapCreate(tonic::Status),
|
||||||
ApiActionSubmissionPublish(api::Error),
|
ApiActionSubmissionPublish(api::Error),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for PublishError{
|
impl std::fmt::Display for PublishError{
|
||||||
@ -18,35 +18,24 @@ impl std::fmt::Display for PublishError{
|
|||||||
impl std::error::Error for PublishError{}
|
impl std::error::Error for PublishError{}
|
||||||
|
|
||||||
pub struct Publisher{
|
pub struct Publisher{
|
||||||
subscriber:async_nats::Subscriber,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
api:api::Context,
|
api:api::Context,
|
||||||
|
maps_grpc:crate::MapsServiceClient,
|
||||||
}
|
}
|
||||||
impl Publisher{
|
impl Publisher{
|
||||||
pub async fn new(
|
pub const fn new(
|
||||||
nats:async_nats::Client,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
api:api::Context,
|
api:api::Context,
|
||||||
)->Result<Self,async_nats::SubscribeError>{
|
maps_grpc:crate::MapsServiceClient,
|
||||||
Ok(Self{
|
)->Self{
|
||||||
subscriber:nats.subscribe("publish_new").await?,
|
Self{
|
||||||
roblox_cookie,
|
roblox_cookie,
|
||||||
api,
|
api,
|
||||||
})
|
maps_grpc,
|
||||||
}
|
|
||||||
pub async fn run(mut self){
|
|
||||||
while let Some(message)=self.subscriber.next().await{
|
|
||||||
self.publish_supress_error(message).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn publish_supress_error(&self,message:async_nats::Message){
|
pub async fn publish(&self,message:async_nats::jetstream::Message)->Result<(),PublishError>{
|
||||||
match self.publish(message).await{
|
println!("publish_new {:?}",message.message.payload);
|
||||||
Ok(())=>println!("Published, hooray!"),
|
|
||||||
Err(e)=>println!("[PublishNew] There was an error, oopsie! {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async fn publish(&self,message:async_nats::Message)->Result<(),PublishError>{
|
|
||||||
println!("publish {:?}",message);
|
|
||||||
// decode json
|
// decode json
|
||||||
let publish_info:PublishNewRequest=serde_json::from_slice(&message.payload).map_err(PublishError::Json)?;
|
let publish_info:PublishNewRequest=serde_json::from_slice(&message.payload).map_err(PublishError::Json)?;
|
||||||
|
|
||||||
@ -58,7 +47,7 @@ impl Publisher{
|
|||||||
|
|
||||||
// upload the map to the strafesnet group
|
// upload the map to the strafesnet group
|
||||||
let upload_response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
let upload_response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
||||||
name:publish_info.DisplayName,
|
name:publish_info.DisplayName.clone(),
|
||||||
description:"".to_owned(),
|
description:"".to_owned(),
|
||||||
ispublic:false,
|
ispublic:false,
|
||||||
allowComments:false,
|
allowComments:false,
|
||||||
@ -66,7 +55,23 @@ impl Publisher{
|
|||||||
},model_data).await.map_err(PublishError::Create)?;
|
},model_data).await.map_err(PublishError::Create)?;
|
||||||
|
|
||||||
// create the map entry in the game database, including release date
|
// create the map entry in the game database, including release date
|
||||||
// game_rpc.maps.create(upload_response.AssetId)
|
self.maps_grpc.clone().create(rust_grpc::maps::MapRequest{
|
||||||
|
id:upload_response.AssetId as i64,
|
||||||
|
display_name:Some(publish_info.DisplayName),
|
||||||
|
creator:Some(publish_info.Creator),
|
||||||
|
game_id:Some(publish_info.GameID as i32),
|
||||||
|
// TODO: scheduling algorithm
|
||||||
|
date:Some(
|
||||||
|
// Publish one week from now
|
||||||
|
(
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
+std::time::Duration::from_secs(7*24*60*60)
|
||||||
|
)
|
||||||
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||||
|
.map_err(PublishError::SystemTime)?
|
||||||
|
.as_secs() as i64
|
||||||
|
),
|
||||||
|
}).await.map_err(PublishError::MapCreate)?;
|
||||||
|
|
||||||
// mark submission as published
|
// mark submission as published
|
||||||
self.api.action_submission_publish(
|
self.api.action_submission_publish(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use futures::{StreamExt,TryStreamExt};
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
use crate::nats_types::ValidateRequest;
|
use crate::nats_types::ValidateRequest;
|
||||||
|
|
||||||
const SCRIPT_CONCURRENCY:usize=16;
|
const SCRIPT_CONCURRENCY:usize=16;
|
||||||
@ -12,7 +13,7 @@ enum Policy{
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ValidateError{
|
pub enum ValidateError{
|
||||||
Blocked,
|
Blocked,
|
||||||
NotAllowed,
|
NotAllowed,
|
||||||
Get(rbx_asset::cookie::GetError),
|
Get(rbx_asset::cookie::GetError),
|
||||||
@ -34,36 +35,22 @@ impl std::fmt::Display for ValidateError{
|
|||||||
impl std::error::Error for ValidateError{}
|
impl std::error::Error for ValidateError{}
|
||||||
|
|
||||||
pub struct Validator{
|
pub struct Validator{
|
||||||
subscriber:async_nats::Subscriber,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
api:api::Context,
|
api:api::Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator{
|
impl Validator{
|
||||||
pub async fn new(
|
pub const fn new(
|
||||||
nats:async_nats::Client,
|
|
||||||
roblox_cookie:rbx_asset::cookie::CookieContext,
|
roblox_cookie:rbx_asset::cookie::CookieContext,
|
||||||
api:api::Context,
|
api:api::Context,
|
||||||
)->Result<Self,async_nats::SubscribeError>{
|
)->Self{
|
||||||
Ok(Self{
|
Self{
|
||||||
subscriber:nats.subscribe("validate").await?,
|
|
||||||
roblox_cookie,
|
roblox_cookie,
|
||||||
api,
|
api,
|
||||||
})
|
|
||||||
}
|
|
||||||
pub async fn run(mut self){
|
|
||||||
while let Some(message)=self.subscriber.next().await{
|
|
||||||
self.validate_supress_error(message).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn validate_supress_error(&self,message:async_nats::Message){
|
pub async fn validate(&self,message:async_nats::jetstream::Message)->Result<(),ValidateError>{
|
||||||
match self.validate(message).await{
|
println!("validate {:?}",message.message.payload);
|
||||||
Ok(())=>println!("Validated, hooray!"),
|
|
||||||
Err(e)=>println!("There was an error, oopsie! {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async fn validate(&self,message:async_nats::Message)->Result<(),ValidateError>{
|
|
||||||
println!("validate {:?}",message);
|
|
||||||
// decode json
|
// decode json
|
||||||
let validate_info:ValidateRequest=serde_json::from_slice(&message.payload).map_err(ValidateError::Json)?;
|
let validate_info:ValidateRequest=serde_json::from_slice(&message.payload).map_err(ValidateError::Json)?;
|
||||||
|
|
||||||
@ -146,10 +133,10 @@ impl Validator{
|
|||||||
if modified{
|
if modified{
|
||||||
// serialize model (slow!)
|
// serialize model (slow!)
|
||||||
let mut data=Vec::new();
|
let mut data=Vec::new();
|
||||||
rbx_binary::to_writer(&mut data,&dom,&[dom.root_ref()]).map_err(ValidateError::WriteDom)?;
|
rbx_binary::to_writer(&mut data,&dom,dom.root().children()).map_err(ValidateError::WriteDom)?;
|
||||||
|
|
||||||
// upload a model lol
|
// upload a model lol
|
||||||
let (model_id,model_version)=if let Some(model_id)=validate_info.ValidatedModelID{
|
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
|
||||||
// upload to existing id
|
// upload to existing id
|
||||||
let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
|
let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
|
||||||
assetid:model_id,
|
assetid:model_id,
|
||||||
@ -160,7 +147,7 @@ impl Validator{
|
|||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Upload)?;
|
},data).await.map_err(ValidateError::Upload)?;
|
||||||
|
|
||||||
(response.AssetId,response.AssetVersionId)
|
response.AssetId
|
||||||
}else{
|
}else{
|
||||||
// create new model
|
// create new model
|
||||||
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
||||||
@ -171,14 +158,14 @@ impl Validator{
|
|||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Create)?;
|
},data).await.map_err(ValidateError::Create)?;
|
||||||
|
|
||||||
(response.AssetId,response.AssetVersionId)
|
response.AssetId
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the submission to use the validated model
|
// update the submission to use the validated model
|
||||||
self.api.update_submission_model(api::UpdateSubmissionModelRequest{
|
self.api.update_submission_model(api::UpdateSubmissionModelRequest{
|
||||||
ID:validate_info.SubmissionID,
|
ID:validate_info.SubmissionID,
|
||||||
ModelID:model_id,
|
ModelID:model_id,
|
||||||
ModelVersion:model_version,
|
ModelVersion:1, //TODO
|
||||||
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
|
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -193,7 +180,7 @@ impl Validator{
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ReadDomError{
|
pub enum ReadDomError{
|
||||||
Binary(rbx_binary::DecodeError),
|
Binary(rbx_binary::DecodeError),
|
||||||
Xml(rbx_xml::DecodeError),
|
Xml(rbx_xml::DecodeError),
|
||||||
Read(std::io::Error),
|
Read(std::io::Error),
|
||||||
|
3
web/.dockerignore
Normal file
3
web/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
bun.lockb
|
@ -1,38 +1,13 @@
|
|||||||
# use the official Bun image
|
FROM oven/bun:latest
|
||||||
# see all versions at https://hub.docker.com/r/oven/bun/tags
|
|
||||||
FROM oven/bun:1 AS base
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
# install dependencies into temp directory
|
WORKDIR /app
|
||||||
# this will cache them and speed up future builds
|
|
||||||
FROM base AS install
|
|
||||||
RUN mkdir -p /temp/dev
|
|
||||||
COPY package.json bun.lockb /temp/dev/
|
|
||||||
RUN cd /temp/dev && bun install --frozen-lockfile
|
|
||||||
|
|
||||||
# install with --production (exclude devDependencies)
|
|
||||||
RUN mkdir -p /temp/prod
|
|
||||||
COPY package.json bun.lockb /temp/prod/
|
|
||||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
|
||||||
|
|
||||||
# copy node_modules from temp directory
|
|
||||||
# then copy all (non-ignored) project files into the image
|
|
||||||
FROM base AS prerelease
|
|
||||||
COPY --from=install /temp/dev/node_modules node_modules
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# [optional] tests & build
|
EXPOSE 3000
|
||||||
ENV NODE_ENV=production
|
|
||||||
RUN bun test
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN bun install
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
ENTRYPOINT ["bun", "run", "start"]
|
||||||
# copy production dependencies and source code into final image
|
|
||||||
FROM base AS release
|
|
||||||
COPY --from=install /temp/prod/node_modules node_modules
|
|
||||||
COPY --from=prerelease /usr/src/app/index.ts .
|
|
||||||
COPY --from=prerelease /usr/src/app/package.json .
|
|
||||||
|
|
||||||
# run the app
|
|
||||||
USER bun
|
|
||||||
EXPOSE 3000/tcp
|
|
||||||
ENTRYPOINT [ "bun", "run", "index.ts" ]
|
|
@ -1,7 +1,16 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
distDir: "build"
|
distDir: "build",
|
||||||
|
output: "standalone",
|
||||||
|
rewrites: async () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/v1/submissions/:submissionid/status/:statustype",
|
||||||
|
destination: "http://mapsservice:8082/v1/submissions/:submissionid/status/:statustype"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
@ -4,34 +4,34 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
background: rgb(59,64,70);
|
background: var(--header-grad-left);
|
||||||
background: linear-gradient(180deg, #363b40 0%, #353a40 100%);
|
background: linear-gradient(180deg, var(--header-grad-left) 0%, var(--header-grad-right) 100%);
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
color: white;
|
color: var(--header-button-left);
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
margin-right: 50px;
|
margin-right: 50px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: rgb(180,180,180);
|
color: var(--header-button-right);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: white
|
color: var(--header-button-hover)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
$review-border-color: #c8c8c8;
|
$review-border: 1px solid var(--review-border);
|
||||||
$review-border: 1px solid $review-border-color;
|
|
||||||
|
|
||||||
@mixin border-with-radius {
|
@mixin border-with-radius {
|
||||||
border: $review-border {
|
border: $review-border {
|
||||||
@ -8,13 +7,42 @@ $review-border: 1px solid $review-border-color;
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
color-scheme: light;
|
color-scheme: light dark;
|
||||||
|
|
||||||
|
--page: white;
|
||||||
|
--header-grad-left: #363b40;
|
||||||
|
--header-grad-right: #353a40;
|
||||||
|
--header-button-left: white;
|
||||||
|
--header-button-right: #b4b4b4;
|
||||||
|
--header-button-hover: white;
|
||||||
|
--review-border: #c8c8c8;
|
||||||
|
--text-color: #1e1e1e;
|
||||||
|
--anchor-link-review: #008fd6;
|
||||||
|
--window-header: #f5f5f5;
|
||||||
|
--comment-highlighted: #ffffd7;
|
||||||
|
--comment-area: white;
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
--page: rgb(15,15,15);
|
||||||
|
--header-grad-left: #363b40;
|
||||||
|
--header-grad-right: #353a40;
|
||||||
|
--header-button-left: white;
|
||||||
|
--header-button-right: #b4b4b4;
|
||||||
|
--header-button-hover: white;
|
||||||
|
--review-border: rgb(50,50,50);
|
||||||
|
--text-color: rgb(230,230,230);
|
||||||
|
--anchor-link-review: #008fd6;
|
||||||
|
--window-header: rgb(10,10,10);
|
||||||
|
--comment-highlighted: #ffffd7;
|
||||||
|
--comment-area: rgb(20,20,20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
|
font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
background-color: var(--page);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: globals.$review-border-color;
|
background-color: var(--review-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.by-creator {
|
.by-creator {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
resize: none;
|
resize: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
|
background-color: var(--comment-area)
|
||||||
}
|
}
|
||||||
|
|
||||||
.leave-comment-window {
|
.leave-comment-window {
|
||||||
@ -36,7 +37,7 @@
|
|||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #f5f5f5;
|
background-color: var(--window-header);
|
||||||
border-bottom: globals.$review-border;
|
border-bottom: globals.$review-border;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ $comments-size: 60px;
|
|||||||
|
|
||||||
//BhopMaptest comment
|
//BhopMaptest comment
|
||||||
&[data-highlighted="true"] {
|
&[data-highlighted="true"] {
|
||||||
background-color: #ffffd7;
|
background-color: var(--comment-highlighted);
|
||||||
}
|
}
|
||||||
> img {
|
> img {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #f5f5f5;
|
background-color: var(--window-header);
|
||||||
border-bottom: globals.$review-border;
|
border-bottom: globals.$review-border;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
|
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
p, h1 {
|
p, h1 {
|
||||||
color: #1e1e1e;
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
font: {
|
font: {
|
||||||
@ -21,7 +20,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: #008fd6;
|
color: var(--anchor-link-review);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
import { Button, ButtonOwnProps } from "@mui/material";
|
import { Button, ButtonOwnProps } from "@mui/material";
|
||||||
|
|
||||||
type Review = "Completed" | "Submit" | "Reject" | "Revoke" | "Validate" | "Publish"
|
type Review = "Completed" | "Submit" | "Reject" | "Revoke" | "Accept" | "Publish"
|
||||||
|
type Action = "completed" | "submit" | "reject" | "revoke" | "trigger-validate" | "trigger-publish"
|
||||||
interface ReviewButton {
|
interface ReviewButton {
|
||||||
name: Review,
|
name: Review,
|
||||||
|
action: Action,
|
||||||
color: ButtonOwnProps["color"]
|
color: ButtonOwnProps["color"]
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReviewButtonClicked(type: Review) {
|
function ReviewButtonClicked(action: Action) {
|
||||||
//magical api requesting goes here, i hope it wont blow up
|
const post = fetch(`http://localhost:3000/v1/submissions/1/status/${action}`, {
|
||||||
console.log(type)
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReviewButton(props: ReviewButton) {
|
function ReviewButton(props: ReviewButton) {
|
||||||
return <Button color={props.color} variant="contained" onClick={() => { ReviewButtonClicked(props.name) }}>{props.name}</Button>
|
return <Button color={props.color} variant="contained" onClick={() => { ReviewButtonClicked(props.action) }}>{props.name}</Button>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ReviewButtons() {
|
export default function ReviewButtons() {
|
||||||
return (
|
return (
|
||||||
<section className="review-set">
|
<section className="review-set">
|
||||||
<ReviewButton color="error" name="Reject"/>
|
<ReviewButton color="info" name="Submit" action="submit"/>
|
||||||
<ReviewButton color="info" name="Revoke"/>
|
<ReviewButton color="info" name="Revoke" action="revoke"/>
|
||||||
<ReviewButton color="info" name="Publish"/>
|
<ReviewButton color="info" name="Accept" action="trigger-validate"/>
|
||||||
<ReviewButton color="info" name="Completed"/>
|
<ReviewButton color="error" name="Reject" action="reject"/>
|
||||||
<ReviewButton color="info" name="Submit"/>
|
<ReviewButton color="info" name="Publish" action="trigger-publish"/>
|
||||||
<ReviewButton color="info" name="Validate"/>
|
<ReviewButton color="info" name="Completed" action="completed"/>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -25,39 +25,28 @@ interface SubmissionInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SubmissionStatusToString(submission_status: SubmissionStatus): string {
|
function SubmissionStatusToString(submission_status: SubmissionStatus): string {
|
||||||
let Review: string
|
|
||||||
switch (submission_status) {
|
switch (submission_status) {
|
||||||
case SubmissionStatus.Published:
|
case SubmissionStatus.Published:
|
||||||
Review = "PUBLISHED"
|
return "PUBLISHED"
|
||||||
break
|
|
||||||
case SubmissionStatus.Rejected:
|
case SubmissionStatus.Rejected:
|
||||||
Review = "REJECTED"
|
return "REJECTED"
|
||||||
break
|
|
||||||
case SubmissionStatus.Publishing:
|
case SubmissionStatus.Publishing:
|
||||||
Review = "PUBLISHING"
|
return "PUBLISHING"
|
||||||
break
|
|
||||||
case SubmissionStatus.Validated:
|
case SubmissionStatus.Validated:
|
||||||
Review = "VALIDATED"
|
return "VALIDATED"
|
||||||
break
|
|
||||||
case SubmissionStatus.Validating:
|
case SubmissionStatus.Validating:
|
||||||
Review = "VALIDATING"
|
return "VALIDATING"
|
||||||
break
|
|
||||||
case SubmissionStatus.Accepted:
|
case SubmissionStatus.Accepted:
|
||||||
Review = "ACCEPTED"
|
return "ACCEPTED"
|
||||||
break
|
|
||||||
case SubmissionStatus.ChangesRequested:
|
case SubmissionStatus.ChangesRequested:
|
||||||
Review = "CHANGES REQUESTED"
|
return "CHANGES REQUESTED"
|
||||||
break
|
|
||||||
case SubmissionStatus.Submitted:
|
case SubmissionStatus.Submitted:
|
||||||
Review = "SUBMITTED"
|
return "SUBMITTED"
|
||||||
break
|
|
||||||
case SubmissionStatus.UnderConstruction:
|
case SubmissionStatus.UnderConstruction:
|
||||||
Review = "UNDER CONSTRUCTION"
|
return "UNDER CONSTRUCTION"
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
Review = "UNKNOWN"
|
return "UNKNOWN"
|
||||||
}
|
}
|
||||||
return Review
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
Loading…
Reference in New Issue
Block a user