From 98c369bafdeb0026cb4ea266a2a28ab61f849a6b Mon Sep 17 00:00:00 2001 From: Rybasher Date: Tue, 23 Apr 2024 14:32:44 +0300 Subject: [PATCH] feat: update local asset driver --- mirror-web-server/src/app.module.ts | 6 ++- mirror-web-server/src/main.ts | 16 ++++++ mirror-web-server/src/metadata.ts | 2 +- .../space/space-godot-server.controller.ts | 29 ++++++----- mirror-web-server/src/space/space.service.ts | 37 +++++++------ .../src/terrain/terrain.service.ts | 18 ++++--- .../util/file-upload/file-upload.service.ts | 52 +++++++++++-------- 7 files changed, 104 insertions(+), 56 deletions(-) diff --git a/mirror-web-server/src/app.module.ts b/mirror-web-server/src/app.module.ts index 62ca9adb..eab2c673 100644 --- a/mirror-web-server/src/app.module.ts +++ b/mirror-web-server/src/app.module.ts @@ -111,13 +111,17 @@ if ( console.log('Added AnalyticsModule') } -if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { +if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' +) { console.log( 'Using local storage, path: ', join(__dirname, '../', 'localStorage') ) imports.push( ServeStaticModule.forRoot({ + serveRoot: '/assets-storage', rootPath: join(__dirname, '../', 'localStorage') }) ) diff --git a/mirror-web-server/src/main.ts b/mirror-web-server/src/main.ts index eba59379..c4d6e639 100644 --- a/mirror-web-server/src/main.ts +++ b/mirror-web-server/src/main.ts @@ -77,6 +77,22 @@ async function bootstrapMirrorWebServer() { }) app.useWebSocketAdapter(new WsAdapter(app)) + // asset storage driver env validation + if ( + process.env.ASSET_STORAGE_DRIVER === 'GCP' && + !process.env.GCS_BUCKET_PUBLIC + ) { + throw new Error('GCS_BUCKET_PUBLIC is required when using GCP storage') + } + + if ( + (!process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL') && + !process.env.ASSET_STORAGE_URL + ) { + throw new Error('ASSET_STORAGE_URL is required when using LOCAL storage') + } + // Important: This *only* sets up validation pipes for HTTP handlers, NOT Websockets. See the notice here: https://docs.nestjs.com/pipes#global-scoped-pipes app.useGlobalPipes( new ValidationPipe({ diff --git a/mirror-web-server/src/metadata.ts b/mirror-web-server/src/metadata.ts index 6502895e..ce5795ec 100644 --- a/mirror-web-server/src/metadata.ts +++ b/mirror-web-server/src/metadata.ts @@ -19,5 +19,5 @@ export default async () => { ["./space/material-instance/material-instance.schema"]: await import("./space/material-instance/material-instance.schema"), ["./auth/auth.controller"]: await import("./auth/auth.controller") }; - return { "@nestjs/swagger": { "models": [[import("./custom-data/dto/custom-data.dto"), { "CreateCustomDataDto": { data: { required: true, type: () => Object, description: "@description We intentionally use a \"data\" property here so that other keys can be added in the future and the dto doesn't assume that everything in the dto should be saved to the database\nWe also want to be able to store top-level key:value pairs that the user doesn't have free reign access to.\n@date 2023-03-03 00:12" } }, "ICustomDataKeyValuePairUpdateDto": { patchCustomData: { required: false, type: () => Object }, removeCustomDataKeys: { required: false, type: () => [String] } } }], [import("./roles/dto/create-role.dto"), { "CreateRoleDto": { defaultRole: { required: true, enum: t["./roles/models/role.enum"].ROLE }, creator: { required: true, type: () => String }, users: { required: false, type: () => Object }, userGroups: { required: false, type: () => Object } } }], [import("./util/file-upload/dto/file-upload.dto"), { "FileUploadDto": { path: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./asset/dto/create-asset.dto"), { "CreateAssetDto": { name: { required: true, type: () => String, description: "Required properties", maxLength: 300 }, assetType: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, defaultRole: { required: false, description: "Optional Properties", enum: t["./roles/models/role.enum"].ROLE }, description: { required: false, type: () => String, maxLength: 5000 }, mirrorPublicLibrary: { required: false, type: () => Boolean }, customData: { required: false, type: () => String }, thirdPartySourceDisplayName: { required: false, type: () => String, description: "START Section: Third Party Source for Mirror Public Library" }, thirdPartySourceUrl: { required: false, type: () => String }, thumbnail: { required: false, type: () => String, description: "END Section: Third Party Source for Mirror Public Library" }, categories: { required: false, type: () => [String], deprecated: true }, currentFile: { required: false, type: () => String, maxLength: 5000 }, public: { required: false, type: () => Boolean }, initPositionX: { required: false, type: () => Number }, initPositionY: { required: false, type: () => Number }, initPositionZ: { required: false, type: () => Number }, initRotationX: { required: false, type: () => Number }, initRotationY: { required: false, type: () => Number }, initRotationZ: { required: false, type: () => Number }, initScaleX: { required: false, type: () => Number }, initScaleY: { required: false, type: () => Number }, initScaleZ: { required: false, type: () => Number }, collisionEnabled: { required: false, type: () => Boolean }, staticEnabled: { required: false, type: () => Boolean }, massKg: { required: false, type: () => Number }, gravityScale: { required: false, type: () => Number }, objectColor: { required: false, type: () => Object }, tags: { required: false, type: () => t["./tag/models/tags.schema"].Tags } }, "CreateMaterialDto": { materialName: { required: false, type: () => String }, materialTransparencyMode: { required: false, type: () => String }, materialTransparencyProperties: { required: false, type: () => String }, textures: { required: false, type: () => String }, parameters: { required: true, type: () => Object }, externalAssetIds: { required: false, type: () => String }, materialType: { required: true, type: () => String }, code: { required: false, type: () => String } }, "CreateTextureDto": { textureImageFileHashMD5: { required: false, type: () => String }, textureLowQualityFileHashMD5: { required: false, type: () => String }, textureImagePropertyAppliesTo: { required: false, type: () => String } }, "CreateMapDto": { mapName: { required: false, type: () => String }, heightmapAssetId: { required: false, type: () => String }, flatMaterialAssetId: { required: false, type: () => String }, cliffMaterialAssetId: { required: false, type: () => String }, mapSize: { required: false, type: () => Number }, mapPrecision: { required: false, type: () => Number }, heightScale: { required: false, type: () => Number }, layerOffset: { required: false, type: () => Number }, flatUVScale: { required: false, type: () => Number }, cliffUVScale: { required: false, type: () => Number }, flatCliffRatio: { required: false, type: () => Number }, flatColor: { required: false, type: () => Object }, cliffColor: { required: false, type: () => Object }, colormapAssetId: { required: false, type: () => String }, colormapStrength: { required: false, type: () => Number } } }], [import("./asset/dto/paginated-search-asset.dto"), { "PaginatedSearchAssetDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String }, sortKey: { required: true, type: () => String }, sortDirection: { required: true, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: true, type: () => Number }, perPage: { required: true, type: () => Number }, startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number }, type: { required: true, deprecated: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, assetType: { required: true, deprecated: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, assetTypes: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE, isArray: true }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES } }, "PaginatedSearchAssetDtoV2": { populate: { required: true, type: () => [String] }, includeSoftDeleted: { required: false, type: () => Boolean } } }], [import("./asset/dto/update-asset.dto"), { "UpdateAssetDto": { __t: { required: false, type: () => Object, description: "@description If a discriminator/subclassed Asset is being updated, then this must be included. Otherwise, the parent Asset will be used, but because Mongoose is NOT typed under the hood, it won't know about the discriminator classes and thus won't work with properties of the discriminator if the discriminator model isn't used.\n@date 2023-06-07 11:24" } }, "AddAssetPurchaseOptionDto": {} }], [import("./asset/dto/upload-asset-file.dto"), { "UploadAssetFileDto": { userId: { required: true, type: () => String }, assetId: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./user/dto/create-user-access-key.dto"), { "CreateUserAccessKeyDto": { token: { required: true, type: () => String }, adminNote: { required: true, type: () => String } } }], [import("./user/dto/update-user.dto"), { "UpdateUserProfileDto": { email: { required: true, type: () => String }, displayName: { required: true, type: () => String }, publicBio: { required: true, type: () => String }, discordUserId: { required: true, type: () => String }, polygonPublicKey: { required: true, type: () => String }, ethereumPublicKey: { required: true, type: () => String }, twitterUsername: { required: true, type: () => String }, githubUsername: { required: true, type: () => String }, instagramUsername: { required: true, type: () => String }, youtubeChannel: { required: true, type: () => String }, artStationUsername: { required: true, type: () => String }, sketchfabUsername: { required: true, type: () => String }, profileImage: { required: true, type: () => String }, coverImage: { required: true, type: () => String } }, "UpdateUserDeepLinkDto": { deepLinkKey: { required: true, type: () => String }, deepLinkValue: { required: true, type: () => String } }, "UpdateUserTermsDto": { termsAgreedtoClosedAlpha: { required: false, type: () => Boolean }, termsAgreedtoGeneralTOSandPP: { required: false, type: () => Boolean } }, "UpdateUserAvatarDto": { avatarUrl: { required: false, type: () => String } }, "UpdateUserTutorialDto": { shownFirstInSpacePopupV1: { required: false, type: () => Boolean }, shownFirstHomeScreenPopupV1: { required: false, type: () => Boolean }, shownWebAppPopupV1: { required: false, type: () => Boolean } }, "UpdateUserAvatarTypeDto": { avatarType: { required: true, type: () => String }, readyPlayerMeUrlGlb: { required: false, type: () => String } }, "AddRpmAvatarUrlDto": { rpmAvatarUrl: { required: true, type: () => String } }, "RemoveRpmAvatarUrlDto": { rpmAvatarUrl: { required: true, type: () => String } }, "UpsertUserEntityActionDto": { forEntity: { required: true, type: () => String }, actionType: { required: true, type: () => String }, entityType: { required: true, type: () => String }, rating: { required: false, type: () => Number, description: "Optional properties", minimum: 1, maximum: 5 } }, "AddUserCartItemToUserCartDto": { purchaseOptionId: { required: true, type: () => String }, forEntity: { required: true, type: () => String }, entityType: { required: true, type: () => String } } }], [import("./user/dto/upload-profile-file.dto"), { "UploadProfileFileDto": { userId: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./user/dto/add-user-sidebar-tag.dto"), { "AddUserSidebarTagDto": { sidebarTag: { required: true, type: () => String } } }], [import("./asset/dto/add-tag-to-asset.dto"), { "AddTagToAssetDto": { assetId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./environment/dto/update-environment.dto"), { "UpdateEnvironmentDto": {} }], [import("./space-object/dto/create-space-object.dto"), { "CreateSpaceObjectDto": { spaceId: { required: true, type: () => String, description: "Required properties", minLength: 24, maxLength: 24 }, name: { required: true, type: () => String, maxLength: 300 }, asset: { required: true, type: () => String }, parentSpaceObject: { required: false, type: () => String }, description: { required: false, type: () => String }, locked: { required: false, type: () => Boolean }, preloadBeforeSpaceStarts: { required: false, type: () => Boolean }, position: { required: false, type: () => Object }, rotation: { required: false, type: () => Object }, scale: { required: false, type: () => Object }, offset: { required: false, type: () => Object }, collisionEnabled: { required: false, type: () => Boolean }, shapeType: { required: false, type: () => String }, bodyType: { required: false, type: () => String }, staticEnabled: { required: false, type: () => Boolean }, massKg: { required: false, type: () => Number }, gravityScale: { required: false, type: () => Number }, castShadows: { required: false, type: () => Boolean }, visibleFrom: { required: false, type: () => Number }, visibleTo: { required: false, type: () => Number }, visibleFromMargin: { required: false, type: () => Number }, visibleToMargin: { required: false, type: () => Number }, materialAssetId: { required: false, type: () => String }, objectColor: { required: false, type: () => Object }, objectTexture: { required: false, type: () => String }, objectTextureSize: { required: false, type: () => Number, deprecated: true }, objectTextureSizeV2: { required: false, type: () => Object }, objectTextureOffset: { required: false, type: () => Object }, objectTextureTriplanar: { required: false, type: () => Boolean }, objectTextureRepeat: { required: false, type: () => Boolean }, audioAutoPlay: { required: false, type: () => Boolean }, audioLoop: { required: false, type: () => Boolean }, audioIsSpatial: { required: false, type: () => Boolean }, audioPitch: { required: false, type: () => Number }, audioBaseVolume: { required: false, type: () => Number }, audioSpatialMaxVolume: { required: false, type: () => Number }, audioSpatialRange: { required: false, type: () => Number }, surfaceMaterialId: { required: false, type: () => [Object] }, scriptEvents: { required: false, type: () => [Object] }, extraNodes: { required: false, type: () => [Object] } } }], [import("./util/dto-generic/batch.dto"), { "Batch": { batch: { required: true } } }], [import("./space-object/dto/update-space-object.dto"), { "UpdateSpaceObjectDto": {} }], [import("./space-object/dto/update-batch-space-object.dto"), { "UpdateBatchSpaceObjectDto": {} }], [import("./space-object/dto/add-tag-to-space-object.dto"), { "AddTagToSpaceObjectDto": { spaceObjectId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./space-object/dto/paginated-search-space-object.dto"), { "PaginatedSearchSpaceObjectDto": { field: { required: false, type: () => String }, search: { required: false, type: () => String }, sortKey: { required: false, type: () => String }, sortDirection: { required: false, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: false, type: () => Number }, perPage: { required: false, type: () => Number }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES } } }], [import("./space-object/dto/update-space-object-tags.dto"), { "UpdateSpaceObjectTagsDto": { spaceObjectId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./terrain/dto/create-terrain.dto"), { "CreateTerrainDto": { name: { required: true, type: () => String, maxLength: 300 }, material: { required: false, type: () => String }, description: { required: false, type: () => String, maxLength: 5000 }, public: { required: false, type: () => Boolean }, generator: { required: false, type: () => String }, noiseType: { required: false, type: () => Number }, positionX: { required: false, type: () => Number }, positionY: { required: false, type: () => Number }, positionZ: { required: false, type: () => Number }, heightStart: { required: false, type: () => Number }, heightRange: { required: false, type: () => Number }, seed: { required: false, type: () => Number } } }], [import("./terrain/dto/update-terrain.dto"), { "UpdateTerrainDto": {} }], [import("./space/dto/create-space.dto"), { "CreateSpaceDto": { name: { required: true, type: () => String, maxLength: 300 }, type: { required: true, type: () => String, description: "Optional properties" }, users: { required: false, type: () => Object, description: "START Section: Roles" }, userGroups: { required: false, type: () => Object }, terrain: { required: false, type: () => String, description: "END Section: Roles" }, environment: { required: false, type: () => String }, ownerUserGroup: { required: false, type: () => String }, template: { required: true, type: () => String }, lowerLimitY: { required: false, type: () => Number }, description: { required: false, type: () => String, maxLength: 5000 }, images: { required: false, type: () => [String] }, publicBuildPermissions: { required: false, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, tags: { required: false, type: () => t["./tag/models/tags.schema"].Tags }, maxUsers: { required: false, type: () => Number } } }], [import("./space/dto/update-space.dto"), { "UpdateSpaceDto": { patchCustomData: { required: false, type: () => Object, description: "@description These properties are PATCHED onto custom data (overwrites declared key-value pairs, but doesn't affect other key/value parirs)\n@date 2023-04-05 23:27" }, activeSpaceVersion: { required: false, type: () => String }, removeCustomDataKeys: { required: false, type: () => [String], description: "@description These properties are deleted from custom data. Other properties will not be affected\n@date 2023-04-05 23:27" }, patchSpaceVariablesData: { required: false, type: () => Object }, removeSpaceVariablesDataKeys: { required: false, type: () => [String], description: "@description These properties are deleted from spaceVariablesData. Other properties will not be affected\n@date 2023-06-05 16:59:26" }, tagsV2: { required: false, type: () => [String] }, scriptIds: { required: false, type: () => [String] }, scriptInstances: { required: false, type: () => [Object] }, materialInstances: { required: false, type: () => [Object] }, publicBuildPermissions: { required: true, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, kickRequests: { required: true, type: () => [String] } }, "CreateNewSpaceVersionDto": { updateSpaceWithActiveSpaceVersion: { required: true, type: () => Boolean }, name: { required: true, type: () => String } }, "SpaceCopyFromTemplateDto": { name: { required: true, type: () => String }, description: { required: true, type: () => String }, publicBuildPermissions: { required: true, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, maxUsers: { required: false, type: () => Number, default: 24 } } }], [import("./space/dto/upload-space-files.dto"), { "UploadSpaceFilesDto": { spaceId: { required: true, type: () => String }, files: { required: true, type: () => [Object] } } }], [import("./space/dto/paginated-search-space.dto"), { "PaginatedSearchSpaceDto": { field: { required: false, type: () => String }, search: { required: false, type: () => String }, sortKey: { required: false, type: () => String }, sortDirection: { required: false, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: false, type: () => Number }, perPage: { required: false, type: () => Number }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } } }], [import("./space-variable/dto/space-variables-data.dto"), { "CreateSpaceVariablesDataDocumentDto": { data: { required: true, type: () => Object, description: "@description We intentionally use a \"data\" property here so that other keys can be added in the future and the dto doesn't assume that everything in the dto should be saved to the database\nWe also want to be able to store top-level key:value pairs that the user doesn't have free reign access to.\n@date 2023-03-03 00:12" } } }], [import("./zone/dto/create-zone.dto"), { "CreateZoneDto": { zoneMode: { required: true, type: () => String, maxLength: 100 }, owner: { required: false, type: () => String, description: "@description The UserId of the user who created the zone. It's optional, but we do want this. It's just set to optional for other CreateZoneDto's validation on the cron job.\n@date 2023-06-17 22:42", minLength: 24, maxLength: 24 }, state: { required: true, type: () => String, maxLength: 100 }, ipAddress: { required: true, type: () => String, maxLength: 100 }, port: { required: true, type: () => Number }, uuid: { required: true, type: () => String, maxLength: 100 }, url: { required: true, type: () => String, maxLength: 100 }, gdServerVersion: { required: true, type: () => String, maxLength: 50 }, containerLastRefreshed: { required: false, type: () => Date }, name: { required: false, type: () => String, maxLength: 300 }, description: { required: false, type: () => String }, space: { required: false, type: () => String, description: "Only required if Build mode, but still run validation in case it exists for some reason (such as manual zone/server creation on the scaler without validation via mirror-server)", minLength: 24, maxLength: 24 }, spaceVersion: { required: false, type: () => String, description: "Only required if Play mode, but still run validation in case it exists for some reason(such as manual zone/server creation on the scaler without validation via mirror-server)", minLength: 24, maxLength: 24 } } }], [import("./zone/dto/update-zone.dto"), { "UpdateZoneDto": { id: { required: false, type: () => String } } }], [import("./zone/dto/create-play-server.dto"), { "CreatePlayServerDto": { zoneName: { required: false, type: () => String } } }], [import("./space/dto/add-tag-to-space.dto"), { "AddTagToSpaceDto": { spaceId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./mirror-db/dto/update-mirror-db-record.dto"), { "UpdateMirrorDBRecordDto": { recordData: { required: true, type: () => Object } } }], [import("./script-entity/dto/create-script-entity.dto"), { "CreateScriptEntityDto": { blocks: { required: true, type: () => [Object] } } }], [import("./script-entity/dto/update-script-entity.dto"), { "UpdateScriptEntityDto": {} }], [import("./roles/dto/set-user-role-for-one.dto"), { "SetUserRoleForOneDto": { targetUserId: { required: true, type: () => String }, role: { required: true, enum: t["./roles/models/role.enum"].ROLE } }, "RemoveUserRoleForOneDto": { targetUserId: { required: true, type: () => String, maxLength: 24 } } }], [import("./space/dto/update-space-tags.dto"), { "UpdateSpaceTagsDto": { spaceId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./space/dto/populate-space.dto"), { "PopulateSpaceDto": { populateCreator: { required: false, type: () => Boolean }, populateUsersPresent: { required: false, type: () => Boolean } } }], [import("./asset/dto/search-asset.dto"), { "SearchAssetDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String } } }], [import("./asset/dto/update-asset-tags.dto"), { "UpdateAssetTagsDto": { assetId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./asset/dto/include-soft-deleted-asset.dto"), { "IncludeSoftDeletedAssetDto": { includeSoftDeleted: { required: false, type: () => Boolean } } }], [import("./asset/dto/upsert-asset.dto"), { "UpsertAssetDto": { name: { required: true, type: () => String }, assetType: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, description: { required: true, type: () => String }, owner: { required: true, type: () => String }, initPositionX: { required: true, type: () => Number }, initPositionY: { required: true, type: () => Number }, initPositionZ: { required: true, type: () => Number }, initRotationX: { required: true, type: () => Number }, initRotationY: { required: true, type: () => Number }, initRotationZ: { required: true, type: () => Number }, initScaleX: { required: true, type: () => Number }, initScaleY: { required: true, type: () => Number }, initScaleZ: { required: true, type: () => Number }, collisionEnabled: { required: true, type: () => Boolean }, staticEnabled: { required: true, type: () => Boolean }, massKg: { required: true, type: () => Number }, gravityScale: { required: true, type: () => Number }, objectColor: { required: true, type: () => Object } } }], [import("./user/dto/submit-user-access-key.dto"), { "SubmitUserAccessKeyDto": { key: { required: true, type: () => String } } }], [import("./user/dto/update-user-sidebar-tags.dto"), { "UpdateUserSidebarTagsDto": { sidebarTags: { required: true, type: () => [String] } } }], [import("./user-groups/dto/create-group-users-invite.dto"), { "CreateUserGroupInviteDto": { group: { required: true, type: () => String, description: "Required properties" }, unlimited: { required: true, type: () => Boolean }, used: { required: true, type: () => Boolean }, status: { required: true, enum: t["./option-sets/user-group-invite-statuses"].USER_GROUP_INVITE_STATUSES }, completed: { required: true, type: () => Boolean }, creator: { required: true, type: () => String }, recipient: { required: true, type: () => String }, expirationDate: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group-users-membership.dto"), { "CreateUserGroupMembershipDto": { group: { required: true, type: () => String }, status: { required: true, enum: t["./option-sets/user-group-membership-statuses"].USER_GROUP_MEMBERSHIP_STATUSES }, creator: { required: true, type: () => String }, user: { required: true, type: () => String }, role: { required: true, enum: t["./option-sets/group-users-roles"].GROUP_ROLE }, expirationDate: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group-users-request.dto"), { "CreateUserGroupRequestDto": { group: { required: true, type: () => String }, unlimited: { required: true, type: () => Boolean }, used: { required: true, type: () => Boolean }, status: { required: true, enum: t["./option-sets/user-group-invite-statuses"].USER_GROUP_INVITE_STATUSES }, completed: { required: true, type: () => Boolean }, completedDate: { required: true, type: () => Date }, creator: { required: true, type: () => String }, updatedAt: { required: true, type: () => Date }, createdAt: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group.users.dto"), { "CreateUserGroupDto": { name: { required: true, type: () => String, description: "Required properties" }, public: { required: true, type: () => Boolean }, publicDescription: { required: true, type: () => String, description: "Optional properties" }, primaryContact: { required: true, type: () => String }, moderators: { required: true, type: () => [String] }, owners: { required: true, type: () => [String] }, image: { required: true, type: () => String }, discordUrl: { required: true, type: () => String }, polygonDaoContractPublicKey: { required: true, type: () => String }, ethereumDaoContractPublicKey: { required: true, type: () => String }, twitterUrl: { required: true, type: () => String }, websiteUrl: { required: true, type: () => String }, creator: { required: true, type: () => String } } }], [import("./user-groups/dto/update-group.users.dto"), { "UpdateUserGroupDto": {} }], [import("./space/material-instance/dto/create-material-instance.dto"), { "CreateMaterialInstanceDto": { parameters: { required: true, type: () => Object }, spaceId: { required: true, type: () => String } } }], [import("./space/material-instance/dto/update-material-instance.dto"), { "UpdateMaterialInstanceDto": { parameters: { required: true, type: () => Object } } }], [import("./zone/dto/populate-zone-owner.dto"), { "PopulateZoneOwnerDto": { populateOwner: { required: false, type: () => Boolean } } }], [import("./godot-server-override-config/dto/create-godot-server-override-config.dto"), { "CreateGodotServerOverrideConfigDto": { spaceId: { required: true, type: () => String, minLength: 24, maxLength: 24 } } }], [import("./block/dto/create-block.dto"), { "CreateBlockDto": { name: { required: true, type: () => String, description: "Required properties" }, blockType: { required: true, type: () => String }, description: { required: false, type: () => String, description: "Optional Properties", maxLength: 1000 }, mirrorPublicLibrary: { required: false, type: () => Boolean } } }], [import("./block/dto/update-block.dto"), { "UpdateBlockDto": {} }], [import("./favorite/dto/create-favorite.dto"), { "CreateFavoriteDto": { asset: { required: true, type: () => String }, user: { required: true, type: () => String }, creator: { required: true, type: () => String } } }], [import("./favorite/dto/update-favorite.dto"), { "UpdateFavoriteDto": {} }], [import("./stripe/dto/token.dto"), { "AddBank": { token: { required: true, type: () => String } }, "AddCard": { token: { required: true, type: () => String } }, "CardToken": { number: { required: true, type: () => String }, exp_month: { required: true, type: () => String }, exp_year: { required: true, type: () => String }, cvc: { required: true, type: () => String } } }], [import("./stripe/dto/paymentIntent.dto"), { "PaymentIntentDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, payment_method: { required: true, type: () => String } } }], [import("./stripe/dto/transfers.dto"), { "TransfersDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, destination: { required: true, type: () => String } } }], [import("./stripe/dto/subscription.dto"), { "SubscriptionDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, productId: { required: true, type: () => String }, priceId: { required: true, type: () => String }, destination: { required: true, type: () => String } }, "ProductDto": { name: { required: true, type: () => String }, description: { required: true, type: () => String } } }], [import("./stripe/dto/metadata.dto"), { "StripeSubscriptionMetadataDto": { userId: { required: true, type: () => String } } }], [import("./tag/dto/create-tag.dto"), { "CreateTagDto": { name: { required: true, type: () => String }, tagType: { required: true, description: "Optional", enum: t["./option-sets/tag-type"].TAG_TYPE }, mirrorPublicLibrary: { required: true, type: () => Boolean }, public: { required: true, type: () => Boolean }, parentTag: { required: true, type: () => String } }, "CreateThirdPartySourceTagDto": { thirdPartySourceHomePageUrl: { required: true, type: () => String }, thirdPartySourcePublicDescription: { required: true, type: () => String, description: "Optional" }, thirdPartySourceTwitterUrl: { required: true, type: () => String }, thirdPartySourceTMUserId: { required: true, type: () => String } } }], [import("./tag/dto/update-tag.dto"), { "UpdateTagDto": {} }], [import("./user-feedback/dto/create-user-feedback.dto"), { "CreateUserFeedbackItemDto": { name: { required: true, type: () => String }, description: { required: false, type: () => String, description: "Optional" }, userFeedbackType: { required: false, type: () => String }, USER_FEEDBACK_ITEM_STATUS: { required: false, type: () => String }, public: { required: false, type: () => Boolean } }, "CreateVoteOnUserFeedbackItemDto": { userFeedbackItemId: { required: true, type: () => String }, vote: { required: true, type: () => String } }, "CreateUserFeedbackItemFeatureRequestDto": {}, "CreateUserFeedbackItemBugDto": {} }], [import("./user-feedback/dto/update-user-feedback.dto"), { "UpdateUserFeedbackDto": {} }], [import("./user-feedback/dto/create-user-feedback-comment.dto"), { "CreateUserFeedbackCommentDto": { text: { required: true, type: () => String }, userFeedbackItemId: { required: true, type: () => String } } }], [import("./asset/dto/asset.dto"), { "GetAssetDto": { startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } }, "AssetParamsDto": { startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } } }], [import("./space/dto/search-space.dto"), { "SearchSpaceDto": { searchField: { required: true, type: () => String }, searchString: { required: true, type: () => String } } }], [import("./auth/dto/DevLoginUserEmailPassword.dto"), { "DevLoginUserEmailPassword": { email: { required: true, type: () => String }, password: { required: true, type: () => String } } }], [import("./user-groups/dto/search-group.dto"), { "SearchUserGroupDto": { searchField: { required: true, type: () => String }, searchstring: { required: true, type: () => String } } }], [import("./zone/dto/paginated-search-zone.dto"), { "PaginatedSearchZoneDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String }, sortKey: { required: true, type: () => String }, sortDirection: { required: true, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: true, type: () => Number }, perPage: { required: true, type: () => Number } } }], [import("./zone/dto/update-zone-status.dto"), { "UpdateZoneStatusDto": { id: { required: false, type: () => String }, uuid: { required: false, type: () => String }, state: { required: false, type: () => String }, url: { required: false, type: () => String }, version: { required: false, type: () => String }, address: { required: false, type: () => String }, port: { required: false, type: () => Number } } }]], "controllers": [[import("./app.controller"), { "AppController": { "getHello": { type: String }, "getHealth": { type: String }, "throwIntentionalError": {} } }], [import("./space/space.controller"), { "SpaceController": { "getAllPublicForUser": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: [Object] }, "getAllPublicForUserV2": { description: "Same as the above but no populate\n@description Retrieves a collection user's public spaces as public data\nid prefix added to prevent wildcard route clashes with file method order", type: [Object] }, "search": { description: "@description Retrieves a collection of space's as public data", type: [t["./space/space.schema"].SpacePublicData] }, "searchV2": { description: "@description Same as the above but no populate\n@date 2023-07-12 23:59", type: [t["./space/space.schema"].SpacePublicData] }, "findAllForMeWhereOwner": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: [Object] }, "findAllForMeWhereOwnerPaginatedV2": {}, "findAllForMeWhereOwnerPaginatedV3": { description: "@description This is the same as me-v2, but no populate\n@date 2023-07-12 23:53" }, "getPopularSpaces": { type: [Object] }, "getFavoriteSpaces": { type: [Object] }, "getRecentSpaces": { type: [Object] }, "getSpacesByTags": {}, "addTagToSpaceWithRoleChecks": { type: Object }, "updateSpaceTagsByTypeWithRoleChecks": { type: Object }, "deleteTagFromSpaceWithRoleChecks": {}, "findDiscoverSpacesForUser": { type: [Object] }, "findDiscoverSpacesForUserPaginatedV2": { type: Object }, "findDiscoverSpacesForUserPaginatedV3": { description: "@description This is the same as discover-v2, but no populate\n@date 2023-07-12 23:53", type: Object }, "findSpaceTemplates": { description: "@description This is the same as discover-v2, but no populate\n@date 2023-07-12 23:53", type: [Object] }, "getPublishedSpaces": { description: "@description Returns all Spaces with an activeSpaceVersion\n@date 2023-06-10 22:23" }, "getPublishedSpacesV2": { description: "@description same as get-published-spaces, but no populate\n@date 2023-07-12 23:54" }, "getLatestPublishedSpaceBySpaceId": { type: Object }, "refreshStats": { type: Object }, "findOne": { type: Object }, "findAllForUserV2": { type: [Object] }, "findAllForUser": { type: [Object] }, "create": { type: Object }, "update": { type: Object }, "remove": { type: Object }, "clearVoxels": {}, "copy": { type: Object }, "copyFromTemplate": { type: Object }, "remixSpace": { type: Object }, "publish": { type: Object }, "getPublishedSpacesBySpaceId": { type: [Object] }, "uploadPublic": { type: Object }, "restoreSpaceFromSpaceVersion": {}, "setUserRoleForOne": { description: "START Section: Owner permissions for role modification", type: Object }, "removeUserRoleForOne": { type: Object }, "kickMe": { description: "END Section: Owner permissions for role modification", type: Object } } }], [import("./terrain/terrain.controller"), { "TerrainController": { "findAllForUser": { type: [Object] }, "findAllPublic": { type: [Object] }, "findOne": { type: Object }, "create": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./util/file-upload/file-upload.controller"), { "FileUploadController": { "batchAssetUploadFromQueueBucket": {} } }], [import("./asset/asset.controller"), { "AssetController": { "search": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: [Object] }, "searchV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:49", type: [Object] }, "getMirrorPublicLibraryAssets": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: [Object] }, "getMirrorPublicLibraryAssetsV2": { description: "@description Same as getMirrorPublicLibraryAssets, but doesn't populate the fields\n@date 2023-07-12 23:33", type: [Object] }, "create": { type: Object }, "getUserRecentInstancedAssets": { type: [Object] }, "createWithUpload": { type: Object }, "getAssetsForMe": { description: "Get player's created assets\nTODO this needs to be paginated", type: [Object] }, "getAssetsForMeV2": { description: "Same as above but no populate\nTODO this needs to be paginated", type: [Object] }, "getAllAccessibleAssetsOfUser": { description: "Get all player's accessible assets. Accessible assets are assets that are public, or assets that are private but owned by the user.", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getAllAccessibleAssetsOfUserV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:45", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getRecentAssetsForUser": { type: [Object] }, "getRecentAssetsForUserV2": { type: [Object] }, "getPaginatedMyAssets": { type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMyAssetsV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:43", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMirrorAssets": { type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMirrorAssetsV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:41", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "findAllForUser": { type: [Object] }, "findOneAssetUsage": { type: t["./asset/asset.models"].AssetUsageApiResponse }, "getAssetsByTag": {}, "addTagToAssetsWithRoleChecks": { type: Object }, "updateAssetTagsByTypeWithRoleChecks": { type: Object }, "deleteTagFromAssetWithRoleChecks": {}, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object }, "undoAssetSoftDelete": { description: "@description This endpoint is used to undo soft delete of an asset.\n(Remove isSoftDeleted and softDeletedAt fields from the asset document)\n\n@date 2023-11-23", type: String }, "upload": { type: Object }, "uploadPublic": { type: Object }, "uploadThumbnail": { type: Object }, "getAsset": {}, "addAssetPurchaseOption": { type: Object }, "deleteAssetPurchaseOption": { type: Object } } }], [import("./user/user.controller"), { "UserController": { "findOne": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: t["./user/user.schema"].UserPublicData }, "findOneWithPublicProfile": { description: "@description Retrieves a Users public profile data including populated\n fields like public assets and public groups", type: t["./user/user.schema"].User }, "search": { description: "@description Retrieves a collection of Users public data", type: [t["./user/user.schema"].UserPublicData] }, "getCurrentUser": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: Object }, "getUserRecents": { type: Object }, "uploadPublic": { description: "TODO - Would be nice to have 1 file upload endpoint that can handle all entity types and paths\n move logic to service" }, "updateProfile": { type: Object }, "updateUserTutorial": { type: Object }, "updateDeepLink": { type: Object }, "updateAvatar": { type: Object }, "updateTermsAgreedTo": { type: Object }, "updateAvatarType": { type: Object }, "getUserEntityAction": { type: Object }, "getUserEntityActionsByMeForEntity": { type: [Object] }, "upsertUserEntityAction": { type: Object }, "deleteUserEntityAction": { type: Object }, "getMyFriends": { description: "START Section: Friends and friend requests ------------------------------------------------------", type: [t["./user/user.service"].Friend] }, "getMyFriendRequests": { type: [t["./user/user.service"].Friend] }, "acceptFriendRequest": { type: [t["./user/user.service"].Friend] }, "rejectFriendRequest": { type: [t["./user/user.service"].Friend] }, "getSentFriendRequests": { type: [t["./user/user.service"].Friend] }, "sendFriendRequest": { type: t["./user/user.schema"].User }, "removeFriend": { type: [t["./user/user.service"].Friend] }, "getUserCart": { description: "END Section: Friends and friend requests ------------------------------------------------------", type: Object }, "addUserCartItemToUserCart": { type: Object }, "removeAllUserItemsFromCart": { type: Object }, "removeUserCartItemFromUserCart": { type: Object }, "addRpmAvatarUrl": { description: "END Section: Cart ------------------------------------------------------", type: Object }, "createSignUpKey": { type: Object }, "submitUserAccessKey": {}, "removeRpmAvatarUrl": { description: "@description Removes an RPM url from readyPlayerMeAvatarUrls in Mongo", type: Object }, "getUserSidebarTags": { type: [String] }, "addUserSidebarTag": { type: String }, "updateUserSidebarTags": { type: [String] }, "deleteUserSidebarTag": {} } }], [import("./environment/environment.controller"), { "EnvironmentController": { "create": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./user-groups/user-group.controller"), { "UserGroupController": { "create": { type: Object }, "getAllGroupsForMe": { description: "@description Find all groups for current user", type: Object }, "getAllGroupInvitesForMe": { description: "@description Find all invites for the current user", type: Object }, "findPublicGroupMembershipForOtherUser": { description: "@description This is used for another user, NOT the current user,\nso we only get the PUBLIC groups that the person is a part of\nTODO - add ApiOkResponse type for UserGroupMembership", type: [Object] }, "getGroupMembershipForMe": { description: "@description Find all group members of current user\nTODO - add ApiOkResponse type for UserGroupMembership", type: Object }, "search": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./space/space-godot-server.controller"), { "SpaceGodotServerController": { "getLatestPublishedSpaceBySpaceId": { description: "***************************\n TEMP: AUTHED ENDPOINT UP HERE FOR 2023-03-17 13:53:32 RELEASE ISSUE\nWe should likely refactor our pattern for classes because order of routes DOES matter in NestJS, so being @Public() up top can cause issues\n**************************", type: Object }, "getActiveSpaceVersionForSpaceBySpaceId": { type: Object }, "updateTerrain": { description: "***************************\n END TEMP: AUTHED ENDPOINT UP HERE FOR 2023-03-17 13:53:32 RELEASE ISSUE\n**************************" } } }], [import("./mirror-server-config/mirror-server-config.controller"), { "MirrorServerConfigController": { "getConfig": { type: Object }, "setGdServerVersion": { type: Object } } }], [import("./space/material-instance/material-instance.controller"), { "MaterialInstanceController": { "create": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "findOne": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "update": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "delete": { type: String } } }], [import("./zone/zone.controller"), { "ZoneController": { "joinBuildServer": { description: "@description Requests a zone server with a specific space id and launches the server if needed for BUILD MODE", type: Object }, "getPlayServersForSpaceVersionId": { description: "@description gets Play servers for a spaceVersionId", type: [Object] }, "getPlayServersForSpaceId": { description: "@description gets Play servers for a spaceId", type: [Object] }, "joinPlayServerByZoneId": { description: "@description joins a play server by spaceVersionId and returns the active session", type: Object }, "joinPlayServerBySpaceId": { description: "@description joins a play server by spaceId, which looks up the activeSpaceVersion and finds the spaceVersion, then returns the zone. If a zone doesn't exist, it will create it", type: Object }, "createPlayServerWithSpaceVersion": { description: "@description joins a play server by spaceVersionId and returns the active session", type: Object }, "createPlayServerWithSpace": { type: Object }, "findAllZonesBySpaceId": { description: "@description Get all the Zone entities associated with a space id.", type: [Object] }, "findAllZonesByUserId": { description: "@description Get all the Zone entities associated with a user id.", type: [Object] }, "findOneZone": { description: "@description Retrieves a zone entity.", type: Object }, "updateOneZone": { description: "@description Update the user controlled values of a zone entity (Name, Description, Space)", type: Object }, "stopAllActiveZones": { description: "@description Stops all zone servers and updates their corresponding entities.\nQueries for every active Zone Server, requests that they shut down, and updates corresponding Zone entities." } } }], [import("./godot-server-override-config/godot-server-override-config.controller"), { "GodotServerOverrideConfigController": { "findOne": { type: String }, "findOneAlias": { type: String }, "create": { type: Object } } }], [import("./mirror-db/mirror-db.controller"), { "MirrorDBController": { "getRecordFromMirrorDBById": { type: Object }, "getRecordFromMirrorDBBySpaceId": { type: Object }, "getRecordFromMirrorDBBySpaceVersionId": { type: Object }, "updateRecordInMirrorDBById": { type: Object }, "deleteRecordFromMirrorDBById": { type: String } } }], [import("./script-entity/script-entity.controller"), { "ScriptEntityController": { "getRecentScripts": { type: [Object] }, "create": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "delete": { type: Object } } }], [import("./space-object/space-object.controller"), { "SpaceObjectController": { "create": { type: Object }, "createAlias": { type: Object }, "copy": { type: Object }, "findAllBySpaceId": { type: [Object] }, "getSpaceObjectsByTag": {}, "updateSpaceObjectTagsByTypeWithRoleChecks": { type: Object }, "addTagToSpaceObjectWithRoleChecks": { type: Object }, "deleteTagFromSpaceObjectWithRoleChecks": {}, "searchSpaceObjectsPaginated": {}, "findOne": { type: Object }, "update": { type: Object }, "remove": {} } }], [import("./auth/auth.controller"), { "AuthController": { "createUserWithEmailPasswordAndType": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************" }, "authedUserCreate": { type: t["./auth/auth.controller"].CreateAuthUserResponse }, "convertAnonymousAccountToFull": {}, "deleteAccount": {} } }], [import("./auth/auth-test.controller"), { "AuthTestController": { "deleteTestAccount": { description: "@description Only used in when NODE_ENV === NODE_ENV.TEST for e2e. Deletes the test account. Checks if the email is an e2e email that ends in @themirror.space. This is a POST so that a body can easily be included (no body in DELETE requests)\n@date 2023-03-16 01:01" } } }], [import("./block/block.controller"), { "BlockController": { "create": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./favorite/favorite.controller"), { "FavoriteController": { "create": { type: Object }, "findAllForUser": { type: [Object] }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./storage/storage.controller"), { "StorageController": { "getClientVersion": { description: "Public endpoint for getting the current version of the client application.", type: Object }, "getClientUrl": { description: "Public endpoint for getting the client application download URL for the target platform parameter.", type: String }, "getClientUrls": { description: "Public endpoint for getting all supported platforms client application download URLs.", type: Object }, "downloadFile": {} } }], [import("./stripe/stripe.controller"), { "StripeController": { "setupIntent": { description: "2023-07-24 11:12:23 Note: I'm commenting these out because it was old code and I don't think we need to expose all these. The implementation also wasn't secure with allowing an userId to be specified." }, "createCustomerAccount": { type: Object }, "createConnectAccount": { type: Object }, "deleteConnectAccount": { type: Object }, "createCard": { type: [Object] }, "getCardsList": { type: Object }, "getStripeAccountInfo": { type: Object }, "addBankAccount": { type: Object }, "deleteCard": { type: [Object] }, "setDefaultPaymentMethod": { type: Object }, "createPaymentIntent": {}, "getPaymentMethods": { type: Object }, "transfersAmount": { type: Object }, "createProduct": { type: Object }, "getAllProductsWithPrice": { type: Object }, "createSubscription": {}, "pauseSubscription": { type: Object }, "resumeSubscription": { type: Object }, "deleteSubscription": { type: Object }, "createDashboardLink": { type: Object }, "createCustomerPortalLink": { type: Object }, "handleStripeWebhook": { type: Object } } }], [import("./tag/tag.controller"), { "TagController": { "create": { type: Object }, "findAllMirrorPublicLibraryTags": { type: [Object] }, "findAllThemeTags": { type: [Object] }, "getTagTypes": { type: [String] }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./user-feedback/user-feedback.controller"), { "UserFeedbackController": { "findNewestPublicUserFeedbackItems": { description: "PUBLICLY ACCESSIBLE ENDPOINTS", type: [Object] }, "findTopPublicUserFeedbackItems": { type: [Object] }, "getUserFeedbackItemTypes": { type: [String] }, "findOne": { type: Object }, "findComments": { type: [Object] }, "findAllPublicUserFeedbackItems": { type: [Object] }, "create": { type: Object }, "voteOnUserFeedbackItem": { type: Object }, "createComment": { type: Object }, "update": { type: Object }, "removeUserFeedbackItem": { type: Object }, "removeComment": { type: Object } } }]] } }; + return { "@nestjs/swagger": { "models": [[import("./custom-data/dto/custom-data.dto"), { "CreateCustomDataDto": { data: { required: true, type: () => Object, description: "@description We intentionally use a \"data\" property here so that other keys can be added in the future and the dto doesn't assume that everything in the dto should be saved to the database\nWe also want to be able to store top-level key:value pairs that the user doesn't have free reign access to.\n@date 2023-03-03 00:12" } }, "ICustomDataKeyValuePairUpdateDto": { patchCustomData: { required: false, type: () => Object }, removeCustomDataKeys: { required: false, type: () => [String] } } }], [import("./roles/dto/create-role.dto"), { "CreateRoleDto": { defaultRole: { required: true, enum: t["./roles/models/role.enum"].ROLE }, creator: { required: true, type: () => String }, users: { required: false, type: () => Object }, userGroups: { required: false, type: () => Object } } }], [import("./util/file-upload/dto/file-upload.dto"), { "FileUploadDto": { path: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./asset/dto/create-asset.dto"), { "CreateAssetDto": { name: { required: true, type: () => String, description: "Required properties", maxLength: 300 }, assetType: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, defaultRole: { required: false, description: "Optional Properties", enum: t["./roles/models/role.enum"].ROLE }, description: { required: false, type: () => String, maxLength: 5000 }, mirrorPublicLibrary: { required: false, type: () => Boolean }, customData: { required: false, type: () => String }, thirdPartySourceDisplayName: { required: false, type: () => String, description: "START Section: Third Party Source for Mirror Public Library" }, thirdPartySourceUrl: { required: false, type: () => String }, thumbnail: { required: false, type: () => String, description: "END Section: Third Party Source for Mirror Public Library" }, categories: { required: false, type: () => [String], deprecated: true }, currentFile: { required: false, type: () => String, maxLength: 5000 }, fileHash: { required: false, type: () => String, maxLength: 64 }, public: { required: false, type: () => Boolean }, initPositionX: { required: false, type: () => Number }, initPositionY: { required: false, type: () => Number }, initPositionZ: { required: false, type: () => Number }, initRotationX: { required: false, type: () => Number }, initRotationY: { required: false, type: () => Number }, initRotationZ: { required: false, type: () => Number }, initScaleX: { required: false, type: () => Number }, initScaleY: { required: false, type: () => Number }, initScaleZ: { required: false, type: () => Number }, collisionEnabled: { required: false, type: () => Boolean }, staticEnabled: { required: false, type: () => Boolean }, massKg: { required: false, type: () => Number }, gravityScale: { required: false, type: () => Number }, objectColor: { required: false, type: () => Object }, tags: { required: false, type: () => t["./tag/models/tags.schema"].Tags } }, "CreateMaterialDto": { materialName: { required: false, type: () => String }, materialTransparencyMode: { required: false, type: () => String }, materialTransparencyProperties: { required: false, type: () => String }, textures: { required: false, type: () => String }, parameters: { required: true, type: () => Object }, externalAssetIds: { required: false, type: () => String }, materialType: { required: true, type: () => String }, code: { required: false, type: () => String } }, "CreateTextureDto": { textureImageFileHashMD5: { required: false, type: () => String }, textureLowQualityFileHashMD5: { required: false, type: () => String }, textureImagePropertyAppliesTo: { required: false, type: () => String } }, "CreateMapDto": { mapName: { required: false, type: () => String }, heightmapAssetId: { required: false, type: () => String }, flatMaterialAssetId: { required: false, type: () => String }, cliffMaterialAssetId: { required: false, type: () => String }, mapSize: { required: false, type: () => Number }, mapPrecision: { required: false, type: () => Number }, heightScale: { required: false, type: () => Number }, layerOffset: { required: false, type: () => Number }, flatUVScale: { required: false, type: () => Number }, cliffUVScale: { required: false, type: () => Number }, flatCliffRatio: { required: false, type: () => Number }, flatColor: { required: false, type: () => Object }, cliffColor: { required: false, type: () => Object }, colormapAssetId: { required: false, type: () => String }, colormapStrength: { required: false, type: () => Number } } }], [import("./asset/dto/paginated-search-asset.dto"), { "PaginatedSearchAssetDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String }, sortKey: { required: true, type: () => String }, sortDirection: { required: true, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: true, type: () => Number }, perPage: { required: true, type: () => Number }, startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number }, type: { required: true, deprecated: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, assetType: { required: true, deprecated: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, assetTypes: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE, isArray: true }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES } }, "PaginatedSearchAssetDtoV2": { populate: { required: true, type: () => [String] }, includeSoftDeleted: { required: false, type: () => Boolean } } }], [import("./asset/dto/update-asset.dto"), { "UpdateAssetDto": { __t: { required: false, type: () => Object, description: "@description If a discriminator/subclassed Asset is being updated, then this must be included. Otherwise, the parent Asset will be used, but because Mongoose is NOT typed under the hood, it won't know about the discriminator classes and thus won't work with properties of the discriminator if the discriminator model isn't used.\n@date 2023-06-07 11:24" } }, "AddAssetPurchaseOptionDto": {} }], [import("./asset/dto/upload-asset-file.dto"), { "UploadAssetFileDto": { userId: { required: true, type: () => String }, assetId: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./user/dto/create-user-access-key.dto"), { "CreateUserAccessKeyDto": { token: { required: true, type: () => String }, adminNote: { required: true, type: () => String } } }], [import("./user/dto/update-user.dto"), { "UpdateUserProfileDto": { email: { required: true, type: () => String }, displayName: { required: true, type: () => String }, publicBio: { required: true, type: () => String }, discordUserId: { required: true, type: () => String }, polygonPublicKey: { required: true, type: () => String }, ethereumPublicKey: { required: true, type: () => String }, twitterUsername: { required: true, type: () => String }, githubUsername: { required: true, type: () => String }, instagramUsername: { required: true, type: () => String }, youtubeChannel: { required: true, type: () => String }, artStationUsername: { required: true, type: () => String }, sketchfabUsername: { required: true, type: () => String }, profileImage: { required: true, type: () => String }, coverImage: { required: true, type: () => String } }, "UpdateUserDeepLinkDto": { deepLinkKey: { required: true, type: () => String }, deepLinkValue: { required: true, type: () => String } }, "UpdateUserTermsDto": { termsAgreedtoClosedAlpha: { required: false, type: () => Boolean }, termsAgreedtoGeneralTOSandPP: { required: false, type: () => Boolean } }, "UpdateUserAvatarDto": { avatarUrl: { required: false, type: () => String } }, "UpdateUserTutorialDto": { shownFirstInSpacePopupV1: { required: false, type: () => Boolean }, shownFirstHomeScreenPopupV1: { required: false, type: () => Boolean }, shownWebAppPopupV1: { required: false, type: () => Boolean } }, "UpdateUserAvatarTypeDto": { avatarType: { required: true, type: () => String }, readyPlayerMeUrlGlb: { required: false, type: () => String } }, "AddRpmAvatarUrlDto": { rpmAvatarUrl: { required: true, type: () => String } }, "RemoveRpmAvatarUrlDto": { rpmAvatarUrl: { required: true, type: () => String } }, "UpsertUserEntityActionDto": { forEntity: { required: true, type: () => String }, actionType: { required: true, type: () => String }, entityType: { required: true, type: () => String }, rating: { required: false, type: () => Number, description: "Optional properties", minimum: 1, maximum: 5 } }, "AddUserCartItemToUserCartDto": { purchaseOptionId: { required: true, type: () => String }, forEntity: { required: true, type: () => String }, entityType: { required: true, type: () => String } } }], [import("./user/dto/upload-profile-file.dto"), { "UploadProfileFileDto": { userId: { required: true, type: () => String }, file: { required: true, type: () => Object } } }], [import("./user/dto/add-user-sidebar-tag.dto"), { "AddUserSidebarTagDto": { sidebarTag: { required: true, type: () => String } } }], [import("./asset/dto/add-tag-to-asset.dto"), { "AddTagToAssetDto": { assetId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./environment/dto/update-environment.dto"), { "UpdateEnvironmentDto": {} }], [import("./space-object/dto/create-space-object.dto"), { "CreateSpaceObjectDto": { spaceId: { required: true, type: () => String, description: "Required properties", minLength: 24, maxLength: 24 }, name: { required: true, type: () => String, maxLength: 300 }, asset: { required: true, type: () => String }, parentSpaceObject: { required: false, type: () => String }, description: { required: false, type: () => String }, locked: { required: false, type: () => Boolean }, preloadBeforeSpaceStarts: { required: false, type: () => Boolean }, position: { required: false, type: () => Object }, rotation: { required: false, type: () => Object }, scale: { required: false, type: () => Object }, offset: { required: false, type: () => Object }, collisionEnabled: { required: false, type: () => Boolean }, shapeType: { required: false, type: () => String }, bodyType: { required: false, type: () => String }, staticEnabled: { required: false, type: () => Boolean }, massKg: { required: false, type: () => Number }, gravityScale: { required: false, type: () => Number }, castShadows: { required: false, type: () => Boolean }, visibleFrom: { required: false, type: () => Number }, visibleTo: { required: false, type: () => Number }, visibleFromMargin: { required: false, type: () => Number }, visibleToMargin: { required: false, type: () => Number }, materialAssetId: { required: false, type: () => String }, objectColor: { required: false, type: () => Object }, objectTexture: { required: false, type: () => String }, objectTextureSize: { required: false, type: () => Number, deprecated: true }, objectTextureSizeV2: { required: false, type: () => Object }, objectTextureOffset: { required: false, type: () => Object }, objectTextureTriplanar: { required: false, type: () => Boolean }, objectTextureRepeat: { required: false, type: () => Boolean }, audioAutoPlay: { required: false, type: () => Boolean }, audioLoop: { required: false, type: () => Boolean }, audioIsSpatial: { required: false, type: () => Boolean }, audioPitch: { required: false, type: () => Number }, audioBaseVolume: { required: false, type: () => Number }, audioSpatialMaxVolume: { required: false, type: () => Number }, audioSpatialRange: { required: false, type: () => Number }, surfaceMaterialId: { required: false, type: () => [Object] }, scriptEvents: { required: false, type: () => [Object] }, extraNodes: { required: false, type: () => [Object] } } }], [import("./util/dto-generic/batch.dto"), { "Batch": { batch: { required: true } } }], [import("./space-object/dto/update-space-object.dto"), { "UpdateSpaceObjectDto": {} }], [import("./space-object/dto/update-batch-space-object.dto"), { "UpdateBatchSpaceObjectDto": {} }], [import("./space-object/dto/add-tag-to-space-object.dto"), { "AddTagToSpaceObjectDto": { spaceObjectId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./space-object/dto/paginated-search-space-object.dto"), { "PaginatedSearchSpaceObjectDto": { field: { required: false, type: () => String }, search: { required: false, type: () => String }, sortKey: { required: false, type: () => String }, sortDirection: { required: false, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: false, type: () => Number }, perPage: { required: false, type: () => Number }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES } } }], [import("./space-object/dto/update-space-object-tags.dto"), { "UpdateSpaceObjectTagsDto": { spaceObjectId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./terrain/dto/create-terrain.dto"), { "CreateTerrainDto": { name: { required: true, type: () => String, maxLength: 300 }, material: { required: false, type: () => String }, description: { required: false, type: () => String, maxLength: 5000 }, public: { required: false, type: () => Boolean }, generator: { required: false, type: () => String }, noiseType: { required: false, type: () => Number }, positionX: { required: false, type: () => Number }, positionY: { required: false, type: () => Number }, positionZ: { required: false, type: () => Number }, heightStart: { required: false, type: () => Number }, heightRange: { required: false, type: () => Number }, seed: { required: false, type: () => Number } } }], [import("./terrain/dto/update-terrain.dto"), { "UpdateTerrainDto": {} }], [import("./space/dto/create-space.dto"), { "CreateSpaceDto": { name: { required: true, type: () => String, maxLength: 300 }, type: { required: true, type: () => String, description: "Optional properties" }, users: { required: false, type: () => Object, description: "START Section: Roles" }, userGroups: { required: false, type: () => Object }, terrain: { required: false, type: () => String, description: "END Section: Roles" }, environment: { required: false, type: () => String }, ownerUserGroup: { required: false, type: () => String }, template: { required: true, type: () => String }, lowerLimitY: { required: false, type: () => Number }, description: { required: false, type: () => String, maxLength: 5000 }, images: { required: false, type: () => [String] }, publicBuildPermissions: { required: false, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, tags: { required: false, type: () => t["./tag/models/tags.schema"].Tags }, maxUsers: { required: false, type: () => Number } } }], [import("./space/dto/update-space.dto"), { "UpdateSpaceDto": { patchCustomData: { required: false, type: () => Object, description: "@description These properties are PATCHED onto custom data (overwrites declared key-value pairs, but doesn't affect other key/value parirs)\n@date 2023-04-05 23:27" }, activeSpaceVersion: { required: false, type: () => String }, removeCustomDataKeys: { required: false, type: () => [String], description: "@description These properties are deleted from custom data. Other properties will not be affected\n@date 2023-04-05 23:27" }, patchSpaceVariablesData: { required: false, type: () => Object }, removeSpaceVariablesDataKeys: { required: false, type: () => [String], description: "@description These properties are deleted from spaceVariablesData. Other properties will not be affected\n@date 2023-06-05 16:59:26" }, tagsV2: { required: false, type: () => [String] }, scriptIds: { required: false, type: () => [String] }, scriptInstances: { required: false, type: () => [Object] }, materialInstances: { required: false, type: () => [Object] }, publicBuildPermissions: { required: true, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, kickRequests: { required: true, type: () => [String] } }, "CreateNewSpaceVersionDto": { updateSpaceWithActiveSpaceVersion: { required: true, type: () => Boolean }, name: { required: true, type: () => String } }, "SpaceCopyFromTemplateDto": { name: { required: true, type: () => String }, description: { required: true, type: () => String }, publicBuildPermissions: { required: true, enum: t["./option-sets/build-permissions"].BUILD_PERMISSIONS }, maxUsers: { required: false, type: () => Number, default: 24 } } }], [import("./space/dto/upload-space-files.dto"), { "UploadSpaceFilesDto": { spaceId: { required: true, type: () => String }, files: { required: true, type: () => [Object] } } }], [import("./space/dto/paginated-search-space.dto"), { "PaginatedSearchSpaceDto": { field: { required: false, type: () => String }, search: { required: false, type: () => String }, sortKey: { required: false, type: () => String }, sortDirection: { required: false, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: false, type: () => Number }, perPage: { required: false, type: () => Number }, tag: { required: false, type: () => [String] }, tagType: { required: false, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } } }], [import("./space-variable/dto/space-variables-data.dto"), { "CreateSpaceVariablesDataDocumentDto": { data: { required: true, type: () => Object, description: "@description We intentionally use a \"data\" property here so that other keys can be added in the future and the dto doesn't assume that everything in the dto should be saved to the database\nWe also want to be able to store top-level key:value pairs that the user doesn't have free reign access to.\n@date 2023-03-03 00:12" } } }], [import("./zone/dto/create-zone.dto"), { "CreateZoneDto": { zoneMode: { required: true, type: () => String, maxLength: 100 }, owner: { required: false, type: () => String, description: "@description The UserId of the user who created the zone. It's optional, but we do want this. It's just set to optional for other CreateZoneDto's validation on the cron job.\n@date 2023-06-17 22:42", minLength: 24, maxLength: 24 }, state: { required: true, type: () => String, maxLength: 100 }, ipAddress: { required: true, type: () => String, maxLength: 100 }, port: { required: true, type: () => Number }, uuid: { required: true, type: () => String, maxLength: 100 }, url: { required: true, type: () => String, maxLength: 100 }, gdServerVersion: { required: true, type: () => String, maxLength: 50 }, containerLastRefreshed: { required: false, type: () => Date }, name: { required: false, type: () => String, maxLength: 300 }, description: { required: false, type: () => String }, space: { required: false, type: () => String, description: "Only required if Build mode, but still run validation in case it exists for some reason (such as manual zone/server creation on the scaler without validation via mirror-server)", minLength: 24, maxLength: 24 }, spaceVersion: { required: false, type: () => String, description: "Only required if Play mode, but still run validation in case it exists for some reason(such as manual zone/server creation on the scaler without validation via mirror-server)", minLength: 24, maxLength: 24 } } }], [import("./zone/dto/update-zone.dto"), { "UpdateZoneDto": { id: { required: false, type: () => String } } }], [import("./zone/dto/create-play-server.dto"), { "CreatePlayServerDto": { zoneName: { required: false, type: () => String } } }], [import("./space/dto/add-tag-to-space.dto"), { "AddTagToSpaceDto": { spaceId: { required: true, type: () => String }, tagName: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, thirdPartySourceHomePageUrl: { required: true, type: () => String } } }], [import("./mirror-db/dto/update-mirror-db-record.dto"), { "UpdateMirrorDBRecordDto": { recordData: { required: true, type: () => Object } } }], [import("./script-entity/dto/create-script-entity.dto"), { "CreateScriptEntityDto": { blocks: { required: true, type: () => [Object] } } }], [import("./script-entity/dto/update-script-entity.dto"), { "UpdateScriptEntityDto": {} }], [import("./roles/dto/set-user-role-for-one.dto"), { "SetUserRoleForOneDto": { targetUserId: { required: true, type: () => String }, role: { required: true, enum: t["./roles/models/role.enum"].ROLE } }, "RemoveUserRoleForOneDto": { targetUserId: { required: true, type: () => String, maxLength: 24 } } }], [import("./space/dto/update-space-tags.dto"), { "UpdateSpaceTagsDto": { spaceId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./space/dto/populate-space.dto"), { "PopulateSpaceDto": { populateCreator: { required: false, type: () => Boolean }, populateUsersPresent: { required: false, type: () => Boolean } } }], [import("./asset/dto/search-asset.dto"), { "SearchAssetDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String } } }], [import("./asset/dto/update-asset-tags.dto"), { "UpdateAssetTagsDto": { assetId: { required: true, type: () => String }, tagType: { required: true, enum: t["./tag/models/tag-types.enum"].TAG_TYPES }, tags: { required: true, type: () => Object } } }], [import("./asset/dto/include-soft-deleted-asset.dto"), { "IncludeSoftDeletedAssetDto": { includeSoftDeleted: { required: false, type: () => Boolean } } }], [import("./asset/dto/upsert-asset.dto"), { "UpsertAssetDto": { name: { required: true, type: () => String }, assetType: { required: true, enum: t["./option-sets/asset-type"].ASSET_TYPE }, description: { required: true, type: () => String }, owner: { required: true, type: () => String }, initPositionX: { required: true, type: () => Number }, initPositionY: { required: true, type: () => Number }, initPositionZ: { required: true, type: () => Number }, initRotationX: { required: true, type: () => Number }, initRotationY: { required: true, type: () => Number }, initRotationZ: { required: true, type: () => Number }, initScaleX: { required: true, type: () => Number }, initScaleY: { required: true, type: () => Number }, initScaleZ: { required: true, type: () => Number }, collisionEnabled: { required: true, type: () => Boolean }, staticEnabled: { required: true, type: () => Boolean }, massKg: { required: true, type: () => Number }, gravityScale: { required: true, type: () => Number }, objectColor: { required: true, type: () => Object } } }], [import("./user/dto/submit-user-access-key.dto"), { "SubmitUserAccessKeyDto": { key: { required: true, type: () => String } } }], [import("./user/dto/update-user-sidebar-tags.dto"), { "UpdateUserSidebarTagsDto": { sidebarTags: { required: true, type: () => [String] } } }], [import("./user-groups/dto/create-group-users-invite.dto"), { "CreateUserGroupInviteDto": { group: { required: true, type: () => String, description: "Required properties" }, unlimited: { required: true, type: () => Boolean }, used: { required: true, type: () => Boolean }, status: { required: true, enum: t["./option-sets/user-group-invite-statuses"].USER_GROUP_INVITE_STATUSES }, completed: { required: true, type: () => Boolean }, creator: { required: true, type: () => String }, recipient: { required: true, type: () => String }, expirationDate: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group-users-membership.dto"), { "CreateUserGroupMembershipDto": { group: { required: true, type: () => String }, status: { required: true, enum: t["./option-sets/user-group-membership-statuses"].USER_GROUP_MEMBERSHIP_STATUSES }, creator: { required: true, type: () => String }, user: { required: true, type: () => String }, role: { required: true, enum: t["./option-sets/group-users-roles"].GROUP_ROLE }, expirationDate: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group-users-request.dto"), { "CreateUserGroupRequestDto": { group: { required: true, type: () => String }, unlimited: { required: true, type: () => Boolean }, used: { required: true, type: () => Boolean }, status: { required: true, enum: t["./option-sets/user-group-invite-statuses"].USER_GROUP_INVITE_STATUSES }, completed: { required: true, type: () => Boolean }, completedDate: { required: true, type: () => Date }, creator: { required: true, type: () => String }, updatedAt: { required: true, type: () => Date }, createdAt: { required: true, type: () => Date } } }], [import("./user-groups/dto/create-group.users.dto"), { "CreateUserGroupDto": { name: { required: true, type: () => String, description: "Required properties" }, public: { required: true, type: () => Boolean }, publicDescription: { required: true, type: () => String, description: "Optional properties" }, primaryContact: { required: true, type: () => String }, moderators: { required: true, type: () => [String] }, owners: { required: true, type: () => [String] }, image: { required: true, type: () => String }, discordUrl: { required: true, type: () => String }, polygonDaoContractPublicKey: { required: true, type: () => String }, ethereumDaoContractPublicKey: { required: true, type: () => String }, twitterUrl: { required: true, type: () => String }, websiteUrl: { required: true, type: () => String }, creator: { required: true, type: () => String } } }], [import("./user-groups/dto/update-group.users.dto"), { "UpdateUserGroupDto": {} }], [import("./space/material-instance/dto/create-material-instance.dto"), { "CreateMaterialInstanceDto": { parameters: { required: true, type: () => Object }, spaceId: { required: true, type: () => String } } }], [import("./space/material-instance/dto/update-material-instance.dto"), { "UpdateMaterialInstanceDto": { parameters: { required: true, type: () => Object } } }], [import("./zone/dto/populate-zone-owner.dto"), { "PopulateZoneOwnerDto": { populateOwner: { required: false, type: () => Boolean } } }], [import("./godot-server-override-config/dto/create-godot-server-override-config.dto"), { "CreateGodotServerOverrideConfigDto": { spaceId: { required: true, type: () => String, minLength: 24, maxLength: 24 } } }], [import("./block/dto/create-block.dto"), { "CreateBlockDto": { name: { required: true, type: () => String, description: "Required properties" }, blockType: { required: true, type: () => String }, description: { required: false, type: () => String, description: "Optional Properties", maxLength: 1000 }, mirrorPublicLibrary: { required: false, type: () => Boolean } } }], [import("./block/dto/update-block.dto"), { "UpdateBlockDto": {} }], [import("./favorite/dto/create-favorite.dto"), { "CreateFavoriteDto": { asset: { required: true, type: () => String }, user: { required: true, type: () => String }, creator: { required: true, type: () => String } } }], [import("./favorite/dto/update-favorite.dto"), { "UpdateFavoriteDto": {} }], [import("./stripe/dto/token.dto"), { "AddBank": { token: { required: true, type: () => String } }, "AddCard": { token: { required: true, type: () => String } }, "CardToken": { number: { required: true, type: () => String }, exp_month: { required: true, type: () => String }, exp_year: { required: true, type: () => String }, cvc: { required: true, type: () => String } } }], [import("./stripe/dto/paymentIntent.dto"), { "PaymentIntentDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, payment_method: { required: true, type: () => String } } }], [import("./stripe/dto/transfers.dto"), { "TransfersDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, destination: { required: true, type: () => String } } }], [import("./stripe/dto/subscription.dto"), { "SubscriptionDto": { amount: { required: true, type: () => Number }, currency: { required: true, type: () => String }, productId: { required: true, type: () => String }, priceId: { required: true, type: () => String }, destination: { required: true, type: () => String } }, "ProductDto": { name: { required: true, type: () => String }, description: { required: true, type: () => String } } }], [import("./stripe/dto/metadata.dto"), { "StripeSubscriptionMetadataDto": { userId: { required: true, type: () => String } } }], [import("./tag/dto/create-tag.dto"), { "CreateTagDto": { name: { required: true, type: () => String }, tagType: { required: true, description: "Optional", enum: t["./option-sets/tag-type"].TAG_TYPE }, mirrorPublicLibrary: { required: true, type: () => Boolean }, public: { required: true, type: () => Boolean }, parentTag: { required: true, type: () => String } }, "CreateThirdPartySourceTagDto": { thirdPartySourceHomePageUrl: { required: true, type: () => String }, thirdPartySourcePublicDescription: { required: true, type: () => String, description: "Optional" }, thirdPartySourceTwitterUrl: { required: true, type: () => String }, thirdPartySourceTMUserId: { required: true, type: () => String } } }], [import("./tag/dto/update-tag.dto"), { "UpdateTagDto": {} }], [import("./user-feedback/dto/create-user-feedback.dto"), { "CreateUserFeedbackItemDto": { name: { required: true, type: () => String }, description: { required: false, type: () => String, description: "Optional" }, userFeedbackType: { required: false, type: () => String }, USER_FEEDBACK_ITEM_STATUS: { required: false, type: () => String }, public: { required: false, type: () => Boolean } }, "CreateVoteOnUserFeedbackItemDto": { userFeedbackItemId: { required: true, type: () => String }, vote: { required: true, type: () => String } }, "CreateUserFeedbackItemFeatureRequestDto": {}, "CreateUserFeedbackItemBugDto": {} }], [import("./user-feedback/dto/update-user-feedback.dto"), { "UpdateUserFeedbackDto": {} }], [import("./user-feedback/dto/create-user-feedback-comment.dto"), { "CreateUserFeedbackCommentDto": { text: { required: true, type: () => String }, userFeedbackItemId: { required: true, type: () => String } } }], [import("./asset/dto/asset.dto"), { "GetAssetDto": { startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } }, "AssetParamsDto": { startItem: { required: true, type: () => Number }, numberOfItems: { required: true, type: () => Number } } }], [import("./space/dto/search-space.dto"), { "SearchSpaceDto": { searchField: { required: true, type: () => String }, searchString: { required: true, type: () => String } } }], [import("./auth/dto/DevLoginUserEmailPassword.dto"), { "DevLoginUserEmailPassword": { email: { required: true, type: () => String }, password: { required: true, type: () => String } } }], [import("./user-groups/dto/search-group.dto"), { "SearchUserGroupDto": { searchField: { required: true, type: () => String }, searchstring: { required: true, type: () => String } } }], [import("./zone/dto/paginated-search-zone.dto"), { "PaginatedSearchZoneDto": { field: { required: true, type: () => String }, search: { required: true, type: () => String }, sortKey: { required: true, type: () => String }, sortDirection: { required: true, enum: t["./util/pagination/pagination.interface"].SORT_DIRECTION }, page: { required: true, type: () => Number }, perPage: { required: true, type: () => Number } } }], [import("./zone/dto/update-zone-status.dto"), { "UpdateZoneStatusDto": { id: { required: false, type: () => String }, uuid: { required: false, type: () => String }, state: { required: false, type: () => String }, url: { required: false, type: () => String }, version: { required: false, type: () => String }, address: { required: false, type: () => String }, port: { required: false, type: () => Number } } }]], "controllers": [[import("./app.controller"), { "AppController": { "getHello": { type: String }, "getHealth": { type: String }, "throwIntentionalError": {} } }], [import("./space/space.controller"), { "SpaceController": { "getAllPublicForUser": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: [Object] }, "getAllPublicForUserV2": { description: "Same as the above but no populate\n@description Retrieves a collection user's public spaces as public data\nid prefix added to prevent wildcard route clashes with file method order", type: [Object] }, "search": { description: "@description Retrieves a collection of space's as public data", type: [t["./space/space.schema"].SpacePublicData] }, "searchV2": { description: "@description Same as the above but no populate\n@date 2023-07-12 23:59", type: [t["./space/space.schema"].SpacePublicData] }, "findAllForMeWhereOwner": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: [Object] }, "findAllForMeWhereOwnerPaginatedV2": {}, "findAllForMeWhereOwnerPaginatedV3": { description: "@description This is the same as me-v2, but no populate\n@date 2023-07-12 23:53" }, "getPopularSpaces": { type: [Object] }, "getFavoriteSpaces": { type: [Object] }, "getRecentSpaces": { type: [Object] }, "getSpacesByTags": {}, "addTagToSpaceWithRoleChecks": { type: Object }, "updateSpaceTagsByTypeWithRoleChecks": { type: Object }, "deleteTagFromSpaceWithRoleChecks": {}, "findDiscoverSpacesForUser": { type: [Object] }, "findDiscoverSpacesForUserPaginatedV2": { type: Object }, "findDiscoverSpacesForUserPaginatedV3": { description: "@description This is the same as discover-v2, but no populate\n@date 2023-07-12 23:53", type: Object }, "findSpaceTemplates": { description: "@description This is the same as discover-v2, but no populate\n@date 2023-07-12 23:53", type: [Object] }, "getPublishedSpaces": { description: "@description Returns all Spaces with an activeSpaceVersion\n@date 2023-06-10 22:23" }, "getPublishedSpacesV2": { description: "@description same as get-published-spaces, but no populate\n@date 2023-07-12 23:54" }, "getLatestPublishedSpaceBySpaceId": { type: Object }, "refreshStats": { type: Object }, "findOne": { type: Object }, "findAllForUserV2": { type: [Object] }, "findAllForUser": { type: [Object] }, "create": { type: Object }, "update": { type: Object }, "remove": { type: Object }, "clearVoxels": {}, "copy": { type: Object }, "copyFromTemplate": { type: Object }, "remixSpace": { type: Object }, "publish": { type: Object }, "getPublishedSpacesBySpaceId": { type: [Object] }, "uploadPublic": { type: Object }, "restoreSpaceFromSpaceVersion": {}, "setUserRoleForOne": { description: "START Section: Owner permissions for role modification", type: Object }, "removeUserRoleForOne": { type: Object }, "kickMe": { description: "END Section: Owner permissions for role modification", type: Object } } }], [import("./terrain/terrain.controller"), { "TerrainController": { "findAllForUser": { type: [Object] }, "findAllPublic": { type: [Object] }, "findOne": { type: Object }, "create": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./util/file-upload/file-upload.controller"), { "FileUploadController": { "batchAssetUploadFromQueueBucket": {} } }], [import("./asset/asset.controller"), { "AssetController": { "search": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: [Object] }, "searchV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:49", type: [Object] }, "getMirrorPublicLibraryAssets": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: [Object] }, "getMirrorPublicLibraryAssetsV2": { description: "@description Same as getMirrorPublicLibraryAssets, but doesn't populate the fields\n@date 2023-07-12 23:33", type: [Object] }, "create": { type: Object }, "getUserRecentInstancedAssets": { type: [Object] }, "createWithUpload": { type: Object }, "getAssetsForMe": { description: "Get player's created assets\nTODO this needs to be paginated", type: [Object] }, "getAssetsForMeV2": { description: "Same as above but no populate\nTODO this needs to be paginated", type: [Object] }, "getAllAccessibleAssetsOfUser": { description: "Get all player's accessible assets. Accessible assets are assets that are public, or assets that are private but owned by the user.", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getAllAccessibleAssetsOfUserV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:45", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getRecentAssetsForUser": { type: [Object] }, "getRecentAssetsForUserV2": { type: [Object] }, "getPaginatedMyAssets": { type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMyAssetsV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:43", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMirrorAssets": { type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "getPaginatedMirrorAssetsV2": { description: "@description Same as above but no populate\n@date 2023-07-12 23:41", type: t["./asset/asset.controller"].AssetFullDataPaginatedResponse }, "findAllForUser": { type: [Object] }, "findOneAssetUsage": { type: t["./asset/asset.models"].AssetUsageApiResponse }, "getAssetsByTag": {}, "addTagToAssetsWithRoleChecks": { type: Object }, "updateAssetTagsByTypeWithRoleChecks": { type: Object }, "deleteTagFromAssetWithRoleChecks": {}, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object }, "undoAssetSoftDelete": { description: "@description This endpoint is used to undo soft delete of an asset.\n(Remove isSoftDeleted and softDeletedAt fields from the asset document)\n\n@date 2023-11-23", type: String }, "upload": { type: Object }, "uploadPublic": { type: Object }, "uploadThumbnail": { type: Object }, "getAsset": {}, "addAssetPurchaseOption": { type: Object }, "deleteAssetPurchaseOption": { type: Object } } }], [import("./user/user.controller"), { "UserController": { "findOne": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************", type: t["./user/user.schema"].UserPublicData }, "findOneWithPublicProfile": { description: "@description Retrieves a Users public profile data including populated\n fields like public assets and public groups", type: t["./user/user.schema"].User }, "search": { description: "@description Retrieves a collection of Users public data", type: [t["./user/user.schema"].UserPublicData] }, "getCurrentUser": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: Object }, "getUserRecents": { type: Object }, "uploadPublic": { description: "TODO - Would be nice to have 1 file upload endpoint that can handle all entity types and paths\n move logic to service" }, "updateProfile": { type: Object }, "updateUserTutorial": { type: Object }, "updateDeepLink": { type: Object }, "updateAvatar": { type: Object }, "updateTermsAgreedTo": { type: Object }, "updateAvatarType": { type: Object }, "getUserEntityAction": { type: Object }, "getUserEntityActionsByMeForEntity": { type: [Object] }, "upsertUserEntityAction": { type: Object }, "deleteUserEntityAction": { type: Object }, "getMyFriends": { description: "START Section: Friends and friend requests ------------------------------------------------------", type: [t["./user/user.service"].Friend] }, "getMyFriendRequests": { type: [t["./user/user.service"].Friend] }, "acceptFriendRequest": { type: [t["./user/user.service"].Friend] }, "rejectFriendRequest": { type: [t["./user/user.service"].Friend] }, "getSentFriendRequests": { type: [t["./user/user.service"].Friend] }, "sendFriendRequest": { type: t["./user/user.schema"].User }, "removeFriend": { type: [t["./user/user.service"].Friend] }, "getUserCart": { description: "END Section: Friends and friend requests ------------------------------------------------------", type: Object }, "addUserCartItemToUserCart": { type: Object }, "removeAllUserItemsFromCart": { type: Object }, "removeUserCartItemFromUserCart": { type: Object }, "addRpmAvatarUrl": { description: "END Section: Cart ------------------------------------------------------", type: Object }, "createSignUpKey": { type: Object }, "submitUserAccessKey": {}, "removeRpmAvatarUrl": { description: "@description Removes an RPM url from readyPlayerMeAvatarUrls in Mongo", type: Object }, "getUserSidebarTags": { type: [String] }, "addUserSidebarTag": { type: String }, "updateUserSidebarTags": { type: [String] }, "deleteUserSidebarTag": {} } }], [import("./environment/environment.controller"), { "EnvironmentController": { "create": { description: "*********************\n AUTH REQUIRED ENDPOINTS\n********************", type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./user-groups/user-group.controller"), { "UserGroupController": { "create": { type: Object }, "getAllGroupsForMe": { description: "@description Find all groups for current user", type: Object }, "getAllGroupInvitesForMe": { description: "@description Find all invites for the current user", type: Object }, "findPublicGroupMembershipForOtherUser": { description: "@description This is used for another user, NOT the current user,\nso we only get the PUBLIC groups that the person is a part of\nTODO - add ApiOkResponse type for UserGroupMembership", type: [Object] }, "getGroupMembershipForMe": { description: "@description Find all group members of current user\nTODO - add ApiOkResponse type for UserGroupMembership", type: Object }, "search": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./space/space-godot-server.controller"), { "SpaceGodotServerController": { "getLatestPublishedSpaceBySpaceId": { description: "***************************\n TEMP: AUTHED ENDPOINT UP HERE FOR 2023-03-17 13:53:32 RELEASE ISSUE\nWe should likely refactor our pattern for classes because order of routes DOES matter in NestJS, so being @Public() up top can cause issues\n**************************", type: Object }, "getActiveSpaceVersionForSpaceBySpaceId": { type: Object }, "updateTerrain": { description: "***************************\n END TEMP: AUTHED ENDPOINT UP HERE FOR 2023-03-17 13:53:32 RELEASE ISSUE\n**************************" } } }], [import("./mirror-server-config/mirror-server-config.controller"), { "MirrorServerConfigController": { "getConfig": { type: Object }, "setGdServerVersion": { type: Object } } }], [import("./space/material-instance/material-instance.controller"), { "MaterialInstanceController": { "create": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "findOne": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "update": { type: t["./space/material-instance/material-instance.schema"].MaterialInstance }, "delete": { type: String } } }], [import("./zone/zone.controller"), { "ZoneController": { "joinBuildServer": { description: "@description Requests a zone server with a specific space id and launches the server if needed for BUILD MODE", type: Object }, "getPlayServersForSpaceVersionId": { description: "@description gets Play servers for a spaceVersionId", type: [Object] }, "getPlayServersForSpaceId": { description: "@description gets Play servers for a spaceId", type: [Object] }, "joinPlayServerByZoneId": { description: "@description joins a play server by spaceVersionId and returns the active session", type: Object }, "joinPlayServerBySpaceId": { description: "@description joins a play server by spaceId, which looks up the activeSpaceVersion and finds the spaceVersion, then returns the zone. If a zone doesn't exist, it will create it", type: Object }, "createPlayServerWithSpaceVersion": { description: "@description joins a play server by spaceVersionId and returns the active session", type: Object }, "createPlayServerWithSpace": { type: Object }, "findAllZonesBySpaceId": { description: "@description Get all the Zone entities associated with a space id.", type: [Object] }, "findAllZonesByUserId": { description: "@description Get all the Zone entities associated with a user id.", type: [Object] }, "findOneZone": { description: "@description Retrieves a zone entity.", type: Object }, "updateOneZone": { description: "@description Update the user controlled values of a zone entity (Name, Description, Space)", type: Object }, "stopAllActiveZones": { description: "@description Stops all zone servers and updates their corresponding entities.\nQueries for every active Zone Server, requests that they shut down, and updates corresponding Zone entities." } } }], [import("./godot-server-override-config/godot-server-override-config.controller"), { "GodotServerOverrideConfigController": { "findOne": { type: String }, "findOneAlias": { type: String }, "create": { type: Object } } }], [import("./mirror-db/mirror-db.controller"), { "MirrorDBController": { "getRecordFromMirrorDBById": { type: Object }, "getRecordFromMirrorDBBySpaceId": { type: Object }, "getRecordFromMirrorDBBySpaceVersionId": { type: Object }, "updateRecordInMirrorDBById": { type: Object }, "deleteRecordFromMirrorDBById": { type: String } } }], [import("./script-entity/script-entity.controller"), { "ScriptEntityController": { "getRecentScripts": { type: [Object] }, "create": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "delete": { type: Object } } }], [import("./space-object/space-object.controller"), { "SpaceObjectController": { "create": { type: Object }, "createAlias": { type: Object }, "copy": { type: Object }, "findAllBySpaceId": { type: [Object] }, "getSpaceObjectsByTag": {}, "updateSpaceObjectTagsByTypeWithRoleChecks": { type: Object }, "addTagToSpaceObjectWithRoleChecks": { type: Object }, "deleteTagFromSpaceObjectWithRoleChecks": {}, "searchSpaceObjectsPaginated": {}, "findOne": { type: Object }, "update": { type: Object }, "remove": {} } }], [import("./auth/auth.controller"), { "AuthController": { "createUserWithEmailPasswordAndType": { description: "***************************\n PUBLICLY ACCESSIBLE ENDPOINTS\n**************************" }, "authedUserCreate": { type: t["./auth/auth.controller"].CreateAuthUserResponse }, "convertAnonymousAccountToFull": {}, "deleteAccount": {} } }], [import("./auth/auth-test.controller"), { "AuthTestController": { "deleteTestAccount": { description: "@description Only used in when NODE_ENV === NODE_ENV.TEST for e2e. Deletes the test account. Checks if the email is an e2e email that ends in @themirror.space. This is a POST so that a body can easily be included (no body in DELETE requests)\n@date 2023-03-16 01:01" } } }], [import("./block/block.controller"), { "BlockController": { "create": { type: Object }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./favorite/favorite.controller"), { "FavoriteController": { "create": { type: Object }, "findAllForUser": { type: [Object] }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./storage/storage.controller"), { "StorageController": { "getClientVersion": { description: "Public endpoint for getting the current version of the client application.", type: Object }, "getClientUrl": { description: "Public endpoint for getting the client application download URL for the target platform parameter.", type: String }, "getClientUrls": { description: "Public endpoint for getting all supported platforms client application download URLs.", type: Object }, "downloadFile": {} } }], [import("./stripe/stripe.controller"), { "StripeController": { "setupIntent": { description: "2023-07-24 11:12:23 Note: I'm commenting these out because it was old code and I don't think we need to expose all these. The implementation also wasn't secure with allowing an userId to be specified." }, "createCustomerAccount": { type: Object }, "createConnectAccount": { type: Object }, "deleteConnectAccount": { type: Object }, "createCard": { type: [Object] }, "getCardsList": { type: Object }, "getStripeAccountInfo": { type: Object }, "addBankAccount": { type: Object }, "deleteCard": { type: [Object] }, "setDefaultPaymentMethod": { type: Object }, "createPaymentIntent": {}, "getPaymentMethods": { type: Object }, "transfersAmount": { type: Object }, "createProduct": { type: Object }, "getAllProductsWithPrice": { type: Object }, "createSubscription": {}, "pauseSubscription": { type: Object }, "resumeSubscription": { type: Object }, "deleteSubscription": { type: Object }, "createDashboardLink": { type: Object }, "createCustomerPortalLink": { type: Object }, "handleStripeWebhook": { type: Object } } }], [import("./tag/tag.controller"), { "TagController": { "create": { type: Object }, "findAllMirrorPublicLibraryTags": { type: [Object] }, "findAllThemeTags": { type: [Object] }, "getTagTypes": { type: [String] }, "findOne": { type: Object }, "update": { type: Object }, "remove": { type: Object } } }], [import("./user-feedback/user-feedback.controller"), { "UserFeedbackController": { "findNewestPublicUserFeedbackItems": { description: "PUBLICLY ACCESSIBLE ENDPOINTS", type: [Object] }, "findTopPublicUserFeedbackItems": { type: [Object] }, "getUserFeedbackItemTypes": { type: [String] }, "findOne": { type: Object }, "findComments": { type: [Object] }, "findAllPublicUserFeedbackItems": { type: [Object] }, "create": { type: Object }, "voteOnUserFeedbackItem": { type: Object }, "createComment": { type: Object }, "update": { type: Object }, "removeUserFeedbackItem": { type: Object }, "removeComment": { type: Object } } }]] } }; }; \ No newline at end of file diff --git a/mirror-web-server/src/space/space-godot-server.controller.ts b/mirror-web-server/src/space/space-godot-server.controller.ts index 0f34120b..fc284bb9 100644 --- a/mirror-web-server/src/space/space-godot-server.controller.ts +++ b/mirror-web-server/src/space/space-godot-server.controller.ts @@ -82,23 +82,28 @@ export class SpaceGodotServerController { try { const remoteRelativePath = `space/${id}/terrain/voxels.dat` - if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { - await this.fileUploadService.uploadFileLocal(file, remoteRelativePath) + if (process.env.ASSET_STORAGE_DRIVER === 'GCP') { + await this.fileUploadService.streamFile( + process.env.GCS_BUCKET_PUBLIC, + remoteRelativePath, + file, + 'publicRead' + ) return { success: true, - publicUrl: `${process.env.ASSET_STORAGE_URL}/${remoteRelativePath}` + publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${remoteRelativePath}` } } - await this.fileUploadService.streamFile( - process.env.GCS_BUCKET_PUBLIC, - remoteRelativePath, - file, - 'publicRead' - ) - return { - success: true, - publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${remoteRelativePath}` + if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' + ) { + await this.fileUploadService.uploadFileLocal(file, remoteRelativePath) + return { + success: true, + publicUrl: `${process.env.ASSET_STORAGE_URL}/${remoteRelativePath}` + } } } catch (e) { this.logger.error(e?.message, e, SpaceGodotServerController.name) diff --git a/mirror-web-server/src/space/space.service.ts b/mirror-web-server/src/space/space.service.ts index 67879596..ed7dbaba 100644 --- a/mirror-web-server/src/space/space.service.ts +++ b/mirror-web-server/src/space/space.service.ts @@ -891,7 +891,7 @@ export class SpaceService implements IRoleConsumer { } else { throw new NotFoundException() } - }) + }) as any as Promise } else { this.logger.log( `canRemoveWithRolesCheck failed for user: ${userId}`, @@ -1030,7 +1030,24 @@ export class SpaceService implements IRoleConsumer { try { const remoteRelativePath = `space/${spaceId}/terrain/voxels.dat` - if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { + if (process.env.ASSET_STORAGE_DRIVER === 'GCP') { + await this.fileUploadService.streamData( + process.env.GCS_BUCKET_PUBLIC, + remoteRelativePath, + 'application/octet-stream', + Buffer.from(new Uint8Array()), + 'publicRead' + ) + return { + success: true, + publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${remoteRelativePath}` + } + } + + if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' + ) { // Create a fake file object to pass to the local file upload method const file: Express.Multer.File = { fieldname: '', @@ -1039,29 +1056,19 @@ export class SpaceService implements IRoleConsumer { mimetype: 'application/octet-stream', size: 0, destination: '', - filename: '', + filename: 'voxels.dat', path: '', buffer: Buffer.from(new Uint8Array()), stream: null // Add the 'stream' property } + await this.fileUploadService.uploadFileLocal(file, remoteRelativePath) + return { success: true, publicUrl: `${process.env.ASSET_STORAGE_URL}/${remoteRelativePath}` } } - - await this.fileUploadService.streamData( - process.env.GCS_BUCKET_PUBLIC, - remoteRelativePath, - 'application/octet-stream', - Buffer.from(new Uint8Array()), - 'publicRead' - ) - return { - success: true, - publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${remoteRelativePath}` - } } catch (e) { this.logger.error(e?.message, e, SpaceService.name) throw new InternalServerErrorException('Error uploading terrain data') diff --git a/mirror-web-server/src/terrain/terrain.service.ts b/mirror-web-server/src/terrain/terrain.service.ts index 98cb5047..a9957bb3 100644 --- a/mirror-web-server/src/terrain/terrain.service.ts +++ b/mirror-web-server/src/terrain/terrain.service.ts @@ -87,14 +87,20 @@ export class TerrainService { const fromPath = `space/${fromId}/terrain/voxels.dat` const toPath = `space/${toId}/terrain/voxels.dat` try { - if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { + if (process.env.ASSET_STORAGE_DRIVER === 'GCP') { + await this.fileUploadService.copyFileInBucket( + process.env.GCS_BUCKET_PUBLIC, + fromPath, + toPath + ) + } + + if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' + ) { return await this.fileUploadService.copyFileLocal(fromPath, toPath) } - await this.fileUploadService.copyFileInBucket( - process.env.GCS_BUCKET_PUBLIC, - fromPath, - toPath - ) } catch (error: any) { /** When no voxel exists, one will be created by godot */ const message: string = error?.message || 'No error message provided' diff --git a/mirror-web-server/src/util/file-upload/file-upload.service.ts b/mirror-web-server/src/util/file-upload/file-upload.service.ts index 3d79266b..57537ec6 100644 --- a/mirror-web-server/src/util/file-upload/file-upload.service.ts +++ b/mirror-web-server/src/util/file-upload/file-upload.service.ts @@ -59,24 +59,29 @@ export class FileUploadService implements FileUploadInterface { file.mimetype )}` + if (process.env.ASSET_STORAGE_DRIVER === 'GCP') { + await this.streamFile( + process.env.GCS_BUCKET_PUBLIC, + pathWithFileType, + file, + 'publicRead' + ) + return { + publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${pathWithFileType}` + } + } + // If we're using local asset storage, we'll just save the file to the local storage - if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { + if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' + ) { console.log('Uploading file to local storage') await this.uploadFileLocal(file, pathWithFileType) return { publicUrl: process.env.ASSET_STORAGE_URL + pathWithFileType } } - - await this.streamFile( - process.env.GCS_BUCKET_PUBLIC, - pathWithFileType, - file, - 'publicRead' - ) - return { - publicUrl: `${process.env.GCP_BASE_PUBLIC_URL}/${pathWithFileType}` - } } catch (error: any) { const message: string = error?.message throw new HttpException(`File upload error: ${message}`, 400) @@ -95,20 +100,25 @@ export class FileUploadService implements FileUploadInterface { file.mimetype )}` + if (process.env.ASSET_STORAGE_DRIVER === 'GCP') { + await this.streamFile( + process.env.GCS_BUCKET, + pathWithFileType, + file, + 'private' + ) + + return { relativePath: pathWithFileType } + } + // If we're using local asset storage, we'll just save the file to the local storage - if (process.env.ASSET_STORAGE_DRIVER === 'LOCAL') { + if ( + !process.env.ASSET_STORAGE_DRIVER || + process.env.ASSET_STORAGE_DRIVER === 'LOCAL' + ) { await this.uploadFileLocal(file, pathWithFileType) return { relativePath: pathWithFileType } } - - await this.streamFile( - process.env.GCS_BUCKET, - pathWithFileType, - file, - 'private' - ) - - return { relativePath: pathWithFileType } } catch (error: any) { const message: string = error?.message throw new HttpException(`File upload error: ${message}`, 400)