add bonus exchange downloaded

This commit is contained in:
xiaomlove
2022-09-17 14:59:46 +08:00
parent dd51f32193
commit fedc67ad5e
23 changed files with 572 additions and 5 deletions

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use App\Repositories\PluginRepository;
use Illuminate\Console\Command;
use Nexus\Plugin\BasePlugin;
class PluginCronjob extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'plugin:cronjob {--action=} {--id=} {--force=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Plugin install / update / delete cronjob handler';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$action = $this->option('action');
$id = $this->option('id');
$force = $this->option('force');
$pluginRep = new PluginRepository();
$pluginRep->cronjob($action, $id, $force);
$log = sprintf("[%s], action: %s, id: %s, force: %s run done !", nexus()->getRequestId(), $action, $id, $force);
$this->info($log);
do_log($log);
return 0;
}
}

View File

@@ -23,6 +23,7 @@ use App\Repositories\AgentAllowRepository;
use App\Repositories\AttendanceRepository;
use App\Repositories\ExamRepository;
use App\Repositories\HitAndRunRepository;
use App\Repositories\PluginRepository;
use App\Repositories\SearchBoxRepository;
use App\Repositories\SearchRepository;
use App\Repositories\TagRepository;
@@ -87,7 +88,10 @@ class Test extends Command
*/
public function handle()
{
$r = \Illuminate\Support\Facades\Cache::lock();
$rep = new PluginRepository();
// $rep->installCronjob();
$r = $rep->getInstalledVersion('xiaomlove/nexusphp-post-like');
dd($r);
}

View File

@@ -35,6 +35,8 @@ class Kernel extends ConsoleKernel
$schedule->command('claim:settle')->hourly()->when(function () {
return Carbon::now()->format('d') == '01';
})->withoutOverlapping();
// $schedule->command('plugin:cronjob')->everyMinute()->withoutOverlapping();
}
/**

View File

@@ -0,0 +1,114 @@
<?php
namespace App\Filament\Resources\System;
use App\Filament\Resources\System\PluginResource\Pages;
use App\Filament\Resources\System\PluginResource\RelationManagers;
use App\Models\Plugin;
use Filament\Forms;
use Filament\Resources\Form;
use Filament\Resources\Resource;
use Filament\Resources\Table;
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\HtmlString;
class PluginResource extends Resource
{
protected static ?string $model = Plugin::class;
protected static ?string $navigationIcon = 'heroicon-o-plus-circle';
protected static ?string $navigationGroup = 'System';
protected static ?int $navigationSort = 99;
protected static bool $shouldRegisterNavigation = false;
protected static function getNavigationLabel(): string
{
return __('admin.sidebar.plugin');
}
public static function getBreadcrumb(): string
{
return self::getNavigationLabel();
}
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('package_name')->label(__('label.plugin.package_name')),
Forms\Components\TextInput::make('remote_url')->label(__('label.plugin.remote_url')),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('id'),
Tables\Columns\TextColumn::make('package_name')->label(__('plugin.labels.package_name')),
Tables\Columns\TextColumn::make('remote_url')->label(__('plugin.labels.remote_url')),
Tables\Columns\TextColumn::make('installed_version')->label(__('plugin.labels.installed_version')),
Tables\Columns\TextColumn::make('statusText')->label(__('label.status')),
])
->filters([
//
])
->actions(self::getActions())
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
}
public static function getPages(): array
{
return [
'index' => Pages\ManagePlugins::route('/'),
];
}
private static function getActions()
{
$actions = [];
$actions[] = Tables\Actions\EditAction::make();
$actions[] = self::buildInstallAction();
$actions[] = self::buildUpdateAction();
$actions[] = Tables\Actions\DeleteAction::make('delete')
->hidden(fn ($record) => $record->status == Plugin::STATUS_NOT_INSTALLED)
->using(function ($record) {
$record->update(['status' => Plugin::STATUS_PRE_DELETE]);
});
return $actions;
}
private static function buildInstallAction()
{
return Tables\Actions\Action::make('install')
->label(__('plugin.actions.install'))
->icon('heroicon-o-arrow-down')
->requiresConfirmation()
->hidden(fn ($record) => $record->status == Plugin::STATUS_NORMAL)
->action(function ($record) {
$record->update(['status' => Plugin::STATUS_PRE_INSTALL]);
})
;
}
private static function buildUpdateAction()
{
return Tables\Actions\Action::make('update')
->label(__('plugin.actions.update'))
->icon('heroicon-o-arrow-up')
->requiresConfirmation()
->hidden(fn ($record) => in_array($record->status, [Plugin::STATUS_NOT_INSTALLED, Plugin::STATUS_PRE_UPDATE]))
->action(function ($record) {
$record->update(['status' => Plugin::STATUS_PRE_UPDATE]);
})
;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Filament\Resources\System\PluginResource\Pages;
use App\Filament\Resources\System\PluginResource;
use Filament\Pages\Actions;
use Filament\Resources\Pages\ManageRecords;
class ManagePlugins extends ManageRecords
{
protected static string $resource = PluginResource::class;
protected ?string $maxContentWidth = 'full';
protected function getActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@@ -25,6 +25,7 @@ class BonusLogs extends NexusModel
const BUSINESS_TYPE_NO_AD = 11;
const BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO = 12;
const BUSINESS_TYPE_LUCKY_DRAW = 13;
const BUSINESS_TYPE_EXCHANGE_DOWNLOAD = 14;
public static array $businessTypes = [
self::BUSINESS_TYPE_CANCEL_HIT_AND_RUN => ['text' => 'Cancel H&R'],
@@ -40,6 +41,7 @@ class BonusLogs extends NexusModel
self::BUSINESS_TYPE_NO_AD => ['text' => 'No ad'],
self::BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO => ['text' => 'Gift to low share ratio'],
self::BUSINESS_TYPE_LUCKY_DRAW => ['text' => 'Lucky draw'],
self::BUSINESS_TYPE_EXCHANGE_DOWNLOAD => ['text' => 'Exchange download'],
];
public static function getBonusForCancelHitAndRun()

32
app/Models/Plugin.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Plugin extends NexusModel
{
protected $fillable = ['display_name', 'package_name', 'remote_url', 'installed_version', 'status', 'description', 'status_result'];
const STATUS_NOT_INSTALLED = -1;
const STATUS_NORMAL = 0;
const STATUS_PRE_INSTALL = 1;
const STATUS_INSTALLING = 2;
const STATUS_INSTALL_FAILED = 3;
const STATUS_PRE_UPDATE = 11;
const STATUS_UPDATING = 12;
const STATUS_UPDATE_FAILED = 13;
const STATUS_PRE_DELETE = 101;
const STATUS_DELETING = 102;
const STATUS_DELETE_FAILED = 103;
public function statusText(): Attribute
{
return new Attribute(
get: fn($value, $attributes) => __('plugin.status.' . $attributes['status'])
);
}
}

View File

@@ -55,4 +55,20 @@ class BaseRepository
return User::query()->findOrFail(intval($user), $fields);
}
protected function executeCommand($command, $format = 'string'): string|array
{
$append = " 2>&1";
if (!str_ends_with($command, $append)) {
$command .= $append;
}
do_log("command: $command");
$result = exec($command, $output, $result_code);
$outputString = implode("\n", $output);
do_log(sprintf('result_code: %s, result: %s, output: %s', $result_code, $result, $outputString));
if ($result_code != 0) {
throw new \RuntimeException($outputString);
}
return $format == 'string' ? $outputString : $output;
}
}

View File

@@ -0,0 +1,187 @@
<?php
namespace App\Repositories;
use App\Models\Plugin;
class PluginRepository extends BaseRepository
{
public function cronjob($action = null, $id = null, $force = false)
{
if ($action == 'install' || $action === null) {
$this->doCronjob('install', $id, $force, Plugin::STATUS_PRE_INSTALL, Plugin::STATUS_INSTALLING);
}
if ($action == 'delete' || $action === null) {
$this->doCronjob('delete', $id, $force, Plugin::STATUS_PRE_DELETE, Plugin::STATUS_DELETING);
}
if ($action == 'update' || $action === null) {
$this->doCronjob('update', $id, $force, Plugin::STATUS_PRE_UPDATE, Plugin::STATUS_UPDATING);
}
}
private function doCronjob($action, $id, $force, $preStatus, $doingStatus)
{
$query = Plugin::query();
if (!$force) {
$query->where('status', $preStatus);
}
if ($id !== null) {
$query->where("id", $id);
}
$list = $query->get();
if ($list->isEmpty()) {
do_log("No plugin need to be $action...");
return;
}
$idArr = $list->pluck('id')->toArray();
Plugin::query()->whereIn('id', $idArr)->update(['status' => $doingStatus]);
foreach ($list as $item) {
match ($action) {
'install' => $this->doInstall($item),
'update' => $this->doUpdate($item),
'delete' => $this->doDelete($item),
default => throw new \InvalidArgumentException("Invalid action: $action")
};
}
}
public function doInstall(Plugin $plugin)
{
$packageName = $plugin->package_name;
try {
$this->execComposerConfig($plugin);
$this->execComposerRequire($plugin);
$output = $this->execPluginInstall($plugin);
$version = $this->getInstalledVersion($packageName);
do_log("success install plugin: $packageName version: $version");
$update = [
'status' => Plugin::STATUS_NORMAL,
'status_result' => $output,
'installed_version' => $version
];
} catch (\Throwable $throwable) {
$update = [
'status' => Plugin::STATUS_INSTALL_FAILED,
'status_result' => $throwable->getMessage()
];
do_log("fail install plugin: " . $packageName);
} finally {
$this->updateResult($plugin, $update);
}
}
public function doDelete(Plugin $plugin)
{
$packageName = $plugin->package_name;
$removeSuccess = true;
try {
$output = $this->execComposerRemove($plugin);
do_log("success remove plugin: $packageName");
$update = [
'status' => Plugin::STATUS_NOT_INSTALLED,
'status_result' => $output,
'installed_version' => null,
];
} catch (\Throwable $throwable) {
$update = [
'status' => Plugin::STATUS_DELETE_FAILED,
'status_result' => $throwable->getMessage()
];
$removeSuccess = false;
do_log("fail remove plugin: " . $packageName);
} finally {
if ($removeSuccess) {
$plugin->delete();
} else {
$this->updateResult($plugin, $update);
}
}
}
public function doUpdate(Plugin $plugin)
{
$packageName = $plugin->package_name;
try {
$output = $this->execComposerUpdate($plugin);
$this->execPluginInstall($plugin);
$version = $this->getInstalledVersion($packageName);
do_log("success update plugin: $packageName to version: $version");
$update = [
'status' => Plugin::STATUS_NORMAL,
'status_result' => $output,
'installed_version' => $version,
];
} catch (\Throwable $throwable) {
$update = [
'status' => Plugin::STATUS_UPDATE_FAILED,
'status_result' => $throwable->getMessage()
];
do_log("fail update plugin: " . $packageName);
} finally {
$this->updateResult($plugin, $update);
}
}
private function getRepositoryKey(Plugin $plugin)
{
return str_replace("xiaomlove/nexusphp-", "", $plugin->package_name);
}
private function execComposerConfig(Plugin $plugin)
{
$command = sprintf("composer config repositories.%s git %s", $this->getRepositoryKey($plugin), $plugin->remote_url);
do_log("[COMPOSER_CONFIG]: $command");
return $this->executeCommand($command);
}
private function execComposerRequire(Plugin $plugin)
{
$command = sprintf("composer require %s", $plugin->package_name);
do_log("[COMPOSER_REQUIRE]: $command");
return $this->executeCommand($command);
}
private function execComposerRemove(Plugin $plugin)
{
$command = sprintf("composer remove %s", $plugin->package_name);
do_log("[COMPOSER_REMOVE]: $command");
return $this->executeCommand($command);
}
private function execComposerUpdate(Plugin $plugin)
{
$command = sprintf("composer update %s", $plugin->package_name);
do_log("[COMPOSER_UPDATE]: $command");
return $this->executeCommand($command);
}
private function execPluginInstall(Plugin $plugin)
{
$command = sprintf("php artisan plugin install %s", $plugin->package_name);
do_log("[PLUGIN_INSTALL]: $command");
return $this->executeCommand($command);
}
private function updateResult(Plugin $plugin, array $update)
{
$update['status_result'] = $update['status_result'] . "\n\nREQUEST_ID: " . nexus()->getRequestId();
do_log("[UPDATE]: " . json_encode($update));
$plugin->update($update);
}
public function getInstalledVersion($packageName)
{
$command = sprintf('composer info |grep -E %s', $packageName);
$result = $this->executeCommand($command);
$parts = preg_split("/[\s]+/", trim($result));
$version = $parts[1];
if (str_contains($version, 'dev')) {
$version .= " $parts[2]";
}
return $version;
}
}