GraphictoriaWeb/web/app/Http/Controllers/Api/AdminController.php

354 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use App\Helpers\AssetHelper;
use App\Helpers\CdnHelper;
use App\Helpers\GridHelper;
use App\Helpers\ValidationHelper;
use App\Http\Controllers\Controller;
use App\Jobs\AppDeployment;
use App\Models\AssetType;
use App\Models\Deployment;
use App\Models\RobloxAsset;
use App\Rules\AppDeploymentFilenameRule;
class AdminController extends Controller
{
// Moderator+
// Admin+
function manualAssetUpload(Request $request)
{
$validator = Validator::make($request->all(), [
'asset-type-id' => ['required', 'int'],
'name' => ['required', 'string'],
'description' => ['string', 'nullable'],
'roblox-id' => ['int', 'min:0', 'nullable'],
'on-sale' => ['required', 'boolean'],
'price' => ['required_if:on-sale,true', 'int', 'min:0'],
'content' => ['nullable'],
'mesh-id' => ['int', 'nullable'],
'base-id' => ['int', 'nullable'],
'overlay-id' => ['int', 'nullable'],
],[
'asset-type-id.required' => 'An asset type ID must be provided.',
'roblox-id.integer' => 'Roblox ID must be an integer.'
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
$valid = $validator->valid();
$isRobloxAsset = ($request->has('roblox-id') && $valid['roblox-id'] > 0);
if($isRobloxAsset)
{
$uploadedAsset = RobloxAsset::where('robloxAssetId', $valid['roblox-id'])->first();
if($uploadedAsset)
{
$validator->errors()->add('roblox-id', 'This asset has already been uploaded!');
return ValidationHelper::generateValidatorError($validator);
}
}
$assetType = AssetType::where('id', $valid['asset-type-id'])
->where('adminCreatable', 1)
->first();
if(!$assetType)
{
$validator->errors()->add('asset-type-id', 'Invalid asset type for admin upload.');
return ValidationHelper::generateValidatorError($validator);
}
$assetFunction = 'Unknown';
$assetFunctionArgs = [];
switch($assetType->id)
{
case 27: // Torso
case 28: // Right Arm
case 29: // Left Arm
case 30: // Left Leg
case 31: // Right Leg
$assetFunctionArgs = [$assetType->id, $valid];
$assetFunction = 'BodyPart';
break;
case 18: // Face
$assetFunctionArgs = [$validator, $valid];
$assetFunction = 'Face';
break;
default:
$assetFunctionArgs = [$validator];
$assetFunction = 'Generic';
break;
}
$assetContent = $this->{ 'manualAssetUpload' . $assetFunction }($request, ...$assetFunctionArgs);
$hash = CdnHelper::SaveContent($assetContent, 'application/octet-stream');
$asset = AssetHelper::newAsset([
'creatorId' => 1,
'name' => $valid['name'],
'description' => $valid['description'],
'approved' => true,
'priceInTokens' => $valid['price'],
'onSale' => $valid['on-sale'] == 1 ? true : false,
'assetTypeId' => $assetType->id,
'assetVersionId' => 0
], $hash);
$asset->logAdminUpload(Auth::user()->id);
if($isRobloxAsset)
{
RobloxAsset::create([
'robloxAssetId' => $valid['roblox-id'],
'localAssetId' => $asset->id
]);
}
return response([
'success' => true,
'message' => 'Your asset has been successfully uploaded!',
'assetId' => $asset->id
]);
}
function manualAssetUploadBodyPart(Request $request, $assetTypeId, $valid)
{
$bodyParts = [
27 => 1, // Torso
28 => 3, // Right Arm
29 => 2, // Left Arm
30 => 4, // Left Leg
31 => 5 // Right Leg
];
$document = simplexml_load_string(GridHelper::getBodyPartXML());
$document->xpath('//int[@name="BaseTextureId"]')[0][0] = $valid['base-id'] ?: 0;
$document->xpath('//token[@name="BodyPart"]')[0][0] = $bodyParts[$assetTypeId];
$document->xpath('//int[@name="MeshId"]')[0][0] = $valid['mesh-id'];
$document->xpath('//string[@name="Name"]')[0][0] = $valid['name'];
$document->xpath('//int[@name="OverlayTextureId"]')[0][0] = $valid['overlay-id'] ?: 0;
$domXML = dom_import_simplexml($document);
$assetContent = $domXML->ownerDocument->saveXML($domXML->ownerDocument->documentElement);
return $assetContent;
}
function manualAssetUploadFace(Request $request, $validator, $valid)
{
if(!$request->has('content'))
{
$validator->errors()->add('content', 'Asset content cannot be blank!');
return ValidationHelper::generateValidatorError($validator);
}
$hash = CdnHelper::SaveContent(
file_get_contents($request->file('content')->path()),
'application/octet-stream'
);
$imageAsset = AssetHelper::newAsset([
'creatorId' => 1,
'name' => $valid['name'],
'approved' => true,
'onSale' => false,
'assetTypeId' => 1, // Image
'assetVersionId' => 0
], $hash);
$imageAsset->logAdminUpload(Auth::user()->id);
$imageAssetUrl = route('client.asset', ['id' => $imageAsset->id]);
$document = simplexml_load_string(GridHelper::getFaceXML());
$document->xpath('//Content[@name="Texture"]')[0][0]->addChild('url', $imageAssetUrl);
$domXML = dom_import_simplexml($document);
$assetContent = $domXML->ownerDocument->saveXML($domXML->ownerDocument->documentElement);
return $assetContent;
}
function manualAssetUploadGeneric(Request $request, $validator)
{
if(!$request->has('content'))
{
$validator->errors()->add('content', 'Asset content cannot be blank!');
return ValidationHelper::generateValidatorError($validator);
}
$assetContent = file_get_contents($request->file('content')->path());
return $assetContent;
}
function manualAssetUploadUnknown(Request $request)
{
throw new \BadMethodCallException('Not implemented');
}
// Owner+
function deploy(Request $request)
{
$validator = Validator::make($request->all(), [
'version' => ['regex:/version\\-[a-fA-F0-9]{16}/'],
'type' => ['required_without:version', 'regex:/(Deploy|Revert)/i'],
'app' => ['required_without:version', 'regex:/(Client|Studio)/i']
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
$valid = $validator->valid();
$response = [
'status' => 'Loading',
'version' => null,
'message' => 'Please wait...',
'progress' => 0
];
if(!$request->has('version'))
{
$deployment = Deployment::newVersionHash($valid);
$response['version'] = $deployment->version;
$response['message'] = 'Created deployment.';
$response['progress'] = 0;
return response($response);
}
$deployment = Deployment::where('version', $valid['version'])->first();
if($deployment === null || !$deployment->isValid()) {
$validator->errors()->add('version', 'Unknown version deployment hash.');
return ValidationHelper::generateValidatorError($validator);
}
$response['version'] = $deployment->version;
if($deployment->error != null)
{
$response['status'] = 'Error';
$response['message'] = sprintf('Failed to deploy %s. Error: %s', $deployment->version, $deployment->error);
$response['progress'] = 1;
return response($response);
}
$steps = 5;
$response['progress'] = $deployment->step/$steps;
switch($deployment->step)
{
case 0:
$response['message'] = 'Files uploading.';
break;
case 1:
$response['message'] = 'Batching deployment.';
break;
case 2:
$response['message'] = 'Unpacking files.';
break;
case 3:
$response['message'] = 'Updating version security.';
break;
case 4:
$response['message'] = 'Pushing deployment to setup.';
break;
case 5:
$response['status'] = 'Success';
$response['message'] = sprintf('Deploy completed. Successfully deployed %s %s', $deployment->app, $deployment->version);
break;
}
return response($response);
}
function deployVersion(Request $request, string $version)
{
$validator = Validator::make($request->all(), [
'file.*' => ['required']
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
$valid = $validator->valid();
$deployment = Deployment::where('version', $version)->first();
if($deployment === null || !$deployment->isValid() || $deployment->step != 0) {
$validator->errors()->add('version', 'Unknown version deployment hash.');
return ValidationHelper::generateValidatorError($validator);
}
$deploymentRule = new AppDeploymentFilenameRule($deployment->app);
if(!$deploymentRule->passes('file', $request->file('file')))
{
$deployment->error = 'Missing files.';
$deployment->save();
$validator->errors()->add('file', $deployment->error);
return ValidationHelper::generateValidatorError($validator);
}
foreach($request->file('file') as $file)
{
$file->storeAs(
'setuptmp',
sprintf('%s-%s', $version, $file->getClientOriginalName())
);
}
$deployment->step = 1; // Batching deployment.
$deployment->save();
AppDeployment::dispatch($deployment);
}
// RCC Only
function uploadRobloxAsset(Request $request)
{
$validator = Validator::make($request->all(), [
'contentId' => ['required', 'int']
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
if(!GridHelper::hasAllAccess())
{
$validator->errors()->add('contentId', 'This API can only be called by the web service.');
return ValidationHelper::generateValidatorError($validator);
}
$valid = $validator->valid();
$asset = AssetHelper::uploadRobloxAsset($valid['contentId'], true);
return route('client.asset', ['id' => $asset->id]);
}
function uploadAsset(Request $request)
{
$validator = Validator::make($request->all(), [
'contentId' => ['required', 'int']
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
if(!GridHelper::hasAllAccess())
{
$validator->errors()->add('contentId', 'This API can only be called by the web service.');
return ValidationHelper::generateValidatorError($validator);
}
$valid = $validator->valid();
$asset = AssetHelper::uploadCustomRobloxAsset($valid['contentId'], true, base64_encode($request->getContent()));
return route('client.asset', ['id' => $asset->id]);
}
}