Compare commits
25 Commits
2f4c88689c
...
a9afdf38cf
Author | SHA1 | Date | |
---|---|---|---|
a9afdf38cf | |||
d3edb6b3da | |||
188fbd2a6d | |||
1468a9edc2 | |||
1053719eab | |||
2867da4b21 | |||
85a144e276 | |||
4227f18992 | |||
123bc8af47 | |||
cd82954b73 | |||
ce08b57e18 | |||
1ca0348924 | |||
936a1f93aa | |||
d5d0e5ffc9 | |||
039309c75a | |||
7cc0b5da7f | |||
f0c44fb4a8 | |||
4fec1bba47 | |||
5ae287f3f2 | |||
bf6c8af21a | |||
65e63431a3 | |||
a8dc6cd35a | |||
539e09fe06 | |||
87fd7adb93 | |||
7d57d1ac4d |
Cargo.lockcompose.yamlopenapi-internal.yamlopenapi.yaml
pkg
api
internal
model
service
service_internal
validation
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1833,7 +1833,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "submissions-api"
|
name = "submissions-api"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -49,7 +49,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
- API_HOST=http://submissions:8082
|
- API_HOST=http://submissions:8082/v1
|
||||||
|
|
||||||
validation:
|
validation:
|
||||||
image:
|
image:
|
||||||
@ -59,7 +59,7 @@ services:
|
|||||||
- ../auth-compose/strafesnet_staging.env
|
- ../auth-compose/strafesnet_staging.env
|
||||||
environment:
|
environment:
|
||||||
- ROBLOX_GROUP_ID=17032139 # "None" is special case string value
|
- ROBLOX_GROUP_ID=17032139 # "None" is special case string value
|
||||||
- API_HOST_INTERNAL=http://submissions:8083
|
- API_HOST_INTERNAL=http://submissions:8083/v1
|
||||||
- NATS_HOST=nats:4222
|
- NATS_HOST=nats:4222
|
||||||
depends_on:
|
depends_on:
|
||||||
- nats
|
- nats
|
||||||
@ -93,11 +93,12 @@ services:
|
|||||||
- maps-service-network
|
- maps-service-network
|
||||||
|
|
||||||
authrpc:
|
authrpc:
|
||||||
image: registry.itzana.me/strafesnet/auth-service:master
|
image: registry.itzana.me/strafesnet/auth-service:staging
|
||||||
container_name: authrpc
|
container_name: authrpc
|
||||||
command: ["serve", "rpc"]
|
command: ["serve", "rpc"]
|
||||||
environment:
|
environment:
|
||||||
- REDIS_ADDR=authredis:6379
|
- REDIS_ADDR=authredis:6379
|
||||||
|
- RBX_GROUP_ID=17032139
|
||||||
env_file:
|
env_file:
|
||||||
- ../auth-compose/auth-service.env
|
- ../auth-compose/auth-service.env
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -108,7 +109,7 @@ services:
|
|||||||
driver: "none"
|
driver: "none"
|
||||||
|
|
||||||
auth-web:
|
auth-web:
|
||||||
image: registry.itzana.me/strafesnet/auth-service:master
|
image: registry.itzana.me/strafesnet/auth-service:staging
|
||||||
command: ["serve", "web"]
|
command: ["serve", "web"]
|
||||||
environment:
|
environment:
|
||||||
- REDIS_ADDR=authredis:6379
|
- REDIS_ADDR=authredis:6379
|
||||||
|
@ -21,7 +21,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
- name: VersionID
|
- name: ValidatedModelVersion
|
||||||
in: query
|
in: query
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
|
@ -717,6 +717,7 @@ components:
|
|||||||
- SubmissionType
|
- SubmissionType
|
||||||
# - TargetAssetID
|
# - TargetAssetID
|
||||||
- StatusID
|
- StatusID
|
||||||
|
- StatusMessage
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
ID:
|
ID:
|
||||||
@ -757,6 +758,9 @@ components:
|
|||||||
StatusID:
|
StatusID:
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
|
StatusMessage:
|
||||||
|
type: string
|
||||||
|
maxLength: 256
|
||||||
SubmissionCreate:
|
SubmissionCreate:
|
||||||
required:
|
required:
|
||||||
- DisplayName
|
- DisplayName
|
||||||
|
@ -1464,9 +1464,13 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("StatusID")
|
e.FieldStart("StatusID")
|
||||||
e.Int32(s.StatusID)
|
e.Int32(s.StatusID)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("StatusMessage")
|
||||||
|
e.Str(s.StatusMessage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfSubmission = [13]string{
|
var jsonFieldsNameOfSubmission = [14]string{
|
||||||
0: "ID",
|
0: "ID",
|
||||||
1: "DisplayName",
|
1: "DisplayName",
|
||||||
2: "Creator",
|
2: "Creator",
|
||||||
@ -1480,6 +1484,7 @@ var jsonFieldsNameOfSubmission = [13]string{
|
|||||||
10: "SubmissionType",
|
10: "SubmissionType",
|
||||||
11: "TargetAssetID",
|
11: "TargetAssetID",
|
||||||
12: "StatusID",
|
12: "StatusID",
|
||||||
|
13: "StatusMessage",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes Submission from json.
|
// Decode decodes Submission from json.
|
||||||
@ -1645,6 +1650,18 @@ func (s *Submission) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"StatusID\"")
|
return errors.Wrap(err, "decode field \"StatusID\"")
|
||||||
}
|
}
|
||||||
|
case "StatusMessage":
|
||||||
|
requiredBitSet[1] |= 1 << 5
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.StatusMessage = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"StatusMessage\"")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@ -1656,7 +1673,7 @@ func (s *Submission) Decode(d *jx.Decoder) error {
|
|||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [2]uint8{
|
for i, mask := range [2]uint8{
|
||||||
0b11111111,
|
0b11111111,
|
||||||
0b00010111,
|
0b00110111,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
|
@ -600,6 +600,7 @@ type Submission struct {
|
|||||||
SubmissionType int32 `json:"SubmissionType"`
|
SubmissionType int32 `json:"SubmissionType"`
|
||||||
TargetAssetID OptInt64 `json:"TargetAssetID"`
|
TargetAssetID OptInt64 `json:"TargetAssetID"`
|
||||||
StatusID int32 `json:"StatusID"`
|
StatusID int32 `json:"StatusID"`
|
||||||
|
StatusMessage string `json:"StatusMessage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the value of ID.
|
// GetID returns the value of ID.
|
||||||
@ -667,6 +668,11 @@ func (s *Submission) GetStatusID() int32 {
|
|||||||
return s.StatusID
|
return s.StatusID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStatusMessage returns the value of StatusMessage.
|
||||||
|
func (s *Submission) GetStatusMessage() string {
|
||||||
|
return s.StatusMessage
|
||||||
|
}
|
||||||
|
|
||||||
// SetID sets the value of ID.
|
// SetID sets the value of ID.
|
||||||
func (s *Submission) SetID(val int64) {
|
func (s *Submission) SetID(val int64) {
|
||||||
s.ID = val
|
s.ID = val
|
||||||
@ -732,6 +738,11 @@ func (s *Submission) SetStatusID(val int32) {
|
|||||||
s.StatusID = val
|
s.StatusID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStatusMessage sets the value of StatusMessage.
|
||||||
|
func (s *Submission) SetStatusMessage(val string) {
|
||||||
|
s.StatusMessage = val
|
||||||
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/SubmissionCreate
|
// Ref: #/components/schemas/SubmissionCreate
|
||||||
type SubmissionCreate struct {
|
type SubmissionCreate struct {
|
||||||
DisplayName string `json:"DisplayName"`
|
DisplayName string `json:"DisplayName"`
|
||||||
|
@ -266,6 +266,25 @@ func (s *Submission) Validate() error {
|
|||||||
Error: err,
|
Error: err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.String{
|
||||||
|
MinLength: 0,
|
||||||
|
MinLengthSet: false,
|
||||||
|
MaxLength: 256,
|
||||||
|
MaxLengthSet: true,
|
||||||
|
Email: false,
|
||||||
|
Hostname: false,
|
||||||
|
Regex: nil,
|
||||||
|
}).Validate(string(s.StatusMessage)); err != nil {
|
||||||
|
return errors.Wrap(err, "string")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: "StatusMessage",
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
|
@ -1096,15 +1096,15 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Encode "VersionID" parameter.
|
// Encode "ValidatedModelVersion" parameter.
|
||||||
cfg := uri.QueryParameterEncodingConfig{
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
Name: "VersionID",
|
Name: "ValidatedModelVersion",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
return e.EncodeValue(conv.Int64ToString(params.VersionID))
|
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return res, errors.Wrap(err, "encode query")
|
return res, errors.Wrap(err, "encode query")
|
||||||
}
|
}
|
||||||
|
@ -1369,9 +1369,9 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
|
|||||||
In: "query",
|
In: "query",
|
||||||
}: params.ValidatedModelID,
|
}: params.ValidatedModelID,
|
||||||
{
|
{
|
||||||
Name: "VersionID",
|
Name: "ValidatedModelVersion",
|
||||||
In: "query",
|
In: "query",
|
||||||
}: params.VersionID,
|
}: params.ValidatedModelVersion,
|
||||||
},
|
},
|
||||||
Raw: r,
|
Raw: r,
|
||||||
}
|
}
|
||||||
|
@ -1114,9 +1114,9 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
|
|||||||
// UpdateSubmissionValidatedModelParams is parameters of updateSubmissionValidatedModel operation.
|
// UpdateSubmissionValidatedModelParams is parameters of updateSubmissionValidatedModel operation.
|
||||||
type UpdateSubmissionValidatedModelParams struct {
|
type UpdateSubmissionValidatedModelParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
ValidatedModelID int64
|
ValidatedModelID int64
|
||||||
VersionID int64
|
ValidatedModelVersion int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (params UpdateSubmissionValidatedModelParams) {
|
func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (params UpdateSubmissionValidatedModelParams) {
|
||||||
@ -1136,10 +1136,10 @@ func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (p
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
key := middleware.ParameterKey{
|
key := middleware.ParameterKey{
|
||||||
Name: "VersionID",
|
Name: "ValidatedModelVersion",
|
||||||
In: "query",
|
In: "query",
|
||||||
}
|
}
|
||||||
params.VersionID = packed[key].(int64)
|
params.ValidatedModelVersion = packed[key].(int64)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@ -1227,10 +1227,10 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Decode query: VersionID.
|
// Decode query: ValidatedModelVersion.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
cfg := uri.QueryParameterDecodingConfig{
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
Name: "VersionID",
|
Name: "ValidatedModelVersion",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
@ -1247,7 +1247,7 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
params.VersionID = c
|
params.ValidatedModelVersion = c
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1258,7 +1258,7 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
|
|||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return params, &ogenerrors.DecodeParamError{
|
return params, &ogenerrors.DecodeParamError{
|
||||||
Name: "VersionID",
|
Name: "ValidatedModelVersion",
|
||||||
In: "query",
|
In: "query",
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ type ValidateRequest struct {
|
|||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
ModelID int64
|
ModelID int64
|
||||||
ModelVersion int64
|
ModelVersion int64
|
||||||
ValidatedModelID int64 // optional value
|
ValidatedModelID *int64 // optional value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new map
|
// Create a new map
|
||||||
|
@ -17,10 +17,11 @@ var (
|
|||||||
// Submissions roles bitflag
|
// Submissions roles bitflag
|
||||||
type Roles int32
|
type Roles int32
|
||||||
var (
|
var (
|
||||||
RolesScriptWrite Roles = 8
|
RolesSubmissionRelease Roles = 1<<4
|
||||||
RolesSubmissionPublish Roles = 4
|
RolesScriptWrite Roles = 1<<3
|
||||||
RolesSubmissionReview Roles = 2
|
RolesSubmissionUpload Roles = 1<<2
|
||||||
RolesMapDownload Roles = 1
|
RolesSubmissionReview Roles = 1<<1
|
||||||
|
RolesMapDownload Roles = 1<<0
|
||||||
RolesEmpty Roles = 0
|
RolesEmpty Roles = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,10 +32,10 @@ var (
|
|||||||
RoleQuat GroupRole = 255
|
RoleQuat GroupRole = 255
|
||||||
RoleItzaname GroupRole = 254
|
RoleItzaname GroupRole = 254
|
||||||
RoleStagingDeveloper GroupRole = 240
|
RoleStagingDeveloper GroupRole = 240
|
||||||
RolesAll Roles = RolesScriptWrite|RolesSubmissionPublish|RolesSubmissionReview|RolesMapDownload
|
RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapDownload
|
||||||
// has SubmissionPublish
|
// has SubmissionUpload
|
||||||
RoleMapAdmin GroupRole = 128
|
RoleMapAdmin GroupRole = 128
|
||||||
RolesMapAdmin Roles = RolesSubmissionPublish|RolesSubmissionReview|RolesMapDownload
|
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapDownload
|
||||||
// has SubmissionReview
|
// has SubmissionReview
|
||||||
RoleMapCouncil GroupRole = 64
|
RoleMapCouncil GroupRole = 64
|
||||||
RolesMapCouncil Roles = RolesSubmissionReview|RolesMapDownload
|
RolesMapCouncil Roles = RolesSubmissionReview|RolesMapDownload
|
||||||
@ -127,9 +128,11 @@ func (usr UserInfoHandle) GetRoles() (Roles, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RoleThumbnail
|
// RoleThumbnail
|
||||||
// RoleMapDownload
|
func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
|
||||||
func (usr UserInfoHandle) HasRoleSubmissionPublish() (bool, error) {
|
return usr.hasRoles(RolesSubmissionRelease)
|
||||||
return usr.hasRoles(RolesSubmissionPublish)
|
}
|
||||||
|
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
|
||||||
|
return usr.hasRoles(RolesSubmissionUpload)
|
||||||
}
|
}
|
||||||
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
|
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
|
||||||
return usr.hasRoles(RolesSubmissionReview)
|
return usr.hasRoles(RolesSubmissionReview)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||||
@ -37,6 +38,15 @@ var (
|
|||||||
ErrActiveSubmissionSameTargetAssetID = errors.New("There is an active submission with the same TargetAssetID")
|
ErrActiveSubmissionSameTargetAssetID = errors.New("There is an active submission with the same TargetAssetID")
|
||||||
ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released")
|
ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released")
|
||||||
ErrReleaseNoTargetAssetID = errors.New("Only submissions with a TargetAssetID can be released")
|
ErrReleaseNoTargetAssetID = errors.New("Only submissions with a TargetAssetID can be released")
|
||||||
|
ErrAcceptOwnSubmission = fmt.Errorf("%w: You cannot accept your own submission as the submitter", ErrPermissionDenied)
|
||||||
|
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
|
||||||
|
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
|
||||||
|
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
|
||||||
)
|
)
|
||||||
|
|
||||||
// POST /submissions
|
// POST /submissions
|
||||||
@ -147,6 +157,7 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
|
|||||||
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),
|
||||||
|
StatusMessage: submission.StatusMessage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +227,7 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
|
|||||||
}
|
}
|
||||||
// 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 !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedRoleMaptest
|
||||||
}
|
}
|
||||||
|
|
||||||
pmap := datastore.Optional()
|
pmap := datastore.Optional()
|
||||||
@ -247,7 +258,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
|
|||||||
}
|
}
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNotSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if Status is ChangesRequested|Submitted|UnderConstruction
|
// check if Status is ChangesRequested|Submitted|UnderConstruction
|
||||||
@ -276,7 +287,7 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
|
|||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -302,7 +313,7 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
|
|||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -334,7 +345,7 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
|
|||||||
}
|
}
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNotSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -366,7 +377,7 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
|
|||||||
}
|
}
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNotSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -386,13 +397,13 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
|
|||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
has_role, err := userInfo.HasRoleSubmissionPublish()
|
has_role, err := userInfo.HasRoleSubmissionUpload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedSubmissionUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -451,13 +462,13 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
|
|||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
has_role, err := userInfo.HasRoleSubmissionPublish()
|
has_role, err := userInfo.HasRoleSubmissionUpload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedSubmissionUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
// check when submission was updated
|
// check when submission was updated
|
||||||
@ -467,7 +478,7 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
|
|||||||
}
|
}
|
||||||
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
|
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
|
||||||
// the last time the submission was updated must be longer than 10 seconds ago
|
// the last time the submission was updated must be longer than 10 seconds ago
|
||||||
return ErrPermissionDenied
|
return ErrDelayReset
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -493,7 +504,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
|
|||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
}
|
}
|
||||||
|
|
||||||
// read submission (this could be done with a transaction WHERE clause)
|
// read submission (this could be done with a transaction WHERE clause)
|
||||||
@ -508,7 +519,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
|
|||||||
}
|
}
|
||||||
// check if caller is NOT the submitter
|
// check if caller is NOT the submitter
|
||||||
if has_role {
|
if has_role {
|
||||||
return ErrPermissionDenied
|
return ErrAcceptOwnSubmission
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -523,7 +534,12 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
|
|||||||
SubmissionID: submission.ID,
|
SubmissionID: submission.ID,
|
||||||
ModelID: submission.AssetID,
|
ModelID: submission.AssetID,
|
||||||
ModelVersion: submission.AssetVersion,
|
ModelVersion: submission.AssetVersion,
|
||||||
ValidatedModelID: submission.ValidatedAssetID,
|
ValidatedModelID: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// sentinel values because we're not using rust
|
||||||
|
if submission.ValidatedAssetID != 0 {
|
||||||
|
validate_request.ValidatedModelID = &submission.ValidatedAssetID
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(validate_request)
|
j, err := json.Marshal(validate_request)
|
||||||
@ -553,7 +569,7 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
|
|||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
}
|
}
|
||||||
|
|
||||||
// check when submission was updated
|
// check when submission was updated
|
||||||
@ -563,7 +579,7 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
|
|||||||
}
|
}
|
||||||
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
|
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
|
||||||
// the last time the submission was updated must be longer than 10 seconds ago
|
// the last time the submission was updated must be longer than 10 seconds ago
|
||||||
return ErrPermissionDenied
|
return ErrDelayReset
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
@ -584,13 +600,13 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
|
|||||||
return ErrUserInfo
|
return ErrUserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
has_role, err := userInfo.HasRoleSubmissionPublish()
|
has_role, err := userInfo.HasRoleSubmissionRelease()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// check if caller has required role
|
// check if caller has required role
|
||||||
if !has_role {
|
if !has_role {
|
||||||
return ErrPermissionDenied
|
return ErrPermissionDeniedNeedSubmissionRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
idList := make([]int64, len(request))
|
idList := make([]int64, len(request))
|
||||||
|
@ -17,7 +17,7 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
|
|||||||
// check if Status is ChangesRequested|Submitted|UnderConstruction
|
// check if Status is ChangesRequested|Submitted|UnderConstruction
|
||||||
pmap := datastore.Optional()
|
pmap := datastore.Optional()
|
||||||
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
|
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
|
||||||
pmap.AddNotNil("validated_asset_version", params.VersionID)
|
pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
|
||||||
// DO NOT reset completed when validated model is updated
|
// DO NOT reset completed when validated model is updated
|
||||||
// pmap.Add("completed", false)
|
// pmap.Add("completed", false)
|
||||||
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, pmap)
|
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, pmap)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "submissions-api"
|
name = "submissions-api"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = ["strafesnet"]
|
publish = ["strafesnet"]
|
||||||
repository = "https://git.itzana.me/StrafesNET/maps-service"
|
repository = "https://git.itzana.me/StrafesNET/maps-service"
|
||||||
|
@ -31,6 +31,47 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::Reqwest)
|
.json().await.map_err(Error::Reqwest)
|
||||||
}
|
}
|
||||||
|
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
||||||
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
|
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut query_pairs=url.query_pairs_mut();
|
||||||
|
query_pairs.append_pair("Page",config.Page.to_string().as_str());
|
||||||
|
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
|
||||||
|
if let Some(name)=config.Name{
|
||||||
|
query_pairs.append_pair("Name",name);
|
||||||
|
}
|
||||||
|
if let Some(hash)=config.Hash{
|
||||||
|
query_pairs.append_pair("Hash",hash);
|
||||||
|
}
|
||||||
|
if let Some(source)=config.Source{
|
||||||
|
query_pairs.append_pair("Source",source);
|
||||||
|
}
|
||||||
|
if let Some(submission_id)=config.SubmissionID{
|
||||||
|
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_ok(
|
||||||
|
self.0.get(url).await.map_err(Error::Reqwest)?
|
||||||
|
).await.map_err(Error::Response)?
|
||||||
|
.json().await.map_err(Error::Reqwest)
|
||||||
|
}
|
||||||
|
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||||
|
let scripts=self.get_scripts(GetScriptsRequest{
|
||||||
|
Page:1,
|
||||||
|
Limit:2,
|
||||||
|
Hash:Some(config.hash),
|
||||||
|
Name:None,
|
||||||
|
Source:None,
|
||||||
|
SubmissionID:None,
|
||||||
|
}).await.map_err(SingleItemError::Other)?;
|
||||||
|
if 1<scripts.len(){
|
||||||
|
return Err(SingleItemError::DuplicateItems);
|
||||||
|
}
|
||||||
|
Ok(scripts.into_iter().next())
|
||||||
|
}
|
||||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
@ -60,9 +60,9 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
|
||||||
pub struct ScriptID(pub(crate)i64);
|
pub struct ScriptID(pub(crate)i64);
|
||||||
#[derive(Clone,Copy,serde::Serialize,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyID(pub(crate)i64);
|
pub struct ScriptPolicyID(pub(crate)i64);
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
@ -70,7 +70,7 @@ pub struct GetScriptRequest{
|
|||||||
pub ScriptID:ScriptID,
|
pub ScriptID:ScriptID,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct GetScriptsRequest<'a>{
|
pub struct GetScriptsRequest<'a>{
|
||||||
pub Page:u32,
|
pub Page:u32,
|
||||||
pub Limit:u32,
|
pub Limit:u32,
|
||||||
@ -83,11 +83,12 @@ pub struct GetScriptsRequest<'a>{
|
|||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
pub SubmissionID:Option<i64>,
|
pub SubmissionID:Option<i64>,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub struct HashRequest<'a>{
|
pub struct HashRequest<'a>{
|
||||||
pub hash:&'a str,
|
pub hash:&'a str,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptResponse{
|
pub struct ScriptResponse{
|
||||||
pub ID:ScriptID,
|
pub ID:ScriptID,
|
||||||
pub Name:String,
|
pub Name:String,
|
||||||
@ -96,7 +97,7 @@ pub struct ScriptResponse{
|
|||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct CreateScriptRequest<'a>{
|
pub struct CreateScriptRequest<'a>{
|
||||||
pub Name:&'a str,
|
pub Name:&'a str,
|
||||||
pub Source:&'a str,
|
pub Source:&'a str,
|
||||||
@ -104,12 +105,12 @@ pub struct CreateScriptRequest<'a>{
|
|||||||
pub SubmissionID:Option<i64>,
|
pub SubmissionID:Option<i64>,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptIDResponse{
|
pub struct ScriptIDResponse{
|
||||||
pub ID:ScriptID,
|
pub ID:ScriptID,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum Policy{
|
pub enum Policy{
|
||||||
None=0, // not yet reviewed
|
None=0, // not yet reviewed
|
||||||
@ -120,7 +121,7 @@ pub enum Policy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct GetScriptPoliciesRequest<'a>{
|
pub struct GetScriptPoliciesRequest<'a>{
|
||||||
pub Page:u32,
|
pub Page:u32,
|
||||||
pub Limit:u32,
|
pub Limit:u32,
|
||||||
@ -132,7 +133,7 @@ pub struct GetScriptPoliciesRequest<'a>{
|
|||||||
pub Policy:Option<Policy>,
|
pub Policy:Option<Policy>,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyResponse{
|
pub struct ScriptPolicyResponse{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
pub FromScriptHash:String,
|
pub FromScriptHash:String,
|
||||||
@ -140,20 +141,20 @@ pub struct ScriptPolicyResponse{
|
|||||||
pub Policy:Policy
|
pub Policy:Policy
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct CreateScriptPolicyRequest{
|
pub struct CreateScriptPolicyRequest{
|
||||||
pub FromScriptID:ScriptID,
|
pub FromScriptID:ScriptID,
|
||||||
pub ToScriptID:ScriptID,
|
pub ToScriptID:ScriptID,
|
||||||
pub Policy:Policy,
|
pub Policy:Policy,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyIDResponse{
|
pub struct ScriptPolicyIDResponse{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct UpdateScriptPolicyRequest{
|
pub struct UpdateScriptPolicyRequest{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
@ -165,6 +166,7 @@ pub struct UpdateScriptPolicyRequest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct UpdateSubmissionModelRequest{
|
pub struct UpdateSubmissionModelRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub ModelID:u64,
|
pub ModelID:u64,
|
||||||
@ -172,15 +174,18 @@ pub struct UpdateSubmissionModelRequest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionUploadedRequest{
|
pub struct ActionSubmissionUploadedRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub TargetAssetID:Option<u64>,
|
pub TargetAssetID:Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionAcceptedRequest{
|
pub struct ActionSubmissionAcceptedRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub StatusMessage:String,
|
pub StatusMessage:String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub struct SubmissionID(pub i64);
|
pub struct SubmissionID(pub i64);
|
||||||
|
@ -39,8 +39,7 @@ async fn main()->Result<(),StartupError>{
|
|||||||
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new(cookie));
|
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new(cookie));
|
||||||
|
|
||||||
// maps-service api
|
// maps-service api
|
||||||
let mut api_host_internal=std::env::var("API_HOST_INTERNAL").expect("API_HOST_INTERNAL env required");
|
let api_host_internal=std::env::var("API_HOST_INTERNAL").expect("API_HOST_INTERNAL env required");
|
||||||
api_host_internal+="v1/";
|
|
||||||
let api=submissions_api::internal::Context::new(api_host_internal).map_err(StartupError::API)?;
|
let api=submissions_api::internal::Context::new(api_host_internal).map_err(StartupError::API)?;
|
||||||
|
|
||||||
// nats
|
// nats
|
||||||
|
@ -7,7 +7,7 @@ pub enum PublishError{
|
|||||||
Json(serde_json::Error),
|
Json(serde_json::Error),
|
||||||
Create(rbx_asset::cookie::CreateError),
|
Create(rbx_asset::cookie::CreateError),
|
||||||
SystemTime(std::time::SystemTimeError),
|
SystemTime(std::time::SystemTimeError),
|
||||||
ApiActionSubmissionPublish(submissions_api::Error),
|
ApiActionSubmissionUploaded(submissions_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{
|
||||||
@ -53,7 +53,7 @@ impl Publisher{
|
|||||||
self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
|
self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
|
||||||
SubmissionID:publish_info.SubmissionID,
|
SubmissionID:publish_info.SubmissionID,
|
||||||
TargetAssetID:Some(upload_response.AssetId),
|
TargetAssetID:Some(upload_response.AssetId),
|
||||||
}).await.map_err(PublishError::ApiActionSubmissionPublish)?;
|
}).await.map_err(PublishError::ApiActionSubmissionUploaded)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -21,23 +21,33 @@ fn source_has_illegal_keywords(source:&str)->bool{
|
|||||||
source.find("getfenv").is_some()||source.find("require").is_some()
|
source.find("getfenv").is_some()||source.find("require").is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_source(source:&str)->String{
|
||||||
|
let mut hasher=siphasher::sip::SipHasher::new();
|
||||||
|
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
||||||
|
let hash=std::hash::Hasher::finish(&hasher);
|
||||||
|
format!("{:016x}",hash)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ValidateError{
|
pub enum ValidateError{
|
||||||
Flagged,
|
ScriptFlaggedIllegalKeyword(String),
|
||||||
Blocked,
|
ScriptBlocked(Option<submissions_api::types::ScriptID>),
|
||||||
NotAllowed,
|
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
|
||||||
Get(rbx_asset::cookie::GetError),
|
ModelFileDownload(rbx_asset::cookie::GetError),
|
||||||
ReadDom(ReadDomError),
|
ModelFileDecode(ReadDomError),
|
||||||
ApiGetScriptPolicy(submissions_api::types::SingleItemError),
|
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
|
||||||
ApiGetScript(submissions_api::Error),
|
ApiGetScript(submissions_api::Error),
|
||||||
ApiCreateScript(submissions_api::Error),
|
ApiCreateScript(submissions_api::Error),
|
||||||
ApiCreateScriptPolicy(submissions_api::Error),
|
ApiCreateScriptPolicy(submissions_api::Error),
|
||||||
|
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
|
||||||
ApiUpdateSubmissionModel(submissions_api::Error),
|
ApiUpdateSubmissionModel(submissions_api::Error),
|
||||||
ApiActionSubmissionValidate(submissions_api::Error),
|
ApiActionSubmissionValidate(submissions_api::Error),
|
||||||
WriteDom(rbx_binary::EncodeError),
|
ModelFileRootMustHaveOneChild,
|
||||||
Upload(rbx_asset::cookie::UploadError),
|
ModelFileChildRefIsNil,
|
||||||
Create(rbx_asset::cookie::CreateError),
|
ModelFileEncode(rbx_binary::EncodeError),
|
||||||
|
AssetUpload(rbx_asset::cookie::UploadError),
|
||||||
|
AssetCreate(rbx_asset::cookie::CreateError),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for ValidateError{
|
impl std::fmt::Display for ValidateError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -89,10 +99,10 @@ impl Validator{
|
|||||||
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
|
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
|
||||||
asset_id:validate_info.ModelID,
|
asset_id:validate_info.ModelID,
|
||||||
version:Some(validate_info.ModelVersion),
|
version:Some(validate_info.ModelVersion),
|
||||||
}).await.map_err(ValidateError::Get)?;
|
}).await.map_err(ValidateError::ModelFileDownload)?;
|
||||||
|
|
||||||
// decode dom (slow!)
|
// decode dom (slow!)
|
||||||
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?;
|
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ModelFileDecode)?;
|
||||||
|
|
||||||
/* VALIDATE MAP */
|
/* VALIDATE MAP */
|
||||||
|
|
||||||
@ -105,12 +115,14 @@ impl Validator{
|
|||||||
// check the source for illegal keywords
|
// check the source for illegal keywords
|
||||||
if source_has_illegal_keywords(source){
|
if source_has_illegal_keywords(source){
|
||||||
// immediately abort
|
// immediately abort
|
||||||
return Err(ValidateError::Flagged);
|
// grab path to offending script
|
||||||
|
let path=get_partial_path(&dom,script);
|
||||||
|
return Err(ValidateError::ScriptFlaggedIllegalKeyword(path));
|
||||||
}
|
}
|
||||||
// associate a name and policy with the source code
|
// associate a name and policy with the source code
|
||||||
// policy will be fetched from the database to replace the default policy
|
// policy will be fetched from the database to replace the default policy
|
||||||
script_map.insert(source.clone(),NamePolicy{
|
script_map.insert(source.clone(),NamePolicy{
|
||||||
name:script.name.clone(),
|
name:get_partial_path(&dom,script),
|
||||||
policy:Policy::None,
|
policy:Policy::None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -121,14 +133,12 @@ impl Validator{
|
|||||||
futures::stream::iter(script_map.iter_mut().map(Ok))
|
futures::stream::iter(script_map.iter_mut().map(Ok))
|
||||||
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
|
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
|
||||||
// get the hash
|
// get the hash
|
||||||
let mut hasher=siphasher::sip::SipHasher::new();
|
let hash=hash_source(source.as_str());
|
||||||
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
|
||||||
let hash=std::hash::Hasher::finish(&hasher);
|
|
||||||
|
|
||||||
// fetch the script policy
|
// fetch the script policy
|
||||||
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
|
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
|
||||||
hash:format!("{:016x}",hash).as_str(),
|
hash:hash.as_str(),
|
||||||
}).await.map_err(ValidateError::ApiGetScriptPolicy)?;
|
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
|
||||||
|
|
||||||
// write the policy to the script_map, fetching the replacement code if necessary
|
// write the policy to the script_map, fetching the replacement code if necessary
|
||||||
if let Some(script_policy)=script_policy{
|
if let Some(script_policy)=script_policy{
|
||||||
@ -170,10 +180,22 @@ impl Validator{
|
|||||||
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
||||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
|
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
|
||||||
match script_map.get(source.as_str()).map(|p|&p.policy){
|
match script_map.get(source.as_str()).map(|p|&p.policy){
|
||||||
Some(Policy::Blocked)=>return Err(ValidateError::Blocked),
|
Some(Policy::Blocked)=>{
|
||||||
|
let hash=hash_source(source.as_str());
|
||||||
|
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
|
||||||
|
hash:hash.as_str(),
|
||||||
|
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
|
||||||
|
return Err(ValidateError::ScriptBlocked(script.map(|s|s.ID)));
|
||||||
|
},
|
||||||
None
|
None
|
||||||
|Some(Policy::None)
|
|Some(Policy::None)
|
||||||
=>return Err(ValidateError::NotAllowed),
|
=>{
|
||||||
|
let hash=hash_source(source.as_str());
|
||||||
|
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
|
||||||
|
hash:hash.as_str(),
|
||||||
|
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
|
||||||
|
return Err(ValidateError::ScriptNotYetReviewed(script.map(|s|s.ID)));
|
||||||
|
},
|
||||||
Some(Policy::Allowed)=>(),
|
Some(Policy::Allowed)=>(),
|
||||||
Some(Policy::Delete)=>{
|
Some(Policy::Delete)=>{
|
||||||
modified=true;
|
modified=true;
|
||||||
@ -195,7 +217,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().children()).map_err(ValidateError::WriteDom)?;
|
let &[map_ref]=dom.root().children()else{
|
||||||
|
return Err(ValidateError::ModelFileRootMustHaveOneChild);
|
||||||
|
};
|
||||||
|
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
|
||||||
|
|
||||||
// upload a model lol
|
// upload a model lol
|
||||||
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
|
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
|
||||||
@ -207,18 +232,22 @@ impl Validator{
|
|||||||
ispublic:None,
|
ispublic:None,
|
||||||
allowComments:None,
|
allowComments:None,
|
||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Upload)?;
|
},data).await.map_err(ValidateError::AssetUpload)?;
|
||||||
|
|
||||||
response.AssetId
|
response.AssetId
|
||||||
}else{
|
}else{
|
||||||
|
// grab the map instance from the map re
|
||||||
|
let Some(map_instance)=dom.get_by_ref(map_ref)else{
|
||||||
|
return Err(ValidateError::ModelFileChildRefIsNil);
|
||||||
|
};
|
||||||
// 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{
|
||||||
name:dom.root().name.clone(),
|
name:map_instance.name.clone(),
|
||||||
description:"".to_owned(),
|
description:"".to_owned(),
|
||||||
ispublic:true,
|
ispublic:true,
|
||||||
allowComments:true,
|
allowComments:true,
|
||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Create)?;
|
},data).await.map_err(ValidateError::AssetCreate)?;
|
||||||
|
|
||||||
response.AssetId
|
response.AssetId
|
||||||
};
|
};
|
||||||
@ -291,6 +320,25 @@ fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
||||||
|
struct ParentIter<'a>{
|
||||||
|
dom:&'a rbx_dom_weak::WeakDom,
|
||||||
|
instance:Option<&'a rbx_dom_weak::Instance>,
|
||||||
|
}
|
||||||
|
impl<'a> Iterator for ParentIter<'a>{
|
||||||
|
type Item=&'a rbx_dom_weak::Instance;
|
||||||
|
fn next(&mut self)->Option<Self::Item>{
|
||||||
|
let parent=self.instance.map(|i|i.parent()).and_then(|p|self.dom.get_by_ref(p));
|
||||||
|
core::mem::replace(&mut self.instance,parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tragic:Vec<_>=ParentIter{dom,instance:Some(instance)}.map(|i|i.name.as_str()).collect();
|
||||||
|
tragic.pop();
|
||||||
|
tragic.reverse();
|
||||||
|
tragic.join(".")
|
||||||
|
}
|
||||||
|
|
||||||
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
||||||
let mut scripts=std::vec::Vec::new();
|
let mut scripts=std::vec::Vec::new();
|
||||||
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user