Merge branch 'upload-video'

mobile
ricky rx 2 years ago
commit a64a017f00

@ -40,11 +40,14 @@ MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
STORAGE_FOLDER='mytivi'
APP_STORAGE=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_DEFAULT_REGION=
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_ENDPOINT=
PUSHER_APP_ID=
PUSHER_APP_KEY=

@ -0,0 +1,29 @@
<?php
namespace App\Helper;
class Common {
public static function convertRequestConfig(?array $requestConfig): array {
$config = [];
if (isset($requestConfig['timezone'])) $config['timezone'] = $requestConfig['timezone'];
if (isset($requestConfig['attendanceRadius'])) $config['attendance_radius'] = (double) $requestConfig['attendanceRadius'];
if (isset($requestConfig['mustInRadius'])) $config['must_in_radius'] = self::trueOrFalse($requestConfig['mustInRadius']);
if (isset($requestConfig['mustTakePicture'])) $config['must_take_picture'] = self::trueOrFalse($requestConfig['mustTakePicture']);
return $config;
}
public static function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
public static function trueOrFalse(mixed $value): bool {
return ($value === "true" || $value === "1" || $value === 1 || $value === true) ? true : false;
}
}

@ -0,0 +1,29 @@
<?php
namespace App\Helper;
use Illuminate\Validation\Rule;
class DatabaseHelper {
public static function str2SearchPatern($str) {
return '%'.preg_replace('!\s+!', '%', trim($str ?? '')).'%';
}
public static function trimNull($str) {
if(empty($str)) return null;
$str = trim($str);
return $str ? $str : null;
}
public static function trimUpperNull($str) {
$str = self::trimNull($str);
return $str ? strtoupper($str) : null;
}
public static function getOrderBysValidations($key = 'orderBys') {
return [
$key => 'nullable|array',
"$key.*" => ['nullable', 'string', Rule::in(['asc', 'desc'])],
];
}
public static function getSearchValidation() { return 'nullable|string'; }
}
?>

@ -13,8 +13,15 @@ class FileHelper {
return implode(', ', $allowedFileExtensions);
}
static function convertToStrLaraValidation(array $allowedFileExtensions) {
return implode(',', $allowedFileExtensions);
static function convertToStrLaraValidation(array $allowedFileExtensions, $type = 'string') {
$validations = [
'mimes:' . implode(',', $allowedFileExtensions),
'extensions:' . implode(',', $allowedFileExtensions)
];
if($type == 'string') return implode('|', $validations);
else if($type == 'array') return $validations;
else throw new \Exception('Type not valid');
}
}
?>

@ -0,0 +1,18 @@
<?php
namespace App\Helper\Traits\Models;
use Illuminate\Database\Eloquent\Builder;
trait CanMultiOrderBy {
public function scopeMultiOrderBy(Builder $queryBuilder, ?Array $orderBys, string $defaultOrderBy = '') {
$orderBys = $orderBys ?? [];
if(!empty($orderBys)) {
foreach($orderBys as $column => $direction) {
$queryBuilder->orderBy($column, $direction);
}
} else if($defaultOrderBy) {
$queryBuilder->orderByRaw($defaultOrderBy);
}
}
}

@ -0,0 +1,20 @@
<?php
namespace App\Helper\Traits\Models;
use App\Helper\DatabaseHelper;
use Illuminate\Database\Eloquent\Builder;
trait CanMultiSearch {
public function scopeMultiSearch(Builder $queryBuilder, string|null $search, Array $searchColumns) {
$search = trim($search ?? '');
if(empty($search)) return $queryBuilder;
$search = DatabaseHelper::str2SearchPatern($search);
return $queryBuilder->where(function($q) use($search, $searchColumns) {
foreach($searchColumns as $column) {
$q->orWhere($column, 'ilike', "%$search%");
}
});
}
}

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\api\superadmin;
use App\Helper\DatabaseHelper;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\VideoUpdate;
use Illuminate\Http\Request;
class VideoUploadController extends Controller {
public function init(Request $request) {
$request->validate([
'perPage' => 'nullable|integer|min:1',
...DatabaseHelper::getOrderBysValidations(),
'search' => DatabaseHelper::getSearchValidation()
]);
$data = VideoUpdate::multiSearch($request->search, ['file_name'])
->multiOrderBy($request->orderBys, 'created_at desc')
->paginate($request->perPage ?? 10 );
return JSONResponse::Success(['data' => $data]);
}
public function save(Request $request) { return VideoUpdate::upsertFromRequest($request); }
public function update(Request $request) { return VideoUpdate::upsertFromRequest($request); }
public function delete(Request $request) { return VideoUpdate::deleteFromRequest($request); }
public function changeSelectedVideo(Request $request) { return VideoUpdate::changeSelectedVideoFromRequest($request); }
}

@ -2,11 +2,146 @@
namespace App\Models;
use App\Helper\Common;
use App\Helper\FileHelper;
use App\Helper\JSONResponse;
use App\Helper\Traits\Models\CanMultiOrderBy;
use App\Helper\Traits\Models\CanMultiSearch;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\File;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class VideoUpdate extends Model {
use HasFactory;
use CanMultiSearch;
use CanMultiOrderBy;
protected $table = 'video_updates';
protected $hidden = ['file'];
protected $appends = ['file_url'];
public static function upsertFromRequest(Request $request) {
$request->validate([
'id' => 'nullable|integer|exists:App\Models\VideoUpdate,id',
'is_selected' => 'nullable|in:true,false',
'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedVideoExtensions),
'file_name' => 'required|string',
], [
'file' => ['required_without' => 'The file field is required.']
]);
$delOldDbFileLocation = '';
$newDbFileLocation = '';
try {
// save photo
if($request->file) $newDbFileLocation = self::saveFile($request->file)['db_url'];
// try to upsert data
DB::beginTransaction();
$videoUpdate = null;
if(!$request->id) $videoUpdate = new VideoUpdate();
else $videoUpdate = VideoUpdate::findOrFail($request->id);
// del old db file location if has old file
if($newDbFileLocation) {
if($videoUpdate->file) $delOldDbFileLocation = $videoUpdate->file;
$videoUpdate->file = $newDbFileLocation;
}
$videoUpdate->file_name = $request->file_name;
if($request->is_selected == 'true') {
VideoUpdate::where('is_selected', true)->update(['is_selected' => false]);
$videoUpdate->is_selected = true;
} else $videoUpdate->is_selected = false;
// renew data
$videoUpdate->save();
// delete old file if exist
if($delOldDbFileLocation) self::deleteFile($delOldDbFileLocation);
DB::commit();
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
if($newDbFileLocation) self::deleteFile($newDbFileLocation);
throw $th;
}
}
public static function deleteFromRequest(Request $request) {
$request->validate(['id' => 'required|integer|exists:App\Models\VideoUpdate,id']);
try {
DB::beginTransaction();
$videoUpdate = VideoUpdate::findOrFail($request->id);
if($videoUpdate->is_selected) throw new \Exception("Cannot delete video when 'is Selected' is true");
$oldDbFile = $videoUpdate->file;
$videoUpdate->delete();
if($oldDbFile) self::deleteFile($oldDbFile);
DB::commit();
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
throw $th;
}
}
public static function changeSelectedVideoFromRequest(Request $request) {
$request->validate(['id' => 'required|integer|exists:App\Models\VideoUpdate,id']);
try {
DB::beginTransaction();
$videoUpdate = VideoUpdate::findOrFail($request->id);
$videoUpdate->is_selected = !$videoUpdate->is_selected;
$videoUpdate->save();
DB::commit();
if($videoUpdate->is_selected) {
VideoUpdate::where([
['id', '!=', $videoUpdate->id],
['is_selected', true]
])->update(['is_selected' => false]);
}
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
throw $th;
}
}
// -- File UTILITIES
protected function fileUrl(): Attribute {
return Attribute::make(
fn() => $this->file ? Storage::disk('s3')->url($this->file) : ''
);
}
private static function fileFolder() { return env('STORAGE_FOLDER', 'tivi') . '/video-upload'; }
public static function saveFile($file) {
if (!$file->isValid()) throw new \Exception('File is not valid');
//Save file to local data
$fileName = self::getFileName($file);
$path = self::fileFolder();
Storage::disk('s3')->put($path . '/' . $fileName, file_get_contents(new File($file)), 'public');
return ['db_url' => "$path/$fileName"];
}
private static function getFileName($file) {
$tz = Carbon::now()->timestamp;
$extension = $file->getClientOriginalExtension();
$name = "$tz-" . Common::generateRandomString();
return "$name.$extension";
}
public static function deleteFile($dbUrl) { Storage::disk('s3')->delete($dbUrl); }
// -- END File UTILITIES
}

@ -9,7 +9,8 @@
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^11.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.8"
"laravel/tinker": "^2.8",
"league/flysystem-aws-s3-v3": "3.0"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",

273
composer.lock generated

@ -4,8 +4,157 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "de9bbe912febcc4d52bea5bd480b1397",
"content-hash": "f180282c8ac03d8abf741f87e8b0e4c7",
"packages": [
{
"name": "aws/aws-crt-php",
"version": "v1.2.4",
"source": {
"type": "git",
"url": "https://github.com/awslabs/aws-crt-php.git",
"reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/eb0c6e4e142224a10b08f49ebf87f32611d162b2",
"reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35||^5.6.3||^9.5",
"yoast/phpunit-polyfills": "^1.0"
},
"suggest": {
"ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality."
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "AWS SDK Common Runtime Team",
"email": "aws-sdk-common-runtime@amazon.com"
}
],
"description": "AWS Common Runtime for PHP",
"homepage": "https://github.com/awslabs/aws-crt-php",
"keywords": [
"amazon",
"aws",
"crt",
"sdk"
],
"support": {
"issues": "https://github.com/awslabs/aws-crt-php/issues",
"source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.4"
},
"time": "2023-11-08T00:42:13+00:00"
},
{
"name": "aws/aws-sdk-php",
"version": "3.304.8",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "0079eaa0a0eaef2d73d0a4a11389cdfce1d33189"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0079eaa0a0eaef2d73d0a4a11389cdfce1d33189",
"reference": "0079eaa0a0eaef2d73d0a4a11389cdfce1d33189",
"shasum": ""
},
"require": {
"aws/aws-crt-php": "^1.2.3",
"ext-json": "*",
"ext-pcre": "*",
"ext-simplexml": "*",
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
"guzzlehttp/promises": "^1.4.0 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"mtdowling/jmespath.php": "^2.6",
"php": ">=7.2.5",
"psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
"andrewsville/php-token-reflection": "^1.4",
"aws/aws-php-sns-message-validator": "~1.0",
"behat/behat": "~3.0",
"composer/composer": "^1.10.22",
"dms/phpunit-arraysubset-asserts": "^0.4.0",
"doctrine/cache": "~1.4",
"ext-dom": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-sockets": "*",
"nette/neon": "^2.3",
"paragonie/random_compat": ">= 2",
"phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5",
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0",
"sebastian/comparator": "^1.2.3 || ^4.0",
"yoast/phpunit-polyfills": "^1.0"
},
"suggest": {
"aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
"doctrine/cache": "To use the DoctrineCacheAdapter",
"ext-curl": "To send requests using cURL",
"ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
"ext-sockets": "To use client-side monitoring"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Aws\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Amazon Web Services",
"homepage": "http://aws.amazon.com"
}
],
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
"homepage": "http://aws.amazon.com/sdkforphp",
"keywords": [
"amazon",
"aws",
"cloud",
"dynamodb",
"ec2",
"glacier",
"s3",
"sdk"
],
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.304.8"
},
"time": "2024-04-19T18:13:09+00:00"
},
{
"name": "brick/math",
"version": "0.11.0",
@ -1775,6 +1924,62 @@
],
"time": "2024-04-07T19:17:50+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
"reference": "f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c",
"reference": "f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c",
"shasum": ""
},
"require": {
"aws/aws-sdk-php": "^3.132.4",
"league/flysystem": "^2.0.0 || ^3.0.0",
"league/mime-type-detection": "^1.0.0",
"php": "^8.0.2"
},
"conflict": {
"guzzlehttp/guzzle": "<7.0",
"guzzlehttp/ringphp": "<1.1.1"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\Flysystem\\AwsS3V3\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "AWS S3 filesystem adapter for Flysystem.",
"keywords": [
"Flysystem",
"aws",
"file",
"files",
"filesystem",
"s3",
"storage"
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
"source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.0.0"
},
"time": "2022-01-13T21:11:49+00:00"
},
{
"name": "league/flysystem-local",
"version": "3.25.1",
@ -1991,6 +2196,72 @@
],
"time": "2024-04-12T21:02:21+00:00"
},
{
"name": "mtdowling/jmespath.php",
"version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/jmespath/jmespath.php.git",
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b",
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"symfony/polyfill-mbstring": "^1.17"
},
"require-dev": {
"composer/xdebug-handler": "^3.0.3",
"phpunit/phpunit": "^8.5.33"
},
"bin": [
"bin/jp.php"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"files": [
"src/JmesPath.php"
],
"psr-4": {
"JmesPath\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Declaratively specify how to extract elements from a JSON document",
"keywords": [
"json",
"jsonpath"
],
"support": {
"issues": "https://github.com/jmespath/jmespath.php/issues",
"source": "https://github.com/jmespath/jmespath.php/tree/2.7.0"
},
"time": "2023-08-25T10:54:48+00:00"
},
{
"name": "nesbot/carbon",
"version": "3.2.4",

@ -1,6 +1,7 @@
<?php
use App\Http\Controllers\api\AuthController;
use App\Http\Controllers\api\superadmin\VideoUploadController;
use App\Http\Middleware\Cors;
use App\Http\Middleware\UserAuthMiddleware;
use Illuminate\Http\Request;
@ -25,3 +26,14 @@ Route::controller(AuthController::class )->group(function() {
Route::post('/auth/logout', 'logout');
});
});
Route::middleware(USER_MIDDLEWARES)->prefix('superadmin')->group(function() {
Route::controller(VideoUploadController::class)->group(function() {
Route::post('/video-upload', 'init');
Route::post('/video-upload/save', 'save');
Route::post('/video-upload/update', 'update');
Route::post('/video-upload/delete', 'delete');
Route::post('/video-upload/change-selected-video', 'changeSelectedVideo');
});
});
// tmux session, tmux attach session -t
Loading…
Cancel
Save