Compare commits

..

No commits in common. 'main' and 'upgrade-project' have entirely different histories.

@ -3,16 +3,10 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
MOBILE_AUTH_TOKEN=Gaobd5OKPdGARLGTD03vSFStrADAxmQ9
# for CORS
SANCTUM_STATEFUL_DOMAINS=[http://localhost:3000]
# for Indokargo
IK_BE_DOMAIN=
IK_SESSION_ID=
IK_OAUTH_ID=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
@ -46,14 +40,11 @@ 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=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_ENDPOINT=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=

@ -1,29 +1,66 @@
### Note: if you forgot how to open readme from vs code:
- Ctrl K + V (Side bar readme)
- Crtl Shift V (Full size read me)
# Laravel Tivi
## Instalation
- open /etc/php/8.2/fpm/php.ini
- upload_max_filesize = 100M
- post_max_size = 100M
- Pull Project on server
- Setting .env from .env.example
- note: fill SANCTUM_LARAVEL_DOMAINS with frontend url
- example: SANCTUM_STATEFUL_DOMAINS=[http://tv.bintangtechnology.com,https://tv.bintangtechnology.com,]
- run command
- sudo chmod -R 777 storage -> laravel storage log permission
- php artisan storage:link -> if u use public storage in laravel, not s3
- composer update {or} /usr/bin/php8.2 /usr/local/bin/composer update
- php artisan migrate --seed {or} php8.2 artisan migrate --seed
- php artisan key:generate {or} php artisan key:generate
- note:
- usually, cors problem cause by chmod on /storage or upload_max_filesize / post_max_size (only happen when u debug with firefox)
## Update
- open project loaction (ex: /var/www/api_tivi)
- run command
- git pull --all
- composer install
- php artisan migrate (if it has database changes)
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

@ -1,86 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Helper\Sts\Indokargo;
use App\Models\StsLog;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Http\Request;
class StsReschedule extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sts:reschedule';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Resschedule failed sts to sts data';
/**
* Execute the console command.
*/
public function handle(): void {
$sleep = 30; // 30 second
while(true) {
$this->info('---------------------------');
$this->info('reschedule sts:' . Carbon::now()->toDateTimeString());
$this->info('---------------------------');
// get reschedu;e sts logs
$success = 0;
$failed = 0;
$rescheduleStsLogs = StsLog::where(['is_retry' => true])->get();
foreach($rescheduleStsLogs as $rescheduleStsLog) {
try {
$partner = $rescheduleStsLog->partner;
$module = $rescheduleStsLog->module;
$serviceName = $rescheduleStsLog->service_name;
$request = new Request((array) $rescheduleStsLog->request_data ?? []);
$localId = $rescheduleStsLog->local_id;
$partnerId = $rescheduleStsLog->partner_id;
$lastSeq = $rescheduleStsLog->seq;
if($partner == StsLog::PARTNER_INDOKARGO) {
if($module == StsLog::MODULE_TV) {
if($serviceName == StsLog::SERVICE_CREATE_TV_ADDRESS) {
Indokargo::createTVAddress($request, $localId, $lastSeq);
} else if($serviceName == StsLog::SERVICE_UPDATE_TV_ADDRESS) {
Indokargo::updateTVAddress($request, $localId, $partnerId, $lastSeq);
} else if($serviceName == StsLog::SERVICE_CHANGE_STATUS_TV_ADDRESS) {
Indokargo::changeStatusAddress($request, $localId, $partnerId, $lastSeq);
} else {
throw new \Exception("Service name '$partner' > '$module' > '$serviceName' not found service");
}
} else {
throw new \Exception("Module name '$partner' > '$module' not found service");
}
} else {
throw new \Exception("Partner name '$partner' not found service");
}
$success++;
} catch(\Throwable $th) {
$failed++;
$this->info("stsLog id: ". $rescheduleStsLog->id . " => " . $th->getMessage());
}
}
// save all retry request is retry false
$rescheduleStsLogIds = $rescheduleStsLogs->pluck('id')->toArray();
StsLog::whereIn('id', $rescheduleStsLogIds)->update(['is_retry' => false]);
$this->info('---------------------------');
$this->info("result: $success success, $failed failed" );
$this->info("Sleep in $sleep seconds" );
$this->info('---------------------------');
sleep($sleep);
}
}
}

@ -1,36 +0,0 @@
<?php
namespace App\Helper;
use Illuminate\Support\Facades\DB;
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;
}
public static function setTimezone($timezone) {
date_default_timezone_set($timezone);
DB::statement("SET TIME ZONE '$timezone'");
}
}

@ -1,37 +0,0 @@
<?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'; }
public static function compileDirtyEloquentToArrMessage($model) {
$dirties = $model->getDirty();
foreach($dirties as $key => $dirty) {
$dirties[$key] = $model->getOriginal($key) . ' => ' . $dirty;
}
return $dirties;
}
}
?>

@ -1,38 +0,0 @@
<?php
namespace App\Helper;
use AWS\CRT\HTTP\Request;
class FileHelper {
static $allowedVideoExtensions = ['mp4', 'mkv'];
static $allowedApkExtensions = ['apk'];
static function convertToStrJsValidation(array $allowedFileExtensions) {
// add leading '.' for every extension;
foreach($allowedFileExtensions as &$allowedFileExtension) { $allowedFileExtension = ".$allowedFileExtension"; }
unset($allowedFileExtension);
return implode(', ', $allowedFileExtensions);
}
static function convertToStrLaraValidation(array $allowedFileExtensions, $type = 'string') {
$validations = [
'mimes:' . implode(',', self::_convertFileExtensionToMimes($allowedFileExtensions)),
'extensions:' . implode(',', $allowedFileExtensions)
];
if($type == 'string') return implode('|', $validations);
else if($type == 'array') return $validations;
else throw new \Exception('Type not valid');
}
private static function _convertFileExtensionToMimes($allowedFileExtensions) {
$mimes = [];
foreach($allowedFileExtensions as $allowedFileExtension) {
if($allowedFileExtension == 'apk') $mimes[] = 'zip'; // mimes of apk == zip
else $mimes[] = $allowedFileExtension;
}
return $mimes;
}
}
?>

@ -1,51 +0,0 @@
<?php
namespace App\Helper\Frontend;
use App\Helper\JSONResponse;
use App\Models\Tv;
use Illuminate\Http\Request;
/**
* Note:
* if any of frontend component need specific api query / data specific.
* Please add function to get data here.
*
* But, if the parent component still needs to pass the url to the
* child component, the url api can be placed in the app/Http/Controllers/api/superadmin/GeneralController
*/
class ApiUtilities {
// for components/app/tv/button/excel.vue
public static function tvExcel(Request $request) {
$request->validate(['a' => 'nullable|string']);
switch($request->a) {
case 'excelTemplate':
return Tv::getExcelTemplate();
break;
case 'validateData':
$tvCodes = $request->tvCodes ?? [];
$tvs = $request->tvs ?? [];
$oValidation = TV::validateExcel($tvs, $tvCodes);
return JSONResponse::Success(['oValidation' => $oValidation]);
break;
case 'uploadExcel':
$tvCodes = $request->tvCodes ?? [];
$tvs = $request->tvs ?? [];
$oValidation = TV::validateExcel($tvs, $tvCodes);
$result = TV::uploadExcel($tvs, $oValidation, $request->user());
return JSONResponse::Success($result);
break;
case 'exportData':
return Tv::getExportData($request);
break;
case 'excelDetail':
return Tv::getExcelDetail($request);
break;
}
throw new \Exception('Invalid Request Command');
}
}
?>

@ -1,180 +0,0 @@
<?php
namespace App\Helper\STS;
use App\Helper\JSONResponse;
use App\Models\StsLog;
use App\Models\Tv;
use Carbon\Carbon;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;
class Indokargo {
const STATUS_SUCCESS = 'Success';
const STATUS_FAILURE = 'Failure';
// const STATUS_WARNING = 'Warning';
//------------------------------------------------------------------------
//-- UTILITIES
private static function _checkIkResponse($res) {
$status = $res['status'];
if($res['status'] == self::STATUS_SUCCESS) return;
else if($res['status'] == self::STATUS_FAILURE) {
// note: Indokargo error message can be array or string
$errorMessage = $res['error']['error_message'];
if(is_array($errorMessage)) throw ValidationException::withMessages($errorMessage);
else throw new \Exception($errorMessage);
}
else {
throw new \Exception("Indokargo status is not valid (ik status = $status)" );
}
}
private static function _HttpTransaction(StsLog $stsLog, $callback) {
try {
return $callback();
} catch(ConnectionException $e) {
// if error cause by connection error, try again later
$stsLog->error_info = [
'message' => $e->getMessage(),
'errors' => $e->getTrace()
];
$stsLog->result = StsLog::STATUS_FAILED;
$stsLog->is_retry = true;
$stsLog->save();
return JSONResponse::Success(['is_warning' => true,
'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]);
} catch(\Throwable $th) {
DB::rollBack();
$stsLog->error_info = [
'message' => $th->getMessage(),
'errors' => $th->getTrace()
];
$stsLog->result = StsLog::STATUS_FAILED;
$stsLog->save();
throw $th;
}
}
//-- END UTILITIES
//------------------------------------------------------------------------
public static function createTVAddress(Request $request, int $tvFk, int $lastSeq = 0) {
$seq = $lastSeq + 1;
/**
* local id = TV FK, partnerId = ik_address_id
* Why not new tv request id as local_id?
* - New Tv request data will be deleted after TV data is inserted into the local DB
*/
$stsLog = new StsLog();
$stsLog->partner = StsLog::PARTNER_INDOKARGO;
$stsLog->is_outgoing = true;
$stsLog->module = StsLog::MODULE_TV;
$stsLog->service_name = StsLog::SERVICE_CREATE_TV_ADDRESS;
$stsLog->local_id = $tvFk;
$stsLog->partner_id = null;
$stsLog->seq = $seq;
$stsLog->request_data = $request->all();
$stsLog->request_time = Carbon::now();
return self::_HttpTransaction($stsLog, function() use($tvFk, $request, $stsLog) {
$request->validate(['code' => 'required|string']);
$result = Http::indokargo()->post('tv/address/create', $request->all());
$res = $result->throw()->json();
// update stsLog
$stsLog->response_data = $res;
$stsLog->response_time = Carbon::now();
self::_checkIkResponse($res);
$ikAddress = $res['data']['0'];
$ikAddressId = $ikAddress['id'];
$stsLog->partner_id = $ikAddressId;
// check ik address id is exist (almost impossible to happen, 0.01%)
$isDuplicateIkAddressId = TV::where([
['ik_address_id', '=', $ikAddressId],
['id', '!=', $tvFk]
])->first();
if($isDuplicateIkAddressId) throw new \Exception("IK Address ID Already exist in current db ($ikAddressId)");
DB::beginTransaction();
$tv = Tv::findOrFail($tvFk);
$tv->ik_address_id = $ikAddressId;
$tv->save();
$stsLog->result = StsLog::STATUS_SUCCESS;
$stsLog->save();
DB::commit();
return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']);
});
}
public static function updateTVAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) {
$seq = $lastSeq + 1;
$request->merge(['ik_address_id' => $ikAddressId]);
$stsLog = new StsLog();
$stsLog->partner = StsLog::PARTNER_INDOKARGO;
$stsLog->is_outgoing = true;
$stsLog->module = StsLog::MODULE_TV;
$stsLog->service_name = StsLog::SERVICE_UPDATE_TV_ADDRESS;
$stsLog->local_id = $tvFk;
$stsLog->partner_id = $ikAddressId;
$stsLog->seq = $seq;
$stsLog->request_data = $request->all();
$stsLog->request_time = Carbon::now();
return self::_HttpTransaction($stsLog, function() use($stsLog, $request) {
$request->validate(['code' => 'required|string', 'ik_address_id' => 'required|string']);
$result = Http::indokargo()->post('tv/address/update-tv', $request->all());
$res = $result->throw()->json();
// update stsLog
$stsLog->response_data = $res;
$stsLog->response_time = Carbon::now();
self::_checkIkResponse($res);
$stsLog->result = StsLog::STATUS_SUCCESS;
$stsLog->save();
DB::commit();
return JSONResponse::Success(['message' => "Success to save data"]);
});
}
public static function changeStatusAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) {
$seq = $lastSeq + 1;
$request->merge(['ik_address_id' => $ikAddressId]);
$stsLog = new StsLog();
$stsLog->partner = StsLog::PARTNER_INDOKARGO;
$stsLog->is_outgoing = true;
$stsLog->module = StsLog::MODULE_TV;
$stsLog->service_name = StsLog::SERVICE_CHANGE_STATUS_TV_ADDRESS;
$stsLog->local_id = $tvFk;
$stsLog->partner_id = $request->ik_address_id;
$stsLog->seq = $seq;
$stsLog->request_data = $request->all();
$stsLog->request_time = Carbon::now();
return self::_HttpTransaction($stsLog, function() use($stsLog, $request) {
$request->validate(['ik_address_id' => 'required|string', 'is_active'=> 'required|boolean']);
$result = Http::indokargo()->post('tv/address/change-status', $request->all());
$res = $result->throw()->json();
// update stsLog
$stsLog->response_data = $res;
$stsLog->response_time = Carbon::now();
self::_checkIkResponse($res);
$stsLog->result = StsLog::STATUS_SUCCESS;
$stsLog->save();
DB::commit();
return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Change Status']);
});
}
}
?>

@ -1,18 +0,0 @@
<?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);
}
}
}

@ -1,20 +0,0 @@
<?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%");
}
});
}
}

@ -9,7 +9,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller {
public function login(Request $request) {
public static function login(Request $request) {
$request->validate(
['username' => 'required|string'],
['password' => 'required|string' ]
@ -27,21 +27,15 @@ class AuthController extends Controller {
}
$token = $user->createToken('auth_token')->plainTextToken;
return JSONResponse::Success(['session' => $user->getObjSession($token)]);
return JSONResponse::Success(['session' => [
'username' => $user->username,
'email' => $user->email,
'token' => $token]
]);
}
// check in middleware
public function check(Request $request) {
return JSONResponse::Success(['session' => $request->user()->getObjSession($request->bearerToken())]);
}
public function logOut(Request $request) {
$request->user()->currentAccessToken()->delete();
public static function check(Request $request) {
return JSONResponse::Success();
}
public function changePassword(Request $request) {
$request->merge(['id' => $request->user()->id]);
return User::changePasswordFromRequest($request);
}
}

@ -1,20 +0,0 @@
<?php
namespace App\Http\Controllers\api\mobile;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\ApkUpdate;
use App\Models\VideoUpdate;
use Illuminate\Http\Request;
class CheckUpdateController extends Controller {
public function videoUpdateLatest(Request $request) {
$lastestVideoUpdate = VideoUpdate::getLatestSelected();
return JSONResponse::Success(['latestVideoUpload' => $lastestVideoUpdate]);
}
public function apkUpdateLatest(Request $request) {
$latestApkUpdate = ApkUpdate::getLatest();
return JSONResponse::Success(['latestApkUpdate' => $latestApkUpdate]);
}
}

@ -1,38 +0,0 @@
<?php
namespace App\Http\Controllers\api\mobile;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\ApkUpdate;
use App\Models\NewTvRequest;
use App\Models\Tv;
use App\Models\VideoUpdate;
use Illuminate\Http\Request;
class TvController extends Controller {
public function newRequest(Request $request) {
return NewTvRequest::checkOrCreateFromRequest($request);
}
public function checkUpdate(Request $request) {
$request->validate(['id' => 'nullable|integer',
'apk_version_code' => 'required|integer',
'apk_version_name' => 'required|string']);
$tv = null;
$latestApkUpdate = null;
$latestVideoUpdate = null;
if($request->id) {
$tv = Tv::find($request->id);
$tv->last_connected_at = now();
$tv->apk_version_code = $request->apk_version_code;
$tv->apk_version_name = $request->apk_version_name;
$tv->save();
$latestApkUpdate = ApkUpdate::getLatest();
$latestVideoUpdate = VideoUpdate::getLatestSelected();
}
return JSONResponse::Success(['tv' => $tv, 'latestApkUpdate' => $latestApkUpdate, 'latestVideoUpdate' => $latestVideoUpdate]);
}
}

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

@ -1,16 +0,0 @@
<?php
namespace App\Http\Controllers\api\superadmin;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\Tv;
use Illuminate\Http\Request;
class GeneralController extends Controller {
public function tvSearch(Request $request) {
$request->validate(['search' => 'nullable|string']);
$tvs = Tv::multiSearch($request->search, ['code'])->orderBy('code', 'asc')->limit(10)->get();
return JSONResponse::Success(['tvs' => $tvs ]);
}
}

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

@ -1,30 +0,0 @@
<?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); }
}

@ -1,32 +0,0 @@
<?php
namespace App\Http\Controllers\api\superadmin\tv;
use App\Helper\DatabaseHelper;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\NewTvRequest;
use Illuminate\Http\Request;
class NewTvRequestController extends Controller {
public function init(Request $request) {
NewTvRequest::deleteExpiredRequests();
$request->validate([
'perPage' => 'nullable|integer|min:1',
...DatabaseHelper::getOrderBysValidations(),
'search' => DatabaseHelper::getSearchValidation()
]);
$newTvRequests = NewTvRequest::with('tv')
->addColumnCanApprove()
->addColumnCanReject()
->multiSearch($request->search, ['code'])
->multiOrderBy($request->orderBys, 'created_at desc')
->paginate($request->perPage ?? 10);
return JSONResponse::Success(['data' => $newTvRequests ]);
}
public function approve(Request $request) { return NewTvRequest::approveFromRequest($request); }
public function reject(Request $request) { return NewTvRequest::rejectFromRequest($request); }
}

@ -1,33 +0,0 @@
<?php
namespace App\Http\Controllers\api\superadmin\tv;
use App\Helper\DatabaseHelper;
use App\Helper\Frontend\ApiUtilities;
use App\Helper\JSONResponse;
use App\Http\Controllers\Controller;
use App\Models\ApkUpdate;
use App\Models\Tv;
use Illuminate\Http\Request;
class TvController extends Controller {
public function init(Request $request) {
$request->validate([
'perPage' => 'nullable|integer|min:1',
'isFirstTime' => 'nullable|boolean',
]);
$additionalData = [];
if($request->isFirstTime) {
$additionalData['apkUpdates'] = ApkUpdate::select('version_code', 'version_name')
->orderBy('version_code', 'desc')
->get();
}
$newTvRequests = TV::validateAndGetEloquentFromRequest($request)->paginate($request->perPage ?? 10);
return JSONResponse::Success(['data' => $newTvRequests, ...$additionalData ]);
}
public function excel(Request $request) { return ApiUtilities::tvExcel($request); }
public function update(Request $request) { return Tv::updateFromRequest($request); }
public function changeStatus(Request $request) { return Tv::changeStatusFromRequest($request); }
}

@ -1,25 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class MobileMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response {
$serverMobileAuthToken = env('MOBILE_AUTH_TOKEN', '');
if(!$serverMobileAuthToken) throw new \Exception('Mobile Auth token in server is not found');
$clientMobileAuthToken = $request->header('mobile-token', '');
if(!$clientMobileAuthToken) $clientMobileAuthToken = $request->header('mobile-auth-token', '');
if($serverMobileAuthToken != $clientMobileAuthToken) throw new \Exception('Invalid Mobile Auth Token');
return $next($request);
}
}

@ -1,142 +0,0 @@
<?php
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\Builder;
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;
use Illuminate\Validation\Rule;
class ApkUpdate extends Model {
use HasFactory;
use CanMultiSearch;
use CanMultiOrderBy;
protected $table = 'apk_updates';
protected $hidden = ['file'];
protected $appends = ['file_url'];
// ---------------------------------------------------------------------------------------
// -- RELATED TO SCOPE
public function scopeNewest(Builder $query){
return $query->orderByDesc('version_code');
}
// -- END RELATED TO SCOPE
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- RELATED TO GET DATA
public static function getLatest(){ return self::latest('version_code')->first(); }
// -- END RELATED TO GET DATA
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- RELATED TO MODIFICATION DATA FROM REQUEST
public static function upsertFromRequest(Request $request) {
$request->validate([
'id' => 'nullable|integer|exists:App\Models\ApkUpdate,id',
'name' => 'required|string',
'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedApkExtensions),
'version_code' => ['required', 'integer', 'min:1', Rule::unique('apk_updates', 'version_code')->ignore($request->id)],
'version_name' => 'required|string',
'change_note' => 'nullable|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();
$apkUpdate = null;
if(!$request->id) $apkUpdate = new ApkUpdate();
else $apkUpdate = ApkUpdate::findOrFail($request->id);
// del old db file location if has old file
if($newDbFileLocation) {
if($apkUpdate->file) $delOldDbFileLocation = $apkUpdate->file;
$apkUpdate->file = $newDbFileLocation;
}
$apkUpdate->name = $request->name;
$apkUpdate->version_code = $request->version_code;
$apkUpdate->version_name = $request->version_name;
$apkUpdate->change_note = $request->change_note;
$apkUpdate->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\ApkUpdate,id']);
try {
DB::beginTransaction();
$apkUpdate = ApkUpdate::findOrFail($request->id);
$oldDbFile = $apkUpdate->file;
$apkUpdate->delete();
if($oldDbFile) self::deleteFile($oldDbFile);
DB::commit();
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
throw $th;
}
}
// -- END RELATED TO MODIFICATION DATA FROM REQUEST
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- 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') . '/apk-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
// ---------------------------------------------------------------------------------------
}

@ -1,22 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TvAppInfo extends Model {
use HasFactory;
protected $table = 'tv_app_infos';
protected $primaryKey = 'tv_fk';
public $incrementing = false;
//------------------------------------------------------------
// -- RELATED TO RELATIONSHIP
/// BELONGS TO
public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); }
// -- END RELATED TO RELATIONSHIP
//------------------------------------------------------------
}

@ -1,18 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TvConnectLog extends Model {
use HasFactory;
//------------------------------------------------------------
// -- RELATED TO RELATIONSHIP
/// BELONGS TO
public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); }
// -- END RELATED TO RELATIONSHIP
//------------------------------------------------------------
}

@ -1,18 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TvSession extends Model {
use HasFactory;
//------------------------------------------------------------
// -- RELATED TO RELATIONSHIP
/// BELONGS TO
public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); }
// -- END RELATED TO RELATIONSHIP
//------------------------------------------------------------
}

@ -1,225 +0,0 @@
<?php
namespace App\Models;
use App\Helper\JSONResponse;
use App\Helper\STS\Indokargo;
use App\Helper\Traits\Models\CanMultiOrderBy;
use App\Helper\Traits\Models\CanMultiSearch;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class NewTvRequest extends Model {
use HasFactory;
use CanMultiSearch;
use CanMultiOrderBy;
protected $table = 'new_tv_requests';
protected $casts = ['device_info'=>AsArrayObject::class];
//------------------------------------------------------------
// -- RELATED TO RELATIONSHIP
// HAS ONE
public function self(): HasOne { return $this->hasOne(self::class, 'id', 'id'); }
/// BELONGS TO
public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); }
// -- END RELATED TO RELATIONSHIP
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO SCOPE
public function scopeAddColumnCanApprove(Builder $query) {
$query->withCount(['self as can_approve' => function($q) {
$q->whereNull('approved_at');
}]);
}
public function scopeAddColumnCanReject(Builder $query) {
$query->withCount(['self as can_reject' => function($q) {
$q->whereNull('approved_at');
}]);
}
// -- END RELATED TO SCOPE
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO DATA FUNCTION
private static function _generateRandomCode(): string {
$totalDigit = 6;
$randomDigit = '';
while(true) {
$randomDigit = strtoupper(Str::random($totalDigit));
$isExist = NewTvRequest::where('code', $randomDigit)->first();
if(!$isExist) break;
}
return $randomDigit;
}
/**
* Rule:
* 1. if it has not been approved for more than the expiry date, deleted it
* 2. if it has been approved & has been responded more than expiry date, delete it
* - case: when has been approved, but intenet connection missing, how can device know
* that the new request tv has been approved?
*/
private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); }
public static function deleteExpiredRequests() {
$expiredTime = self::_getMaxExpiredTime()->toDateTimeString();
NewTvRequest::where(function($q) use ($expiredTime) {
$q->whereNull('approved_at')->where('created_at', '<=', $expiredTime);
})->orWhere(function($q) use ($expiredTime) {
$q->where('responded_at', '<=', $expiredTime );
})->delete();
}
// -- END RELATED TO DATA FUNCTION
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO FROM REQUEST FUNCTION
private static function _checkOrCreateResponse(NewTvRequest $newTvReq) {
if($newTvReq->approved_at) {
// make sure waiting tv_device responded before deleted
if(empty($newTvReq->responded_at)) {
$newTvReq->responded_at = now();
$newTvReq->save();
}
return JSONResponse::Success([
'message' => 'approved',
'new_tv_request' => $newTvReq,
'tv' => $newTvReq->tv
]);
}
return JSONResponse::Success([
'message' => 'not activate',
'new_tv_request' => $newTvReq,
'tv' => null
]);
}
public static function checkOrCreateFromRequest(Request $request) {
$request->validate([
'id' => 'nullable|string|required_with:code|integer',
'code' => 'nullable|string|required_with:id',
'device_info' => 'required|array'
]);
if(!empty($request->id)) {
$checkTvReq = NewTvRequest::where('code', $request->code)->find($request->id);
if($checkTvReq) return self::_checkOrCreateResponse($checkTvReq);
}
$newTvReq = new self;
$newTvReq->code = self::_generateRandomCode();
$newTvReq->device_info = $request->device_info;
$newTvReq->save();
return self::_checkOrCreateResponse($newTvReq);
}
public static function approveFromRequest(Request $request) {
$request->validate([
'id' => 'required|integer|exists:App\Models\NewTvRequest',
'tv' => 'nullable|array',
'existingTv' => 'nullable|array',
'action' => 'required|string',
]);
$newTvReq = NewTvRequest::addColumnCanApprove()
->findOrFail($request->id);
if(!$newTvReq->can_approve) throw new \Exception('Cannot approve current request');
try {
$tv = null;
if($request->action == 'existing') {
$existingTvRequest = new Request($request->existingTv);
$existingTvRequest->validate([
'id' => 'required|integer|exists:App\Models\Tv',
]);
$tv = TV::findOrFail($existingTvRequest->id);
} else if ($request->action == 'new') {
$tvRequest = new Request($request->tv);
if($tvRequest->code) $tvRequest->merge(['code' => strtoupper($tvRequest->code)]);
$tvRequest->validate([
'company_name' => 'nullable|string',
'address' => 'nullable|string',
'street_address' => 'nullable|string',
'col1' => 'nullable|string',
'col2' => 'nullable|string',
'col3' => 'nullable|string',
'col4' => 'nullable|string',
'col5' => 'nullable|string',
'col6' => 'nullable|string',
'col7' => 'nullable|string',
'col8' => 'nullable|string',
'col9' => 'nullable|string',
'col10' => 'nullable|string',
'notes' => 'nullable|string',
]);
DB::beginTransaction();
$tv = new Tv();
$tv->code = TV::generateUniqueCode();
$tv->company_name = $tvRequest->company_name;
$tv->address = $tvRequest->address;
$tv->street_address = $tvRequest->street_address;
$tv->col1 = $tvRequest->col1;
$tv->col2 = $tvRequest->col2;
$tv->col3 = $tvRequest->col3;
$tv->col4 = $tvRequest->col4;
$tv->col5 = $tvRequest->col5;
$tv->col6 = $tvRequest->col6;
$tv->col7 = $tvRequest->col7;
$tv->col8 = $tvRequest->col8;
$tv->col9 = $tvRequest->col9;
$tv->col10 = $tvRequest->col10;
$tv->notes = $tvRequest->notes;
$tv->device_info = $newTvReq->device_info;
$tv->installed_at = now();
$tv->save();
TvLog::historyCreate($request->user(), $tv->id, $tv);
// TODO: waiting execution until update from ops
// NEED TO REFACTOR (cause by add existing tv code)
// // try to sys_to_sys with indokargo
// DB::commit();
// $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id);
// return $jsonResponse;
} else {
throw new \Exception('INVALID FORM ACTION');
}
$newTvReq->approved_at = now();
$newTvReq->tv_fk = $tv->id;
$newTvReq->save();
DB::commit();
return JSONResponse::Success(['message'=>'Success to approve new tv request']);
} catch(\Throwable $th) {
DB::rollback();
throw $th;
}
}
public static function rejectFromRequest(Request $request) {
$request->validate([
'id' => 'required|integer|exists:App\Models\NewTvRequest',
]);
$newTvRequest = NewTvRequest::addColumnCanReject()
->findOrFail($request->id);
if(!$newTvRequest->can_reject) throw new \Exception('Cannot reject current request');
$newTvRequest->delete();
return JSONResponse::Success(['data' => $newTvRequest ]);
}
// -- END RELATED TO FROM REQUEST FUNCTION
//------------------------------------------------------------
}

@ -1,20 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Outlet extends Model {
use HasFactory;
protected $table = 'outlets';
//------------------------------------------------------------
// -- RELATED TO MIGRATION
/// HAS ONE
public function tv_app_info(): HasOne { return $this->hasOne(Tv::class, 'outlet_fk', 'id'); }
// -- END RELATED TO MIGRATION2
//------------------------------------------------------------
}

@ -1,49 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StsLog extends Model {
use HasFactory;
protected $table = 'sts_logs';
protected $fillable = [
'partner',
'is_outgoing',
'module',
'service_name',
'local_id',
'partner_id',
'seq',
'result',
'error_info',
'request_data',
'request_time',
'response_data',
'response_time',
'is_retry',
'created_at',
'updated_at'
];
protected $casts = [
'error_info' => AsArrayObject::class,
'request_data' => AsArrayObject::class,
'response_data' => AsArrayObject::class,
];
const STATUS_SUCCESS = 'success';
const STATUS_FAILED = 'failed';
const STATUS_WARNING = 'warning';
const PARTNER_INDOKARGO = 'indokargo';
// related to indokargo
const MODULE_TV = 'tv';
const SERVICE_CREATE_TV_ADDRESS = 'create-tv-address';
const SERVICE_UPDATE_TV_ADDRESS = 'update-tv-address';
const SERVICE_CHANGE_STATUS_TV_ADDRESS = 'change-statustv-address';
}

@ -1,372 +0,0 @@
<?php
namespace App\Models;
use App\Helper\Common;
use App\Helper\DatabaseHelper;
use App\Helper\JSONResponse;
use App\Helper\STS\Indokargo;
use App\Helper\Traits\Models\CanMultiOrderBy;
use App\Helper\Traits\Models\CanMultiSearch;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class Tv extends Model {
use HasFactory;
use CanMultiSearch;
use CanMultiOrderBy;
protected $table = 'tvs';
protected $hidden = ['ik_cust_id', 'ik_address_id'];
protected $casts = ['device_info'=>AsArrayObject::class];
//------------------------------------------------------------
// -- RELATED TO RELATIONSHIP
/// HAS ONE
public function new_tv_request(): HasOne { return $this->hasOne(NewTvRequest::class, 'tv_fk', 'id'); }
public function tv_app_info(): HasOne { return $this->hasOne(TvAppInfo::class, 'tv_fk', 'id'); }
/// HAS MANY
public function tv_connect_logs(): HasMany { return $this->hasMany(TvConnectLog::class, 'tv_fk', 'id'); }
public function tv_sessions(): HasMany { return $this->hasMany(TvSession::class, 'tv_fk', 'id'); }
/// BELONGS TO
public function outlet(): BelongsTo { return $this->belongsTo(Outlet::class, 'outlet_fk', 'id'); }
// -- END RELATED TO RELATIONSHIP
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO DATA FUNCTION
public static function generateUniqueCode() {
// init
$countSuffixDigit = 5;
date_default_timezone_set('Asia/Jakarta');
$prefixCode = 'TV' . date('Ym');
// try to get unique suffix
$uniqueCode = '';
while(true) {
$suffixCode = strtoupper(Str::random($countSuffixDigit));
$uniqueCode = $prefixCode . $suffixCode;
$isUnique = TV::where('code', $uniqueCode)->first();
if(!$isUnique) break;
}
return $uniqueCode;
}
// -- END RELATED TO DATA FUNCTION
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO REQUEST
public static function validateAndGetEloquentFromRequest(Request $request) {
$request->validate([
...DatabaseHelper::getOrderBysValidations(),
'search' => DatabaseHelper::getSearchValidation(),
'apkVersionCode' => 'nullable|integer',
'tz' => 'required|timezone',
'isActive' => 'nullable|boolean',
'installedAt' => 'nullable|array',
'installedAt.from' => 'nullable|date',
'installedAt.to' => 'nullable|date',
'lastConnectedAt' => 'nullable|array',
'lastConnectedAt.from' => 'nullable|date',
'lastConnectedAt.to' => 'nullable|date',
'lastConnectedDay' => 'nullable|int'
]);
Common::setTimezone($request->tz);
return Tv::when($request->isActive != null, function($q) use($request) {
$q->where('is_active', $request->isActive);
})
->when($request->apkVersionCode, function($q, $apkVersionCode) {
$q->where('apk_version_code', $apkVersionCode);
})
->when($request->installedAt['from'] ?? '', function($q, $from) {
$q->where(DB::raw('DATE(installed_at)'), '>=', $from);
})
->when($request->installedAt['to'] ?? '', function($q, $to) {
$q->where(DB::raw('DATE(installed_at)'), '<=', $to);
})
->when($request->lastConnectedAt['from'] ?? '', function($q, $from) {
$q->where(DB::raw('DATE(last_connected_at)'), '>=', $from);
})
->when($request->lastConnectedAt['to'] ?? '', function($q, $to) {
$q->where(DB::raw('DATE(last_connected_at)'), '<=', $to);
})
->multiSearch($request->search, ['code', 'company_name'])
->multiOrderBy($request->orderBys, 'created_at desc');
}
public static function updateFromRequest(Request $request) {
if($request->code) $request->merge(['code' => strtoupper($request->code)]);
$request->validate([
'id' => 'required|integer|exists:App\Models\Tv',
'code' => ['required','string',Rule::unique('tvs', 'code')
->when($request->id, function($q, $id) { $q->whereNot('id', $id);})],
'company_name' => 'nullable|string',
'address' => 'nullable|string',
'street_address' => 'nullable|string',
'col1' => 'nullable|string',
'col2' => 'nullable|string',
'col3' => 'nullable|string',
'col4' => 'nullable|string',
'col5' => 'nullable|string',
'col6' => 'nullable|string',
'col7' => 'nullable|string',
'col8' => 'nullable|string',
'col9' => 'nullable|string',
'col10' => 'nullable|string',
'notes' => 'nullable|string',
]);
try {
DB::beginTransaction();
$tv = TV::findOrFail($request->id);
$oldTv = $tv->replicate();
$tv->company_name = $request->company_name;
$tv->address = $request->address;
$tv->street_address = $request->street_address;
$tv->code = $request->code;
$tv->col1 = $request->col1;
$tv->col2 = $request->col2;
$tv->col3 = $request->col3;
$tv->col4 = $request->col4;
$tv->col5 = $request->col5;
$tv->col6 = $request->col6;
$tv->col7 = $request->col7;
$tv->col8 = $request->col8;
$tv->col9 = $request->col9;
$tv->col10 = $request->col10;
$tv->notes = $request->notes;
$tv->update();
TvLog::historyUpdate($request->user(), $tv->id, $oldTv, $tv);
DB::commit();
return JSONResponse::Success(['message'=>'Success to update tv data']);
// TODO: waiting from ops workflow (Dont forget to resync co_name, address, street address in ik)
// // try to sys_to_sys with indokargo
// $jsonResponse = Indokargo::updateTVAddress($request, $tv->id, $tv->ik_address_id);
// DB::commit();
// return $jsonResponse;
} catch(\Throwable $th) {
DB::rollback();
throw $th;
}
}
public static function changeStatusFromRequest(Request $request) {
$request->validate(['id' => 'required|integer|exists:App\Models\TV,id']);
try {
DB::beginTransaction();
$tv = Tv::findOrFail($request->id);
$tv->is_active = !$tv->is_active;
$tv->save();
DB::commit();
return JSONResponse::Success(['message'=>'Success to change tv status']);
// TODO: waiting from ops workflow (Dont forget to resync co_name, address, street address in ik)
// // try to sys_to_sys with indokargo
// $jsonResponse = Indokargo::changeStatusAddress(new Request(['is_active' => $tv->is_active]),
// $tv->id, $tv->ik_address_id);
// DB::commit();
// return $jsonResponse;
} catch(\Throwable $th) {
DB::rollback();
throw $th;
}
}
// -- END RELATED TO REQUEST
//------------------------------------------------------------
//------------------------------------------------------------
// -- RELATED TO EXCEL
public static function getExcelDetail(Request $request) {
return JSONResponse::Success(['rows' => TV::validateAndGetEloquentFromRequest($request)->get()]);
}
// ---- RELATED TO EXPORT IMPORT
const EXCEL_SUCCESS = 'success';
const EXCEL_FAILED = 'failed';
const EXCEL_NO_CHANGE = 'no_change';
const EXCEL_UPDATE = 'update';
const EXCEL_TEMPLATE_COLS = ['code', 'company_name', 'address', 'street_address', 'notes',
'col1', 'col2', 'col3', 'col4', 'col5',
'col6', 'col7', 'col8', 'col9', 'col10',
'is_active'];
public static function getExcelTemplate() {
$row = [];
foreach(self::EXCEL_TEMPLATE_COLS as $col) { $row[$col] = ''; }
$rows = [$row];
return JSONResponse::Success(['rows' => $rows]);
}
public static function getExportData(Request $request) {
$rows = TV::validateAndGetEloquentFromRequest($request)->get();
return JSONResponse::Success(['rows' => $rows, 'cols' => self::EXCEL_TEMPLATE_COLS]);
}
// ------ RELATED TO IMPORT
private static function _checkExcelFormat($tvRows) {
if(!$tvRows) throw new \Exception("No Data");
$firstTvRow = $tvRows[0];
if(!$firstTvRow) throw new \Exception('Column not found');
// check if excel is empty or not
$firstTvRow['row'] = null;
if(!array_filter($firstTvRow)) throw new \Exception("Excel is empty");
// check is header col is exists
$errors = [];
foreach(self::EXCEL_TEMPLATE_COLS as $col) {
if(!array_key_exists($col, $firstTvRow)) $errors[$col] = "Column $col is Required";
}
if($errors) throw ValidationException::withMessages($errors);
}
private static function _getCountTvCodes($tvRows, $tvCodes) {
if(!$tvCodes) $tvCodes = array_column($tvRows, 'code');
$tvCodes = array_map(function($code) { return DatabaseHelper::trimUpperNull($code) ?? ''; }, $tvCodes);
return array_count_values($tvCodes);
}
private static function _changeModelFromTvRow(Tv $tv, $tvRow) {
$tv->company_name = $tvRow['company_name'];
$tv->address = $tvRow['address'];
$tv->street_address = $tvRow['street_address'];
$tv->code = $tvRow['code'];
$tv->col1 = $tvRow['col1'];
$tv->col2 = $tvRow['col2'];
$tv->col3 = $tvRow['col3'];
$tv->col4 = $tvRow['col4'];
$tv->col5 = $tvRow['col5'];
$tv->col6 = $tvRow['col6'];
$tv->col7 = $tvRow['col7'];
$tv->col8 = $tvRow['col8'];
$tv->col9 = $tvRow['col9'];
$tv->col10 = $tvRow['col10'];
$tv->notes = $tvRow['notes'];
return $tv;
}
public static function validateExcel($tvRows, $tvCodes = []) {
self::_checkExcelFormat($tvRows);
$countTvCodes = self::_getCountTvCodes($tvRows, $tvCodes);
$endStatus = self::EXCEL_SUCCESS;
$results = [];
foreach($tvRows as $tvRow) {
$status = self::EXCEL_NO_CHANGE;
$message = '';
$tvRow['code'] = strtoupper($tvRow['code'] ?? '');
try {
// STEP 1: check validation
$validator = Validator::make($tvRow, [
'row' => 'required|integer',
'code' => 'required|string',
'company_name' => 'nullable|string',
'address' => 'nullable|string',
'street_address' => 'nullable|string',
'col1' => 'nullable|string',
'col2' => 'nullable|string',
'col3' => 'nullable|string',
'col4' => 'nullable|string',
'col5' => 'nullable|string',
'col6' => 'nullable|string',
'col7' => 'nullable|string',
'col8' => 'nullable|string',
'col9' => 'nullable|string',
'col10' => 'nullable|string',
'notes' => 'nullable|string',
]);
if($validator->fails()) {
$errors = $validator->errors()->toArray();
$messages = [];
foreach($errors as $eMessages) { $messages = array_merge($messages, $eMessages); }
throw new \Exception(implode(', ', $messages));
}
// STEP 2: check code is duplicate or not or not
$code = $tvRow['code'];
if(($countTvCodes[$code] ?? 0) > 1) throw new \Exception('Code is Duplicate in Excel');
// STEP 3: check code existing in database or not
$tvCheck = Tv::where('code', 'ilike', $code)->first();
if(!$tvCheck) throw new \Exception("TV Code '$code' not found in database");
// STEP 4: check has update data or not
$tvCheck = self::_changeModelFromTvRow($tvCheck, $tvRow);
if($tvCheck->isDirty()) {
$status = self::EXCEL_UPDATE;
$message = DatabaseHelper::compileDirtyEloquentToArrMessage($tvCheck);
}
} catch (\Throwable $th) {
$endStatus = self::EXCEL_FAILED;
$status = self::EXCEL_FAILED;
$message = $th->getMessage();
}
$results[] = ['row' => $tvRow['row'], 'status' => $status, 'message' => $message];
}
return ['status' => $endStatus, 'results' => $results];
}
public static function uploadExcel($tvRows, $oValidation, User $user) {
$validationResults = $oValidation['results'];
$countUploads = [
self::EXCEL_UPDATE => 0, self::EXCEL_NO_CHANGE => 0, self::EXCEL_FAILED => 0
];
$additionalErrors = [];
foreach($validationResults as $result) {
$validateStatus = $result['status'];
try {
switch($validateStatus) {
case self::EXCEL_FAILED:
throw new \Exception($result['message']);
break;
case self::EXCEL_UPDATE:
// get sparepart data
$idxTvRow = array_search($result['row'], array_column($tvRows, 'row'));
if($idxTvRow === false) throw new \Exception('Row Not Found');
$tvRow = $tvRows[$idxTvRow];
$tvRow['code'] = strtoupper($tvRow['code'] ?? '');
// try to upsert
DB::beginTransaction();
$tv = TV::where('code', 'ilike', $tvRow['code'])->firstOrFail();
$oldTV = $tv->replicate();
$newTv = self::_changeModelFromTvRow($tv, $tvRow);
if(!$newTv->isDirty()) throw new \Exception('No Change');
$newTv->save();
// save data log
TvLog::historyUpdateExcel($user, $newTv->id, $oldTV, $newTv);
DB::commit();
break;
case self::EXCEL_NO_CHANGE:
default:
break;
}
} catch (\Throwable $th) {
DB::rollBack();
$validateStatus = self::EXCEL_FAILED;
$additionalErrors[] = 'row ' . ($result['row'] ?? '-') . ' => ' . $th->getMessage();
}
$countUploads[$validateStatus]++;
}
return ['countUploads' => $countUploads, 'additionalErrors' => $additionalErrors];
}
// ------ END RELATED TO IMPORT
// ---- END RELATED TO EXPORT IMPORT
// -- END RELATED TO EXCEL
//------------------------------------------------------------
}

@ -1,39 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TvLog extends Model {
use HasFactory;
protected $table = 'tv_logs';
protected $hidden = ['ik_cust_id', 'ik_address_id'];
protected $casts = ['from'=>'object', 'to'=>'object'];
const TYPES = ['create', 'update', 'update-excel'];
public static function historyCreate(User $user, int $tvFk, Tv $newTv) { self::saveHistory('create', $user, $tvFk, null, $newTv); }
public static function historyUpdate(User $user, int $tvFk, Tv $oldTv, Tv $newTv) { self::saveHistory('update', $user, $tvFk, $oldTv, $newTv); }
public static function historyUpdateExcel(User $user, int $tvFk, Tv $oldTv, Tv $newTv) { self::saveHistory('update-excel', $user, $tvFk, $oldTv, $newTv); }
private static function saveHistory(String $type, ?User $user, int $tvFk, ?Tv $oldTv, ?Tv $newTv) {
if(!in_array($type, self::TYPES)) throw new \Exception("Type '$type' No Valid");
$tvLog = new TvLog();
$tvLog->tv_fk = $tvFk;
$tvLog->type = $type;
if($oldTv) {
$oldTv = $oldTv->toArray();
unset($oldTv['id']);
$tvLog->from =$oldTv;
}
if($newTv) {
$newTv = $newTv->toArray();
unset($newTv['id']);
$tvLog->to = $newTv;
}
if($user) { $tvLog->user_fk = $user->id; }
$tvLog->save();
}
}

@ -3,25 +3,14 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Helper\FileHelper;
use App\Helper\JSONResponse;
use App\Helper\Traits\Models\CanMultiOrderBy;
use App\Helper\Traits\Models\CanMultiSearch;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
use CanMultiSearch;
use CanMultiOrderBy;
/**
* The attributes that are mass assignable.
@ -55,123 +44,4 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public static function upsertFromRequest(Request $request) {
$request->validate([
'id' => 'nullable|integer|exists:App\Models\User,id',
'name' => 'required|string',
'username' => 'required|string',
'email' => 'required|email',
'password' => 'required_without:id|string|min:8',
'is_active' => 'required_with:id|boolean',
], [
'password.required_without' => 'The password field is required.',
'is_active.required_with' => 'The is active field is required.'
]);
try {
// try to upsert data
DB::beginTransaction();
$user = null;
if(!$request->id) {
$user = new User();
$user->is_active = $request->is_active;
$user->password = Hash::make($request->password);
}
else $user = User::findOrFail($request->id);
$user->name = $request->email;
$user->email = $request->email;
$user->username = $request->username;
$user->checkUniqueFieldBeforeExecuteDB();
$user->save();
// renew data;
DB::commit();
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
throw $th;
}
}
public static function deleteFromRequest(Request $request) {
$request->validate(['id' => 'required|integer|exists:App\Models\User,id']);
try {
DB::beginTransaction();
$user = User::findOrFail($request->id);
if($user->is_selected) throw new \Exception("Cannot delete video when 'is Selected' is true");
$oldDbFile = $user->file;
$user->delete();
if($oldDbFile) self::deleteFile($oldDbFile);
DB::commit();
return JSONResponse::Success();
} catch (\Throwable $th) {
DB::rollBack();
throw $th;
}
}
public static function changeStatusFromRequest(Request $request) {
$request->validate(['id' => 'required|integer|exists:App\Models\User,id']);
$user = User::findOrFail($request->id);
$user->preventChangeForSelfUser($request->user());
$user->is_active = !$user->is_active;
$user->save();
return JSONResponse::Success();
}
public function preventChangeForSelfUser(User $currentUser) {
if($currentUser->id == $this->id) throw new \Exception("You cannot 'delete' / 'change status' your own user");
}
public function checkUniqueFieldBeforeExecuteDB() {
// check email
$isUsernameExist = User::where('username', $this->username)
->when($this->id, function(Builder $q, $userId) {
$q->where('id', '!=', $userId);
})->first();
if($isUsernameExist) throw new \Exception("Username '" . $this->username . "' has already used by another user");
// check email
$isEmailExist = User::where('email', $this->email)
->when($this->id, function(Builder $q, $userId) {
$q->where('id', '!=', $userId);
})->first();
if($isEmailExist) throw new \Exception("Email '" . $this->email . "' has already used by another user");
}
public static function changePasswordFromRequest(Request $request) {
$request->validate([
'id' => 'required|integer|exists:App\Models\User,id',
'newPassword' => 'required|string|min:8',
'confirmNewPassword' => 'required|string|min:8',
]);
if($request->newPassword != $request->confirmNewPassword) {
throw new \Exception("New Password & Confirm New Pasword are not same");
}
$user = User::findOrFail($request->id);
$user->password = Hash::make($request->newPassword);
$user->save();
return JSONResponse::Success();
}
public function getObjSession($currentAccessToken) {
return [
'name' => $this->name,
'username' => $this->username,
'email' => $this->email,
'token' => $currentAccessToken,
'allowedFileExtension' => [
'video' => FileHelper::convertToStrJsValidation(FileHelper::$allowedVideoExtensions),
'apk' => FileHelper::convertToStrJsValidation(FileHelper::$allowedApkExtensions)
]
];
}
}

@ -1,187 +0,0 @@
<?php
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\Builder;
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', 'file_size_mb'];
// ---------------------------------------------------------------------------------------
// -- RELATED TO SCOPE
public function scopeSelected(Builder $query){
return $query->where('is_selected', true);
}
// -- END RELATED TO SCOPE
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- RELATED TO GET DATA
public static function getLatestSelected(){ return self::selected()->latest('updated_at')->first(); }
// -- END RELATED TO GET DATA
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- RELATED TO ATTRIBUTE
protected function fileSizeMb(): Attribute {
return Attribute::make(
fn() => $this->file_size_kb != null ? round($this->file_size_kb / 1024, 2) : null
);
}
// -- END RELATED TO ATTRIBUTE
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- RELATED TO MODIFICATION DATA FROM REQUEST
public static function upsertFromRequest(Request $request) {
$request->validate([
'id' => 'nullable|integer|exists:App\Models\VideoUpdate,id',
'is_selected' => 'nullable|boolean',
'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedVideoExtensions),
'file_name' => 'required|string',
], [
'file' => ['required_without' => 'The file field is required.']
]);
$delOldDbFileLocation = '';
$newDbFileLocation = '';
$newFileSizeKb = 0;
try {
// save video to storage & get file size
if($request->file) {
$newDbFileLocation = self::saveFile($request->file)['db_url'];
$newFileSizeKb = round($request->file('file')->getSize() / 1024, 2);
}
// 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_size_kb = $newFileSizeKb;
}
$videoUpdate->file_name = $request->file_name;
if($request->is_selected) {
VideoUpdate::where('is_selected', true)
->when($request->id, function(Builder $query, $id) {
$query->where('id', '!=', $id);
})
->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;
}
}
// -- END RELATED MODIFICATION DATA FROM REQUEST
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// -- 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
// ---------------------------------------------------------------------------------------
}

@ -2,7 +2,6 @@
namespace App\Providers;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -18,15 +17,8 @@ class AppServiceProvider extends ServiceProvider
/**
* Bootstrap any application services.
*/
public function boot(): void {
Http::macro('indokargo', function() {
$headers = [
'oauth' => env('INDOKARGO_API_OAUTH', ''),
'session' => env('INDOKARGO_API_SESSION_ID', '')
];
return Http::withHeaders($headers)->timeout(120)
->baseUrl(env('INDOKARGO_API_URL', '').'/v1/');
});
public function boot(): void
{
//
}
}

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

273
composer.lock generated

@ -4,157 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f180282c8ac03d8abf741f87e8b0e4c7",
"content-hash": "de9bbe912febcc4d52bea5bd480b1397",
"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",
@ -1924,62 +1775,6 @@
],
"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",
@ -2196,72 +1991,6 @@
],
"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",

@ -53,7 +53,7 @@ return [
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => true,
'throw' => false,
],
],

@ -1,4 +0,0 @@
Notes:
- Auto Update app
- Auto Update video
- Laravel Sanctum

@ -16,11 +16,11 @@ return new class extends Migration
$table->string('name');
$table->string('username');
$table->string('email')->unique();
$table->timestampTz('email_verified_at')->nullable();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_active');
$table->rememberToken();
$table->timestampsTz();
$table->timestamps();
});
}

@ -14,7 +14,7 @@ return new class extends Migration
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestampTz('created_at')->nullable();
$table->timestamp('created_at')->nullable();
});
}

@ -18,7 +18,7 @@ return new class extends Migration
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestampTz('failed_at')->useCurrent();
$table->timestamp('failed_at')->useCurrent();
});
}

@ -17,9 +17,9 @@ return new class extends Migration
$table->string('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestampTz('last_used_at')->nullable();
$table->timestampTz('expires_at')->nullable();
$table->timestampsTz();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
}

@ -1,27 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('video_updates', function (Blueprint $table) {
$table->id();
$table->string('file_name', 100);
$table->string('file');
$table->boolean('is_selected');
$table->timestampsTz();
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('video_updates');
}
};

@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('apk_updates', function (Blueprint $table) {
$table->id();
$table->string('name', 100);
$table->string('file');
$table->integer('version_code');
$table->string('change_note')->nullable();
$table->timestampsTz();
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('apk_updates');
}
};

@ -1,2 +0,0 @@
Notes:
- TV information & Logs

@ -1,43 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('outlets', function (Blueprint $table) {
$table->id();
$table->string('code', 50)->index();
$table->string('name', 255)->nullable()->index();
$table->string('address', 255)->nullable();
$table->string('street_address', 255)->nullable();
$table->string('phone', 50)->nullable()->index();
$table->double('lat')->default(0);
$table->double('long')->default(0);
$table->string('notes', 255)->nullable();
$table->string('country', 255);
$table->string('ik_country_id', 255);
$table->string('state', 255);
$table->string('ik_state_id', 255);
$table->string('region', 255);
$table->string('ik_region_id', 255);
$table->string('city', 255);
$table->string('ik_city_id', 255);
$table->string('zipcode', 255)->nullable();
$table->timestampTz('last_visited_at')->nullable();
$table->timestampsTz();
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::drop('outlets');
}
};

@ -1,109 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('tvs', function (Blueprint $table) {
$table->id();
$table->string('code', 50)->unique();
$table->string('ik_address_id', 255)->nullable();
$table->integer('apk_version_code')->nullable();
$table->string('apk_version_name')->nullable();
$table->string('company_name')->nullable();
$table->string('address')->nullable();
$table->string('street_address')->nullable();
$table->string('col1', 255)->nullable();
$table->string('col2', 255)->nullable();
$table->string('col3', 255)->nullable();
$table->string('col4', 255)->nullable();
$table->string('col5', 255)->nullable();
$table->string('col6', 255)->nullable();
$table->string('col7', 255)->nullable();
$table->string('col8', 255)->nullable();
$table->string('col9', 255)->nullable();
$table->string('col10', 255)->nullable();
$table->string('notes', 255)->nullable();
$table->boolean('is_active')->default(true);
$table->jsonb('device_info')->nullable();
$table->timestampTz('installed_at');
$table->timestampTz('last_connected_at')->nullable();
$table->timestampsTz();
});
Schema::create('new_tv_requests', function (Blueprint $table) {
$table->id();
$table->string('code', 50);
$table->jsonb('device_info')->nullable();
$table->foreignId('tv_fk')->nullable();
$table->timestampTz('approved_at')->nullable();
$table->timestampTz('responded_at')->nullable();
$table->timestampsTz();
});
Schema::create('tv_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('tv_fk');
$table->string('type');
$table->json('from')->nullable();
$table->json('to')->nullable();
$table->foreignId('user_fk')->nullable();
$table->timestampsTz();
$table->foreign('tv_fk')->references('id')->on('tvs')->cascadeOnDelete();
$table->foreign('user_fk')->references('id')->on('users');
});
// Schema::create('tv_sessions', function (Blueprint $table) {
// $table->id();
// $table->foreignId('tv_fk')->index();
// $table->timestampTz('started_at')->index();
// $table->timestampTz('finished_at')->nullable()->index();
// $table->boolean('is_playing_video');
// $table->jsonb('current_videos')->nullable();
// $table->timestampsTz();
// $table->foreign('tv_fk')->references('id')->on('tvs');
// });
// Schema::create('tv_app_infos', function (Blueprint $table) {
// $table->foreignId('tv_fk')->unique()->index();
// $table->timestampTz('installed_at')->nullable()->index();
// $table->jsonb('current_videos')->nullable();
// $table->timestampTz('last_updated_video')->nullable()->index();
// $table->timestampsTz();
// $table->foreign('tv_fk')->references('id')->on('tvs');
// });
// Schema::create('tv_app_logs', function (Blueprint $table) {
// $table->id();
// $table->foreignId('tv_fk')->index();
// $table->string('requested_to');
// $table->string('ip', 50);
// $table->jsonb('request_data')->nullable();
// $table->enum('result', ['success', 'failed']);
// $table->jsonb('response_data')->nullable();
// $table->timestampsTz();
// $table->foreign('tv_fk')->references('id')->on('tvs');
// });
}
/**
* Reverse the migrations.
*/
public function down(): void {
// Schema::drop('tv_app_logs');
// Schema::drop('tv_app_infos');
// Schema::drop('tv_sessions');
Schema::drop('tv_logs');
Schema::drop('new_tv_requests');
Schema::drop('tvs');
}
};

@ -1,39 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('sts_logs', function (Blueprint $table) {
$table->id();
$table->string('partner', 255)->nullable();
$table->boolean('is_outgoing');
$table->string('module', 255)->nullable();
$table->string('service_name', 255)->nullable();
$table->string('local_id', 255)->nullable();
$table->string('partner_id', 255)->nullable();
$table->enum('result', ['success', 'failed', 'warning']);
$table->jsonb('error_info')->nullable();
$table->tinyInteger('seq');
$table->jsonb('request_data')->nullable();
$table->timestampTz('request_time')->nullable();
$table->jsonb('response_data')->nullable();
$table->timestampTz('response_time')->nullable();
$table->boolean('is_retry')->default('false');
$table->timestampsTz();
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::drop('sts_logs');
}
};

@ -1,26 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void {
Schema::table('video_updates', function(Blueprint $table) {
$table->double('file_size_kb')->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::table('video_updates', function(Blueprint $table) {
$table->dropColumn('file_size_kb');
});
}
};

@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('apk_updates', function (Blueprint $table) {
$table->string('version_name', 50)->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('apk_updates', function (Blueprint $table) {
$table->dropColumn('version_name');
});
}
};

@ -17,9 +17,9 @@ class DatabaseSeeder extends Seeder
\App\Models\User::factory()->create([
'name' => 'Test ',
'username' => 'superadmin',
'email' => 'superadmin@mail.com',
'password' => Crypt::encrypt('socrates')
'username' => 'username',
'email' => 'admin@mail.com',
'password' => Crypt::encrypt('superadmin')
]);
}
}

@ -1,11 +1,7 @@
<?php
use App\Http\Controllers\api\AuthController;
use App\Http\Controllers\api\superadmin\ApkUploadController;
use App\Http\Controllers\api\superadmin\UserManagementController;
use App\Http\Controllers\api\superadmin\VideoUploadController;
use App\Http\Middleware\Cors;
use App\Http\Middleware\MobileMiddleware;
use App\Http\Middleware\UserAuthMiddleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@ -23,17 +19,5 @@ use Illuminate\Support\Facades\Route;
const USER_MIDDLEWARES = ['auth:sanctum', UserAuthMiddleware::class];
Route::controller(AuthController::class )->group(function() {
Route::post('/login', 'login');
Route::middleware(USER_MIDDLEWARES)->group(function() {
Route::post('/auth/check', 'check');
Route::post('/auth/change-password', 'changePassword');
Route::post('/auth/logout', 'logout');
});
});
Route::middleware(USER_MIDDLEWARES)->group(function() {
Route::prefix('/superadmin')->group(base_path('routes/api/superadmin.php'));
});
Route::middleware(MobileMiddleware::class)->group(function() {
Route::prefix('/mobile')->group(base_path('routes/api/mobile.php'));
Route::middleware(USER_MIDDLEWARES)->post('/auth/check', 'check');
});

@ -1,16 +0,0 @@
<?php
use App\Http\Controllers\api\mobile\CheckUpdateController;
use App\Http\Controllers\api\mobile\TvController;
use Illuminate\Support\Facades\Route;
Route::controller(CheckUpdateController::class)->group(function() {
Route::post('/check-update/video-update/latest', 'videoUpdateLatest');
Route::post('/check-update/apk-update/latest', 'apkUpdateLatest');
});
Route::controller(TvController::class)->group(function() {
Route::post('/tv/new-request', 'newRequest');
Route::post('/tv/check-update', 'checkUpdate');
});
?>

@ -1,52 +0,0 @@
<?php
use App\Http\Controllers\api\superadmin\ApkUploadController;
use App\Http\Controllers\api\superadmin\GeneralController;
use App\Http\Controllers\api\superadmin\tv\NewTvRequestController;
use App\Http\Controllers\api\superadmin\tv\TvController;
use App\Http\Controllers\api\superadmin\UserManagementController;
use App\Http\Controllers\api\superadmin\VideoUploadController;
use Illuminate\Support\Facades\Route;
Route::controller(GeneralController::class)->group(function() {
Route::post('/general/tv/search', 'tvSearch');
});
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');
});
Route::controller(ApkUploadController::class)->group(function() {
Route::post('/apk-upload', 'init');
Route::post('/apk-upload/save', 'save');
Route::post('/apk-upload/update', 'update');
Route::post('/apk-upload/delete', 'delete');
});
Route::controller(UserManagementController::class)->group(function() {
Route::post('/user-management', 'init');
Route::post('/user-management/save', 'save');
Route::post('/user-management/update', 'update');
Route::post('/user-management/change-password', 'changePassword');
Route::post('/user-management/change-status', 'changeStatus');
Route::post('/user-management/delete', 'delete');
});
Route::controller(TvController::class)->group(function() {
Route::post('/tv/tv', 'init');
Route::post('/tv/tv/update', 'update');
Route::post('/tv/tv/change-status', 'changeStatus');
Route::post('/tv/tv/excel', 'excel');
});
Route::controller(NewTvRequestController::class)->group(function() {
Route::post('/tv/new-tv-request', 'init');
Route::post('/tv/new-tv-request/approve', 'approve');
Route::post('/tv/new-tv-request/reject', 'reject');
Route::post('/tv/new-tv-request/tv/search', 'tvSearch');
});
?>
Loading…
Cancel
Save