mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 17:17:13 +08:00
特性:修复一堆问题
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
.vendor
|
||||
|
||||
175
app/Console/Commands/Monitor.php
Normal file
175
app/Console/Commands/Monitor.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Monitor as MonitorModel;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class Monitor extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'monitor';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '耗子Linux面板 - 系统监控';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (Setting::query()->where('name', 'monitor')->value('value')) {
|
||||
$info = self::getNowMonitor();
|
||||
MonitorModel::query()->create(['info' => json_encode($info)]);
|
||||
// 删除过期的记录
|
||||
$days = Setting::query()->where('name', 'monitor_days')->value('value');
|
||||
MonitorModel::query()->where('created_at', '<', Carbon::now()->subDays($days))->delete();
|
||||
$this->info(time().' 监控完成');
|
||||
} else {
|
||||
$this->info('监控未开启');
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统资源统计
|
||||
* @return array
|
||||
*/
|
||||
private function getNowMonitor(): array
|
||||
{
|
||||
// 第一次获取网络信息
|
||||
$net_info1 = $this->getNetInfo();
|
||||
// 卡它一秒钟
|
||||
sleep(1);
|
||||
// 第二次获取网络信息
|
||||
$net_info2 = $this->getNetInfo();
|
||||
|
||||
// CPU统计信息及负载
|
||||
$cpu_info = file_get_contents('/proc/cpuinfo');
|
||||
$physical_list = array();
|
||||
$physical_sum = 0;
|
||||
$siblings_sum = 0;
|
||||
preg_match("/(\d+\.\d+), (\d+\.\d+), (\d+\.\d+)/", exec('uptime'), $uptime);
|
||||
$uptime_1 = $uptime[1] ?? 'No';
|
||||
|
||||
$p_list = explode("\nprocessor", $cpu_info);
|
||||
foreach ($p_list as $key => $val) {
|
||||
preg_match("/physical id\s*:(.*)/", $val, $physical);
|
||||
preg_match("/cpu cores\s*:(.*)/", $val, $cores);
|
||||
preg_match("/siblings\s*:(.*)/", $val, $siblings);
|
||||
if (isset($physical[1])) {
|
||||
if (!in_array($physical[1], $physical_list)) {
|
||||
$physical_sum += 1;
|
||||
|
||||
if (isset($siblings[1])) {
|
||||
$siblings_sum += $siblings[1];
|
||||
}
|
||||
}
|
||||
$physical_list[] = $physical[1];
|
||||
}
|
||||
}
|
||||
|
||||
// CPU使用率
|
||||
$cpu_use = 0.1;
|
||||
|
||||
$result = explode("\n", shell_exec('ps aux'));
|
||||
foreach ($result as $key => $val) {
|
||||
$val = preg_replace("/\s+/", " ", $val);
|
||||
$val = (explode(' ', $val));
|
||||
$cpu_use += isset($val[2]) ? (float) $val[2] : 0;
|
||||
}
|
||||
$cpu_use = $siblings_sum > 0 ? ($cpu_use / $siblings_sum) : $cpu_use;
|
||||
$cpu_use = round($cpu_use, 2);
|
||||
$cpu_use = min($cpu_use, 100);
|
||||
|
||||
|
||||
// 内存使用率
|
||||
$result = explode("\n", shell_exec('free -m'));
|
||||
foreach ($result as $key => $val) {
|
||||
if (str_contains($val, 'Mem')) {
|
||||
$mem_list = preg_replace("/\s+/", " ", $val);
|
||||
} elseif (str_contains($val, 'Swap')) {
|
||||
$swap_list = preg_replace("/\s+/", " ", $val);
|
||||
}
|
||||
}
|
||||
$mem_arr = explode(' ', $mem_list);
|
||||
$swap_arr = explode(' ', $swap_list);
|
||||
// 内存大小MB
|
||||
$mem_total = $mem_arr[1];
|
||||
// Swap大小MB
|
||||
$swap_total = $swap_arr[1];
|
||||
// 使用中MB
|
||||
$mem_use = (str_contains($result[0], 'buff/cache')) ? $mem_arr[2] : ($mem_arr[2] - $mem_arr[5] - $mem_arr[6]);
|
||||
// Swap使用中MB
|
||||
$swap_use = $swap_arr[2];
|
||||
// 使用中%
|
||||
$mem_use_p = round($mem_use / $mem_total, 2) * 100;
|
||||
// Swap使用中%
|
||||
$swap_use_p = round($swap_use / $swap_total, 2) * 100;
|
||||
// 1分钟负载%
|
||||
$uptime_1_p = $uptime_1 * 10;
|
||||
$uptime_1_p = min($uptime_1_p, 100);
|
||||
|
||||
// 构建返回数组
|
||||
$res['cpu_use'] = $cpu_use;
|
||||
$res['uptime'] = $uptime_1;
|
||||
$res['uptime_p'] = $uptime_1_p;
|
||||
$res['mem_total'] = $mem_total;
|
||||
$res['mem_use'] = $mem_use;
|
||||
$res['mem_use_p'] = $mem_use_p;
|
||||
$res['swap_total'] = $swap_total;
|
||||
$res['swap_use'] = $swap_use;
|
||||
$res['swap_use_p'] = $swap_use_p;
|
||||
$res['tx_now'] = $net_info2['tx'] - $net_info1['tx'];
|
||||
$res['rx_now'] = $net_info2['rx'] - $net_info1['rx'];
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络统计信息
|
||||
* @return array
|
||||
*/
|
||||
private function getNetInfo(): array
|
||||
{
|
||||
$net_result = file_get_contents('/proc/net/dev');
|
||||
$net_result = explode("\n", $net_result);
|
||||
foreach ($net_result as $key => $val) {
|
||||
if ($key < 2) {
|
||||
continue;
|
||||
}
|
||||
$val = str_replace(':', ' ', trim($val));
|
||||
$val = preg_replace("/[ ]+/", " ", $val);
|
||||
$arr = explode(' ', $val);
|
||||
if (!empty($arr[0])) {
|
||||
$arr = array($arr[0], $arr[1], $arr[9]);
|
||||
$all_rs[$arr[0].$key] = $arr;
|
||||
}
|
||||
}
|
||||
ksort($all_rs);
|
||||
$tx = 0;
|
||||
$rx = 0;
|
||||
foreach ($all_rs as $key => $val) {
|
||||
// 排除本地lo
|
||||
if (str_contains($key, 'lo')) {
|
||||
continue;
|
||||
}
|
||||
$tx += $val[2];
|
||||
$rx += $val[1];
|
||||
}
|
||||
$res['tx'] = $tx;
|
||||
$res['rx'] = $rx;
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,9 @@ class Panel extends Command
|
||||
{
|
||||
$action = $this->argument('action');
|
||||
switch ($action) {
|
||||
case 'init':
|
||||
$this->init();
|
||||
break;
|
||||
case 'update':
|
||||
$this->update();
|
||||
break;
|
||||
@@ -46,9 +49,6 @@ class Panel extends Command
|
||||
case 'writePluginUnInstall':
|
||||
$this->writePluginUnInstall();
|
||||
break;
|
||||
case 'writePluginUpdate':
|
||||
$this->writePluginUpdate();
|
||||
break;
|
||||
default:
|
||||
$this->error('错误的操作');
|
||||
break;
|
||||
@@ -56,6 +56,19 @@ class Panel extends Command
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @return void
|
||||
*/
|
||||
private function init(): void
|
||||
{
|
||||
Setting::query()->updateOrCreate(['name' => 'name'], ['value' => '耗子Linux面板']);
|
||||
Setting::query()->updateOrCreate(['name' => 'monitor'], ['value' => '1']);
|
||||
Setting::query()->updateOrCreate(['name' => 'monitor_days'], ['value' => '30']);
|
||||
Setting::query()->updateOrCreate(['name' => 'mysql_root_password'], ['value' => '']);
|
||||
Setting::query()->updateOrCreate(['name' => 'postgresql_root_password'], ['value' => '']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新面板
|
||||
* @return void
|
||||
@@ -83,10 +96,10 @@ class Panel extends Command
|
||||
$this->info(shell_exec('rm -rf /tmp/panel.zip'));
|
||||
$this->info(shell_exec('rm -rf /tmp/database.sqlite'));
|
||||
$this->info(shell_exec('rm -rf /tmp/plugins'));
|
||||
$this->info('正在更新数据库...');
|
||||
$this->info('正在更新面板数据库...');
|
||||
$this->info(shell_exec('cd /www/panel && php-panel artisan migrate'));
|
||||
$this->info('正在重启面板服务...');
|
||||
$this->info(shell_exec('systemctl reload panel.service'));
|
||||
$this->info(shell_exec('systemctl restart panel.service'));
|
||||
$this->info('更新完成');
|
||||
}
|
||||
|
||||
@@ -97,21 +110,26 @@ class Panel extends Command
|
||||
private function getInfo(): void
|
||||
{
|
||||
$user = User::query()->where('id', 1);
|
||||
// 判空
|
||||
if (empty($user)) {
|
||||
$this->error('获取失败');
|
||||
return;
|
||||
}
|
||||
// 生成唯一信息
|
||||
$username = Str::random(6);
|
||||
$password = Str::random(12);
|
||||
// 入库
|
||||
$user->update([
|
||||
'username' => $username,
|
||||
'password' => Hash::make($password),
|
||||
]);
|
||||
$this->info('面板用户名:' . $username);
|
||||
$this->info('面板密码:' . $password);
|
||||
// 判空
|
||||
if (empty($user)) {
|
||||
User::query()->create([
|
||||
'id' => 1,
|
||||
'username' => $username,
|
||||
'password' => Hash::make($password),
|
||||
]);
|
||||
} else {
|
||||
// 入库
|
||||
$user->update([
|
||||
'username' => $username,
|
||||
'password' => Hash::make($password),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->info('面板用户名:'.$username);
|
||||
$this->info('面板密码:'.$password);
|
||||
$this->info('访问地址:http://IP:8888');
|
||||
}
|
||||
|
||||
@@ -122,19 +140,15 @@ class Panel extends Command
|
||||
private function writePluginInstall(): void
|
||||
{
|
||||
$pluginSlug = $this->argument('a1');
|
||||
$pluginName = $this->argument('a2');
|
||||
$pluginVersion = $this->argument('a3');
|
||||
|
||||
// 判空
|
||||
if (empty($pluginSlug) || empty($pluginName) || empty($pluginVersion)) {
|
||||
if (empty($pluginSlug)) {
|
||||
$this->error('参数错误');
|
||||
return;
|
||||
}
|
||||
// 入库
|
||||
Plugin::query()->create([
|
||||
'slug' => $pluginSlug,
|
||||
'name' => $pluginName,
|
||||
'version' => $pluginVersion,
|
||||
'show' => 0,
|
||||
]);
|
||||
$this->info('成功');
|
||||
@@ -161,25 +175,4 @@ class Panel extends Command
|
||||
Plugin::query()->where('slug', $pluginSlug)->delete();
|
||||
$this->info('成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入插件更新状态
|
||||
* @return void
|
||||
*/
|
||||
private function writePluginUpdate(): void
|
||||
{
|
||||
$pluginSlug = $this->argument('a1');
|
||||
$pluginVersion = $this->argument('a2');
|
||||
|
||||
// 判空
|
||||
if (empty($pluginSlug) || empty($pluginVersion)) {
|
||||
$this->error('参数错误');
|
||||
return;
|
||||
}
|
||||
// 入库
|
||||
Plugin::query()->where('slug', $pluginSlug)->update([
|
||||
'version' => $pluginVersion,
|
||||
]);
|
||||
$this->info('成功');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
$schedule->command('monitor')->everyMinute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -241,7 +241,7 @@ class InfosController extends Controller
|
||||
*/
|
||||
public function getHomePlugins(): JsonResponse
|
||||
{
|
||||
$plugins = Plugin::where('show', 1)->get();
|
||||
$plugins = Plugin::query()->where('show', 1)->get();
|
||||
// 判空
|
||||
if ($plugins->isEmpty()) {
|
||||
$res['code'] = 0;
|
||||
@@ -310,17 +310,29 @@ class InfosController extends Controller
|
||||
/**
|
||||
* 获取已安装的数据库和PHP版本
|
||||
*/
|
||||
public function getInstalledDbAndPhp()
|
||||
public function getInstalledDbAndPhp(): JsonResponse
|
||||
{
|
||||
// 判断mysql插件目录是否存在
|
||||
if (is_dir('/www/panel/plugins/mysql')) {
|
||||
$mysql_version = 80;
|
||||
$dbVersions = [];
|
||||
// 判断mysql插件是否安装
|
||||
if (isset(PLUGINS['mysql'])) {
|
||||
$dbVersions['mysql'] = PLUGINS['mysql']['version'];
|
||||
} else {
|
||||
$mysql_version = false;
|
||||
$dbVersions['mysql'] = false;
|
||||
}
|
||||
/**
|
||||
* TODO: PostgreSQL版本
|
||||
*/
|
||||
// 判断postgresql插件是否安装
|
||||
if (isset(PLUGINS['postgresql15'])) {
|
||||
$dbVersions['postgresql15'] = PLUGINS['postgresql15']['version'];
|
||||
} else {
|
||||
$dbVersions['postgresql15'] = false;
|
||||
}
|
||||
// 循环获取已安装的PHP版本
|
||||
$php_versions = Plugin::query()->where('slug', 'like', 'php%')->get();
|
||||
$php_versions = $php_versions->toArray();
|
||||
$php_versions = array_column($php_versions, 'slug');
|
||||
$php_versions = array_map(function ($item) {
|
||||
return str_replace('php', '', $item);
|
||||
}, $php_versions);
|
||||
|
||||
$php_version = shell_exec('ls /www/server/php');
|
||||
$php_version = trim($php_version);
|
||||
|
||||
@@ -333,10 +345,7 @@ class InfosController extends Controller
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$res['data'] = array(
|
||||
'db_version' => [
|
||||
'mysql' => $mysql_version,
|
||||
'postgresql' => false
|
||||
],
|
||||
'db_version' => $dbVersions,
|
||||
'php_version' => $php_versions
|
||||
);
|
||||
return response()->json($res);
|
||||
|
||||
102
app/Http/Controllers/Api/MonitorsController.php
Normal file
102
app/Http/Controllers/Api/MonitorsController.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* 耗子Linux面板 - 监控控制器
|
||||
* @author 耗子
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Monitor;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class MonitorsController extends Controller
|
||||
{
|
||||
/**
|
||||
* 修改监控开关
|
||||
*/
|
||||
public function setMonitorSwitch(Request $request): JsonResponse
|
||||
{
|
||||
$switch = $request->input('switch');
|
||||
if ($switch) {
|
||||
$status = true;
|
||||
} else {
|
||||
$status = false;
|
||||
}
|
||||
Setting::query()->where('name', 'monitor')->update(['value' => $status]);
|
||||
return response()->json(['code' => 0, 'msg' => '修改成功']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存天数
|
||||
*/
|
||||
public function setMonitorSaveDays(Request $request): JsonResponse
|
||||
{
|
||||
$days = $request->input('days');
|
||||
Setting::query()->where('name', 'monitor_days')->update(['value' => $days]);
|
||||
return response()->json(['code' => 0, 'msg' => '修改成功']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空监控数据
|
||||
*/
|
||||
public function clearMonitorData(): JsonResponse
|
||||
{
|
||||
Monitor::query()->truncate();
|
||||
return response()->json(['code' => 0, 'msg' => '清空成功']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监控开关和保存天数
|
||||
*/
|
||||
public function getMonitorSwitchAndDays(): JsonResponse
|
||||
{
|
||||
$monitor = Setting::query()->where('name', 'monitor')->first();
|
||||
$monitor_days = Setting::query()->where('name', 'monitor_days')->first();
|
||||
return response()->json([
|
||||
'code' => 0, 'msg' => '获取成功',
|
||||
'data' => ['monitor' => $monitor->value, 'monitor_days' => $monitor_days->value]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监控数据
|
||||
*/
|
||||
public function getMonitorData(Request $request): JsonResponse
|
||||
{
|
||||
$start = $request->input('start') ?? now();
|
||||
$end = $request->input('end') ?? now();
|
||||
$start = Carbon::create($start)->startOfDay();
|
||||
$end = Carbon::create($end)->endOfDay();
|
||||
$data = Monitor::query()->where('created_at', '>=', $start)->where('created_at', '<=', $end)->get()->toArray();
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
if (empty($data)) {
|
||||
$res['data']['times'] = [];
|
||||
$res['data']['uptime'] = [];
|
||||
$res['data']['cpu']['use'] = [];
|
||||
$res['data']['memory']['mem_use'] = [];
|
||||
$res['data']['memory']['mem_use_p'] = [];
|
||||
$res['data']['memory']['swap_use'] = [];
|
||||
$res['data']['memory']['swap_use_p'] = [];
|
||||
$res['data']['network']['tx_now'] = [];
|
||||
$res['data']['network']['rx_now'] = [];
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
$info = json_decode($value['info'], true);
|
||||
$res['data']['times'][] = Carbon::create($value['created_at'])->tz(config('app.timezone', 'PRC'))->isoFormat('MM-DD HH:mm');
|
||||
$res['data']['uptime']['uptime'][] = round($info['uptime'], 2);
|
||||
$res['data']['cpu']['use'][] = round($info['cpu_use'], 2);
|
||||
$res['data']['memory']['mem_use'][] = round($info['mem_use'], 2);
|
||||
$res['data']['memory']['mem_use_p'][] = round($info['mem_use_p'], 2);
|
||||
$res['data']['memory']['swap_use'][] = round($info['swap_use'], 2);
|
||||
$res['data']['memory']['swap_use_p'][] = round($info['swap_use_p'], 2);
|
||||
$res['data']['network']['tx_now'][] = round($info['tx_now'] / 1024, 2);
|
||||
$res['data']['network']['rx_now'][] = round($info['rx_now'] / 1024, 2);
|
||||
}
|
||||
return response()->json($res);
|
||||
}
|
||||
}
|
||||
@@ -30,20 +30,20 @@ class PluginsController extends Controller
|
||||
$data['msg'] = 'success';
|
||||
$data['data'] = $this->pluginList(false);
|
||||
foreach ($data['data'] as $k => $v) {
|
||||
// 获取已装版本
|
||||
$installVersion = Plugin::query()->where('slug', $v['slug'])->first();
|
||||
// 判空
|
||||
if ($installVersion) {
|
||||
$data['data'][$k]['install_version'] = $installVersion->version;
|
||||
} else {
|
||||
$data['data'][$k]['install_version'] = '';
|
||||
}
|
||||
// 获取首页显示状态
|
||||
$shows = Plugin::query()->pluck('show', 'slug');
|
||||
// 如果本地已安装,则显示本地名称
|
||||
$data['data'][$k]['name'] = PLUGINS[$v['slug']]['name'] ?? $data['data'][$k]['name'];
|
||||
// 已装版本
|
||||
$data['data'][$k]['install_version'] = PLUGINS[$v['slug']]['version'] ?? '';
|
||||
// 首页显示
|
||||
$data['data'][$k]['show'] = $shows[$v['slug']] ?? 0;
|
||||
// 去除不需要的字段
|
||||
unset($data['data'][$k]['url']);
|
||||
unset($data['data'][$k]['install']);
|
||||
unset($data['data'][$k]['uninstall']);
|
||||
unset($data['data'][$k]['update']);
|
||||
if (!empty(Plugin::query()->where('slug', $v['slug'])->first())) {
|
||||
if (isset(PLUGINS[$v['slug']])) {
|
||||
$data['data'][$k]['control']['installed'] = true;
|
||||
$data['data'][$k]['control']['allow_uninstall'] = true;
|
||||
// 判断是否有更新
|
||||
@@ -109,7 +109,7 @@ class PluginsController extends Controller
|
||||
// 入库等待安装
|
||||
$task = new Task();
|
||||
$task->name = '安装' . $plugin_data['name'];
|
||||
$task->shell = $plugin_data['install_shell'];
|
||||
$task->shell = $plugin_data['install'];
|
||||
$task->status = 'waiting';
|
||||
$task->log = '/tmp/' . $plugin_data['slug'] . '.log';
|
||||
$task->save();
|
||||
@@ -124,7 +124,8 @@ class PluginsController extends Controller
|
||||
|
||||
/**
|
||||
* 卸载插件
|
||||
* @return
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function uninstall(Request $request): JsonResponse
|
||||
{
|
||||
@@ -162,12 +163,12 @@ class PluginsController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
// 判断插件是否未安装
|
||||
$installed = Task::query()->where('slug', $slug)->first();
|
||||
/*$installed = Task::query()->where('slug', $slug)->first();
|
||||
if (!$installed) {
|
||||
$data['code'] = 1;
|
||||
$data['msg'] = '请不要重复卸载!';
|
||||
$data['msg'] = '插件未安装,无需卸载!';
|
||||
return response()->json($data);
|
||||
}
|
||||
}*/
|
||||
|
||||
// 判断是否是操作openresty
|
||||
if ($slug == 'openresty') {
|
||||
@@ -179,7 +180,7 @@ class PluginsController extends Controller
|
||||
// 入库等待卸载
|
||||
$task = new Task();
|
||||
$task->name = '卸载' . $plugin_data['name'];
|
||||
$task->shell = $plugin_data['uninstall_shell'];
|
||||
$task->shell = $plugin_data['uninstall'];
|
||||
$task->status = 'waiting';
|
||||
$task->log = '/tmp/' . $plugin_data['slug'] . '.log';
|
||||
$task->save();
|
||||
|
||||
248
app/Http/Controllers/Api/SafesController.php
Normal file
248
app/Http/Controllers/Api/SafesController.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* 耗子Linux面板 - 安全控制器
|
||||
* @author 耗子
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SafesController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取防火墙状态
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getFirewallStatus(): JsonResponse
|
||||
{
|
||||
$firewallStatus = trim(shell_exec("systemctl status firewalld | grep Active | awk '{print $3}'"));
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
if ($firewallStatus == '(running)') {
|
||||
$res['data'] = 1;
|
||||
} else {
|
||||
$res['data'] = 0;
|
||||
}
|
||||
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置防火墙状态
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function setFirewallStatus(Request $request): JsonResponse
|
||||
{
|
||||
$status = $request->input('status');
|
||||
if ($status) {
|
||||
shell_exec("systemctl enable firewalld");
|
||||
shell_exec("systemctl start firewalld");
|
||||
} else {
|
||||
shell_exec("systemctl stop firewalld");
|
||||
shell_exec("systemctl disable firewalld");
|
||||
}
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SSH状态
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getSshStatus(): JsonResponse
|
||||
{
|
||||
$sshStatus = trim(shell_exec("systemctl status sshd | grep Active | awk '{print $3}'"));
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
if ($sshStatus == '(running)') {
|
||||
$res['data'] = 1;
|
||||
} else {
|
||||
$res['data'] = 0;
|
||||
}
|
||||
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SSH状态
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function setSshStatus(Request $request): JsonResponse
|
||||
{
|
||||
$status = $request->input('status');
|
||||
if ($status) {
|
||||
shell_exec("systemctl enable sshd");
|
||||
shell_exec("systemctl start sshd");
|
||||
} else {
|
||||
shell_exec("systemctl stop sshd");
|
||||
shell_exec("systemctl disable sshd");
|
||||
}
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SSH端口
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getSshPort(): JsonResponse
|
||||
{
|
||||
$sshPort = trim(shell_exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'"));
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$res['data'] = $sshPort;
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SSH端口
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function setSshPort(Request $request): JsonResponse
|
||||
{
|
||||
$port = $request->input('port');
|
||||
$oldPort = trim(shell_exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'"));
|
||||
shell_exec("sed -i 's/#Port ".$oldPort."/Port ".$port."/g' /etc/ssh/sshd_config");
|
||||
shell_exec("sed -i 's/Port ".$oldPort."/Port ".$port."/g' /etc/ssh/sshd_config");
|
||||
// 判断ssh是否开启
|
||||
$sshStatus = trim(shell_exec("systemctl status sshd | grep Active | awk '{print $3}'"));
|
||||
if ($sshStatus == '(running)') {
|
||||
shell_exec("systemctl restart sshd");
|
||||
}
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ping状态
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getPingStatus(): JsonResponse
|
||||
{
|
||||
$pingStatus = trim(shell_exec("cat /etc/sysctl.conf | grep 'net.ipv4.icmp_echo_ignore_all = 1'"));
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
if ($pingStatus && !str_starts_with($pingStatus, '#')) {
|
||||
$res['data'] = 0;
|
||||
} else {
|
||||
$res['data'] = 1;
|
||||
}
|
||||
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ping状态
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function setPingStatus(Request $request): JsonResponse
|
||||
{
|
||||
$status = $request->input('status');
|
||||
shell_exec("sed -i '/net.ipv4.icmp_echo_ignore_all/d' /etc/sysctl.conf");
|
||||
if (!$status) {
|
||||
// 禁止ping
|
||||
shell_exec("echo 'net.ipv4.icmp_echo_ignore_all = 1' >> /etc/sysctl.conf");
|
||||
}
|
||||
shell_exec("sysctl -p");
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取防火墙规则
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getFirewallRules(): JsonResponse
|
||||
{
|
||||
$firewallRules = trim(shell_exec("firewall-cmd --list-all 2>&1"));
|
||||
// 判断是否开启
|
||||
if (str_contains($firewallRules, 'not running')) {
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$res['data'] = [];
|
||||
return response()->json($res);
|
||||
}
|
||||
// 正则匹配出ports
|
||||
preg_match('/ports: (.*)/', $firewallRules, $matches);
|
||||
$rawPorts = $matches[1];
|
||||
// 22/tcp 80/tcp 443/tcp 8888/tcp 5432/tcp
|
||||
$ports = explode(' ', $rawPorts);
|
||||
// 对ports进行分割为port=>protocol形式
|
||||
$rules = [];
|
||||
foreach ($ports as $port) {
|
||||
$rule = explode('/', $port);
|
||||
$rules[] = [
|
||||
'port' => $rule[0],
|
||||
'protocol' => $rule[1],
|
||||
];
|
||||
}
|
||||
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$res['data'] = $rules;
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加防火墙规则
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function addFirewallRule(Request $request): JsonResponse
|
||||
{
|
||||
$port = $request->input('port');
|
||||
$protocol = $request->input('protocol');
|
||||
// 判断是否开启
|
||||
$firewallStatus = trim(shell_exec("firewall-cmd --state 2>&1"));
|
||||
if ($firewallStatus != 'running') {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '防火墙未开启';
|
||||
return response()->json($res);
|
||||
}
|
||||
// 清空当前规则
|
||||
shell_exec("firewall-cmd --remove-port=".$port."/".$protocol." --permanent");
|
||||
// 添加新的防火墙规则
|
||||
shell_exec("firewall-cmd --add-port=".$port."/".$protocol." --permanent");
|
||||
// 重启防火墙
|
||||
shell_exec("firewall-cmd --reload");
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除防火墙规则
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function deleteFirewallRule(Request $request): JsonResponse
|
||||
{
|
||||
$port = $request->input('port');
|
||||
$protocol = $request->input('protocol');
|
||||
// 判断是否开启
|
||||
$firewallStatus = trim(shell_exec("firewall-cmd --state 2>&1"));
|
||||
if ($firewallStatus != 'running') {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '防火墙未开启';
|
||||
return response()->json($res);
|
||||
}
|
||||
// 清空当前规则
|
||||
shell_exec("firewall-cmd --remove-port=".$port."/".$protocol." --permanent");
|
||||
// 重启防火墙
|
||||
shell_exec("firewall-cmd --reload");
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
}
|
||||
@@ -3,69 +3,64 @@
|
||||
* 耗子Linux面板 - 设置控制器
|
||||
* @author 耗子
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取面板设置
|
||||
* @return
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function get_settings(Request $request)
|
||||
public function get(Request $request)
|
||||
{
|
||||
$settings = Db::table('setting')->select()->toArray();
|
||||
$settings = Setting::query()->get()->toArray();
|
||||
foreach ($settings as $setting) {
|
||||
$res['data'][$setting['name']] = $setting['value'];
|
||||
}
|
||||
$user_password = Db::table('user')->where('username', $request->username)->value('password');
|
||||
$res['data']['username'] = $request->username;
|
||||
$res['data']['password'] = $user_password;
|
||||
|
||||
if (!empty($settings)) {
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
} else {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '面板设置获取失败';
|
||||
$res['data'] = null;
|
||||
return response()->json($res);
|
||||
}
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存面板设置
|
||||
* @return
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function save_settings(Request $request)
|
||||
public function save(Request $request): JsonResponse
|
||||
{
|
||||
// 获取前端传递过来的数据
|
||||
$settings = Request::post();
|
||||
$settings = $request->all();
|
||||
// 将数据入库
|
||||
foreach ($settings as $key => $value) {
|
||||
if ($key == 'access_token' || $key == 'username' || $key == 'password') {
|
||||
continue;
|
||||
}
|
||||
if ($key == 'mysql_root_password') {
|
||||
$old_mysql_password = Db::table('setting')->where('name', 'mysql_root_password')->value('value');
|
||||
$old_mysql_password = Setting::query()->where('name', 'mysql_root_password')->value('value');
|
||||
if ($old_mysql_password != $value) {
|
||||
shell_exec('/www/server/mysql/bin/mysqladmin -uroot -p' . $old_mysql_password . ' password ' . $value);
|
||||
shell_exec('mysql -uroot -p'.$old_mysql_password.' -e "ALTER USER \'root\'@\'localhost\' IDENTIFIED BY \''.$value.'\';"');
|
||||
shell_exec('mysql -uroot -p'.$old_mysql_password.' -e "flush privileges;"');
|
||||
}
|
||||
}
|
||||
Db::table('setting')->where('name', $key)->update(['value' => $value]);
|
||||
Setting::query()->where('name', $key)->update(['value' => $value]);
|
||||
}
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$old_user_info = Db::table('user')->where('username', $request->username)->select()->toArray();
|
||||
|
||||
if ($old_user_info[0]['username'] != $settings['username'] || $old_user_info[0]['password'] != $settings['password']) {
|
||||
$res['msg'] = 'change';
|
||||
Db::table('user')->where('username', $request->username)->update(['username' => $settings['username']]);
|
||||
Db::table('user')->where('username', $settings['username'])->update(['password' => $settings['password']]);
|
||||
}
|
||||
return response()->json($res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,6 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
/*$user = User::create([
|
||||
'id' => '',
|
||||
'username' => 'haozi',
|
||||
'password' => Hash::make('haozi'),
|
||||
]);
|
||||
return response()->json([
|
||||
'code' => 200,
|
||||
'message' => '注册成功',
|
||||
'data' => $user,
|
||||
]);*/
|
||||
// 消毒数据
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
@@ -38,7 +28,8 @@ class UsersController extends Controller
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json([
|
||||
'message' => '参数错误',
|
||||
'code' => 1,
|
||||
'msg' => '参数错误',
|
||||
'errors' => $e->errors()
|
||||
], 422);
|
||||
}
|
||||
@@ -56,7 +47,7 @@ class UsersController extends Controller
|
||||
$user = $request->user();
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
$res['data']['username'] = 'haozi';
|
||||
$res['data']['username'] = $user->username;
|
||||
return response()->json($res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* 耗子Linux面板 - 网站控制器
|
||||
* @author 耗子
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -11,6 +12,7 @@ use App\Models\Setting;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class WebsitesController extends Controller
|
||||
@@ -53,20 +55,21 @@ class WebsitesController extends Controller
|
||||
'php' => 'required|integer',
|
||||
'note' => 'string|nullable|max:255',
|
||||
'db' => 'required|boolean',
|
||||
'db_type' => 'required_if:db,true|string|max:10',
|
||||
'db_name' => 'required_if:db,true|string|max:255',
|
||||
'db_username' => 'required_if:db,true|string|max:255',
|
||||
'db_password' => 'required_if:db,true|string|max:255',
|
||||
'db_type' => 'required_if:db,true|max:10',
|
||||
'db_name' => 'required_if:db,true|max:255',
|
||||
'db_username' => 'required_if:db,true|max:255',
|
||||
'db_password' => 'required_if:db,true|max:255',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json([
|
||||
'message' => '参数错误',
|
||||
'code' => 1,
|
||||
'msg' => '参数错误',
|
||||
'errors' => $e->errors()
|
||||
], 422);
|
||||
], 200);
|
||||
}
|
||||
// path为空时,设置默认值
|
||||
if (empty($credentials['path'])) {
|
||||
$credentials['path'] = '/www/wwwroot/' . $credentials['name'];
|
||||
$credentials['path'] = '/www/wwwroot/'.$credentials['name'];
|
||||
}
|
||||
// ssl默认设置为0
|
||||
$credentials['ssl'] = 0;
|
||||
@@ -76,9 +79,9 @@ class WebsitesController extends Controller
|
||||
// 入库
|
||||
Website::query()->create($credentials);
|
||||
// 创建网站目录
|
||||
shell_exec("mkdir -p " . $credentials['path']);
|
||||
shell_exec("mkdir -p ".$credentials['path']);
|
||||
// 创建index.html
|
||||
shell_exec("touch " . $credentials['path'] . "/index.html");
|
||||
shell_exec("touch ".$credentials['path']."/index.html");
|
||||
// 写入到index.html
|
||||
$index_html = <<<EOF
|
||||
<!DOCTYPE html>
|
||||
@@ -95,7 +98,7 @@ class WebsitesController extends Controller
|
||||
</html>
|
||||
|
||||
EOF;
|
||||
file_put_contents($credentials['path'] . "/index.html", $index_html);
|
||||
file_put_contents($credentials['path']."/index.html", $index_html);
|
||||
|
||||
// 创建nginx配置
|
||||
$port_list = "";
|
||||
@@ -103,18 +106,18 @@ EOF;
|
||||
$domain_arr = explode(PHP_EOL, $domain);
|
||||
foreach ($domain_arr as $key => $value) {
|
||||
$temp = explode(":", $value);
|
||||
$domain_list .= " " . $temp[0];
|
||||
$domain_list .= " ".$temp[0];
|
||||
if (!isset($temp[1])) {
|
||||
if ($key == count($domain_arr) - 1) {
|
||||
$port_list .= " listen 80;";
|
||||
} else {
|
||||
$port_list .= " listen 80;" . PHP_EOL;
|
||||
$port_list .= " listen 80;".PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
if ($key == count($domain_arr) - 1) {
|
||||
$port_list .= " listen " . $temp[1] . ";";
|
||||
$port_list .= " listen ".$temp[1].";";
|
||||
} else {
|
||||
$port_list .= " listen " . $temp[1] . ";" . PHP_EOL;
|
||||
$port_list .= " listen ".$temp[1].";".PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +146,7 @@ $port_list
|
||||
# php标记位开始
|
||||
include enable-php-$credentials[php].conf;
|
||||
# php标记位结束
|
||||
|
||||
|
||||
# waf标记位开始
|
||||
waf on;
|
||||
waf_rule_path /www/server/nginx/ngx_waf/assets/rules/;
|
||||
@@ -176,20 +179,20 @@ $port_list
|
||||
}
|
||||
EOF;
|
||||
// 写入nginx配置
|
||||
file_put_contents('/www/server/vhost/' . $credentials['name'] . '.conf', $nginx_config);
|
||||
shell_exec('echo "" > /www/server/vhost/rewrite/' . $credentials['name'] . '.conf');
|
||||
shell_exec('echo "" > /www/server/vhost/ssl/' . $credentials['name'] . '.pem');
|
||||
shell_exec('echo "" > /www/server/vhost/ssl/' . $credentials['name'] . '.key');
|
||||
file_put_contents('/www/server/vhost/'.$credentials['name'].'.conf', $nginx_config);
|
||||
shell_exec('echo "" > /www/server/vhost/rewrite/'.$credentials['name'].'.conf');
|
||||
shell_exec('echo "" > /www/server/vhost/ssl/'.$credentials['name'].'.pem');
|
||||
shell_exec('echo "" > /www/server/vhost/ssl/'.$credentials['name'].'.key');
|
||||
shell_exec("systemctl reload nginx");
|
||||
|
||||
// 创建数据库
|
||||
if ($credentials['db']) {
|
||||
if ($credentials['db_type'] == 'mysql') {
|
||||
$password = Setting::query()->where('name', 'mysql_root_password')->value('value');
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p" . $password . " -e \"CREATE DATABASE IF NOT EXISTS " . $credentials['db_name'] . " DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;\" 2>&1");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p" . $password . " -e \"CREATE USER '" . $credentials['db_username'] . "'@'localhost' IDENTIFIED BY '" . $credentials['db_password'] . "';\"");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p" . $password . " -e \"GRANT ALL PRIVILEGES ON " . $credentials['db_name'] . ".* TO '" . $credentials['db_username'] . "'@'localhost';\"");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p" . $password . " -e \"flush privileges;\"");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p".$password." -e \"CREATE DATABASE IF NOT EXISTS ".$credentials['db_name']." DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;\" 2>&1");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p".$password." -e \"CREATE USER '".$credentials['db_username']."'@'localhost' IDENTIFIED BY '".$credentials['db_password']."';\"");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p".$password." -e \"GRANT ALL PRIVILEGES ON ".$credentials['db_name'].".* TO '".$credentials['db_username']."'@'localhost';\"");
|
||||
shell_exec("/www/server/mysql/bin/mysql -u root -p".$password." -e \"flush privileges;\"");
|
||||
}
|
||||
}
|
||||
$res['code'] = 0;
|
||||
@@ -251,7 +254,7 @@ EOF;
|
||||
$name = $request->input('name');
|
||||
$website = Website::query()->where('name', $name)->first();
|
||||
// 通过name读取相应的nginx配置
|
||||
$nginx_config = file_get_contents('/www/server/vhost/' . $name . '.conf');
|
||||
$nginx_config = file_get_contents('/www/server/vhost/'.$name.'.conf');
|
||||
// 从nginx配置中port标记位提取全部端口
|
||||
$port_raw = $this->cut('# port标记位开始', '# port标记位结束', $nginx_config);
|
||||
preg_match_all('/listen\s+(.*);/', $port_raw, $matches);
|
||||
@@ -259,7 +262,7 @@ EOF;
|
||||
if ($k == 0) {
|
||||
$website['port'] = $v;
|
||||
} else {
|
||||
$website['port'] .= PHP_EOL . $v;
|
||||
$website['port'] .= PHP_EOL.$v;
|
||||
}
|
||||
}
|
||||
// 从nginx配置中server_name标记位提取全部域名
|
||||
@@ -270,18 +273,30 @@ EOF;
|
||||
if ($k == 0) {
|
||||
$website['domain'] = $v;
|
||||
} else {
|
||||
$website['domain'] .= PHP_EOL . $v;
|
||||
$website['domain'] .= PHP_EOL.$v;
|
||||
}
|
||||
}
|
||||
// 从nginx配置中root标记位提取全部根目录
|
||||
$root_raw = $this->cut('# root标记位开始', '# root标记位结束', $nginx_config);
|
||||
preg_match_all('/root\s+(.+);/', $root_raw, $matches2);
|
||||
$website['root'] = $matches2[1][0];
|
||||
$website['path'] = $matches2[1][0];
|
||||
// 从nginx配置中index标记位提取全部默认文件
|
||||
$index_raw = $this->cut('# index标记位开始', '# index标记位结束', $nginx_config);
|
||||
preg_match_all('/index\s+(.+);/', $index_raw, $matches3);
|
||||
$website['index'] = $matches3[1][0];
|
||||
|
||||
// 检查网站目录下是否存在.user.ini文件且设置了open_basedir
|
||||
if (file_exists($website['path'].'/.user.ini')) {
|
||||
$user_ini = file_get_contents($website['path'].'/.user.ini');
|
||||
if (str_contains($user_ini, 'open_basedir')) {
|
||||
$website['open_basedir'] = 1;
|
||||
} else {
|
||||
$website['open_basedir'] = 0;
|
||||
}
|
||||
} else {
|
||||
$website['open_basedir'] = 0;
|
||||
}
|
||||
|
||||
if ($website['ssl'] == '1') {
|
||||
$ssl_certificate_raw = $this->cut('# ssl标记位开始', '# ssl标记位结束', $nginx_config);
|
||||
// 从nginx配置中ssl_certificate标记位提取全部证书路径
|
||||
@@ -291,15 +306,36 @@ EOF;
|
||||
preg_match_all('/ssl_certificate_key\s+(.+);/', $ssl_certificate_raw, $matches5);
|
||||
$website['ssl_certificate_key'] = file_get_contents($matches5[1][0]);
|
||||
$website['http_redirect'] = str_contains($nginx_config, '# http重定向标记位');
|
||||
$website['hsts'] = str_contains($nginx_config, '# hsts标记位');
|
||||
} else {
|
||||
$website['ssl_certificate'] = @file_get_contents('/www/server/vhost/ssl/'.$name.'.pem');
|
||||
$website['ssl_certificate_key'] = @file_get_contents('/www/server/vhost/ssl/'.$name.'.key');
|
||||
$website['http_redirect'] = 0;
|
||||
$website['hsts'] = 0;
|
||||
}
|
||||
|
||||
// 从nginx配置中ssl标记位提取waf配置
|
||||
$waf_raw = $this->cut('# waf标记位开始', '# waf标记位结束', $nginx_config);
|
||||
if (str_contains($waf_raw, 'waf on;')) {
|
||||
$website['waf'] = 1;
|
||||
} else {
|
||||
$website['waf'] = 0;
|
||||
}
|
||||
preg_match_all('/waf_mode\s+(.+);/', $waf_raw, $matches6);
|
||||
$website['waf_mode'] = $matches6[1][0];
|
||||
preg_match_all('/waf_cc_deny\s+(.+);/', $waf_raw, $matches7);
|
||||
$website['waf_cc_deny'] = $matches7[1][0];
|
||||
preg_match_all('/waf_cache\s+(.+);/', $waf_raw, $matches8);
|
||||
$website['waf_cache'] = $matches8[1][0];
|
||||
|
||||
// 读取伪静态文件的内容
|
||||
$website['rewrite'] = file_get_contents('/www/server/vhost/rewrite/' . $name . '.conf');
|
||||
$website['rewrite'] = file_get_contents('/www/server/vhost/rewrite/'.$name.'.conf');
|
||||
|
||||
// 读取配置原文
|
||||
$website['config_raw'] = file_get_contents('/www/server/vhost/' . $name . '.conf');
|
||||
$website['config_raw'] = file_get_contents('/www/server/vhost/'.$name.'.conf');
|
||||
|
||||
// 读取访问日志
|
||||
$website['log'] = shell_exec('tail -n 100 /www/wwwlogs/' . $name . '.log');
|
||||
$website['log'] = shell_exec('tail -n 100 /www/wwwlogs/'.$name.'.log');
|
||||
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
@@ -315,15 +351,23 @@ EOF;
|
||||
public function saveSiteSettings(Request $request): JsonResponse
|
||||
{
|
||||
// 获取前端传递过来的数据
|
||||
$name = $request->input('name');
|
||||
$config = $request->input('config');
|
||||
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
|
||||
// 如果config_raw与本地配置文件不一致,则更新配置文件,然后返回
|
||||
$config_raw = shell_exec('cat /www/server/vhost/' . $config['name'] . '.conf');
|
||||
if (trim($config_raw) != trim($config['config_raw'])) {
|
||||
file_put_contents('/www/server/vhost/' . $config['name'] . '.conf', $config['config_raw']);
|
||||
// 如果config_raw与本地配置文件不一致,则更新配置文件,然后直接返回
|
||||
$configRaw = shell_exec('cat /www/server/vhost/'.$name.'.conf');
|
||||
if (trim($configRaw) != trim($config['config_raw'])) {
|
||||
file_put_contents('/www/server/vhost/'.$name.'.conf', $config['config_raw']);
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
// 检查网站目录是否存在
|
||||
if (!is_dir($config['path'])) {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '网站目录不存在';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
@@ -331,40 +375,112 @@ EOF;
|
||||
$domain = "server_name";
|
||||
$domain_arr = explode(PHP_EOL, $config['domain']);
|
||||
foreach ($domain_arr as $v) {
|
||||
$domain .= " " . $v;
|
||||
$domain .= " ".$v;
|
||||
}
|
||||
$domain .= ';';
|
||||
|
||||
$domain_config_old = $this->cut('# server_name标记位开始', '# server_name标记位结束', $config_raw);
|
||||
$domain_config_old = $this->cut('# server_name标记位开始', '# server_name标记位结束', $configRaw);
|
||||
if (!empty(trim($domain_config_old)) && $domain_config_old != PHP_EOL) {
|
||||
$config_raw = str_replace($domain_config_old, PHP_EOL . " " . $domain . PHP_EOL . ' ', $config_raw);
|
||||
$configRaw = str_replace($domain_config_old, PHP_EOL." ".$domain.PHP_EOL.' ', $configRaw);
|
||||
}
|
||||
|
||||
// 端口
|
||||
$port = "";
|
||||
$port_arr = explode(PHP_EOL, $config['port']);
|
||||
foreach ($port_arr as $k => $v) {
|
||||
if ($k != count($port_arr) - 1) {
|
||||
$port .= " listen " . $v . ';' . PHP_EOL;
|
||||
$portArr = explode(PHP_EOL, $config['port']);
|
||||
foreach ($portArr as $k => $v) {
|
||||
// 检查端口是否均为数字
|
||||
if (!is_numeric($v) && $v != '443 ssl http2') {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '端口必须为数字';
|
||||
return response()->json($res);
|
||||
}
|
||||
// 检查是否443端口
|
||||
if ($v == '443' && $config['ssl'] == '1') {
|
||||
$v = '443 ssl http2';
|
||||
}
|
||||
if ($k != count($portArr) - 1) {
|
||||
$port .= " listen ".$v.';'.PHP_EOL;
|
||||
} else {
|
||||
$port .= " listen " . $v . ';';
|
||||
$port .= " listen ".$v.';';
|
||||
}
|
||||
}
|
||||
$port_config_old = $this->cut('# port标记位开始', '# port标记位结束', $config_raw);
|
||||
$port_config_old = $this->cut('# port标记位开始', '# port标记位结束', $configRaw);
|
||||
if (!empty(trim($port_config_old)) && $port_config_old != PHP_EOL) {
|
||||
$config_raw = str_replace($port_config_old, PHP_EOL . $port . PHP_EOL . ' ', $config_raw);
|
||||
$configRaw = str_replace($port_config_old, PHP_EOL.$port.PHP_EOL.' ', $configRaw);
|
||||
}
|
||||
|
||||
// 网站目录
|
||||
$pathConfig = $this->cut('# root标记位开始', '# root标记位结束', $configRaw);
|
||||
preg_match_all('/root\s+(.+);/', $pathConfig, $matches1);
|
||||
$pathConfigOld = $matches1[1][0];
|
||||
if (!empty(trim($pathConfigOld)) && $pathConfigOld != PHP_EOL) {
|
||||
$pathConfigNew = str_replace($pathConfigOld, $config['path'], $pathConfig);
|
||||
$configRaw = str_replace($pathConfig, $pathConfigNew, $configRaw);
|
||||
}
|
||||
|
||||
// 如果开启ssl,则更新nginx配置文件
|
||||
// 默认文件
|
||||
$indexConfig = $this->cut('# index标记位开始', '# index标记位结束', $configRaw);
|
||||
preg_match_all('/index\s+(.+);/', $indexConfig, $matches2);
|
||||
$indexConfigOld = $matches2[1][0];
|
||||
if (!empty(trim($indexConfigOld)) && $indexConfigOld != PHP_EOL) {
|
||||
$indexConfigNew = str_replace($indexConfigOld, $config['index'], $indexConfig);
|
||||
$configRaw = str_replace($indexConfig, $indexConfigNew, $configRaw);
|
||||
}
|
||||
|
||||
// open_basedir
|
||||
if ($config['open_basedir'] == 1) {
|
||||
// 判断$config['path']是否为'/'结尾
|
||||
if (str_ends_with($config['path'], '/')) {
|
||||
$open_basedir = "open_basedir=".$config['path'].":/tmp/";
|
||||
} else {
|
||||
$open_basedir = "open_basedir=".$config['path']."/:/tmp/";
|
||||
}
|
||||
// 写入open_basedir配置到.user.ini文件
|
||||
if (is_dir($config['path'])) {
|
||||
file_put_contents($config['path'].'/.user.ini', $open_basedir);
|
||||
// 为.user.ini文件添加i权限
|
||||
shell_exec('chattr +i '.$config['path'].'/.user.ini');
|
||||
}
|
||||
} else {
|
||||
// 移除.user.ini文件的i权限
|
||||
shell_exec('chattr -i '.$config['path'].'/.user.ini');
|
||||
// 删除.user.ini文件
|
||||
if (file_exists($config['path'].'/.user.ini')) {
|
||||
unlink($config['path'].'/.user.ini');
|
||||
}
|
||||
}
|
||||
|
||||
// waf
|
||||
$waf = $config['waf'] == 1 ? 'on' : 'off';
|
||||
$wafMode = empty($config['waf_mode']) ? 'DYNAMIC' : $config['waf_mode'];
|
||||
$wafCcDeny = empty($config['waf_cc_deny']) ? 'rate=1000r/m duration=60m' : $config['waf_cc_deny'];
|
||||
$wafCache = empty($config['waf_cache']) ? 'capacity=50' : $config['waf_cache'];
|
||||
|
||||
$wafConfig = <<<EOF
|
||||
# waf标记位开始
|
||||
waf $waf;
|
||||
waf_rule_path /www/server/nginx/ngx_waf/assets/rules/;
|
||||
waf_mode $wafMode;
|
||||
waf_cc_deny $wafCcDeny;
|
||||
waf_cache $wafCache;
|
||||
EOF;
|
||||
$wafConfig .= PHP_EOL.' ';
|
||||
$wafConfigOld = $this->cut('# waf标记位开始', '# waf标记位结束', $configRaw);
|
||||
if (!empty(trim($wafConfigOld)) && $wafConfigOld != PHP_EOL) {
|
||||
$configRawClean = str_replace($wafConfigOld, "", $configRaw);
|
||||
} else {
|
||||
$configRawClean = $configRaw;
|
||||
}
|
||||
$configRaw = str_replace('# waf标记位开始', $wafConfig, $configRawClean);
|
||||
|
||||
// ssl
|
||||
if ($config['ssl'] == '1') {
|
||||
// 写入证书
|
||||
file_put_contents("/www/server/vhost/ssl/" . $config['name'] . '.pem', $config['ssl_certificate']);
|
||||
file_put_contents("/www/server/vhost/ssl/" . $config['name'] . '.key', $config['ssl_certificate_key']);
|
||||
file_put_contents("/www/server/vhost/ssl/".$name.'.pem', $config['ssl_certificate']);
|
||||
file_put_contents("/www/server/vhost/ssl/".$name.'.key', $config['ssl_certificate_key']);
|
||||
$ssl_config = <<<EOF
|
||||
# ssl标记位开始
|
||||
ssl_certificate /www/server/vhost/ssl/$config[name].pem;
|
||||
ssl_certificate_key /www/server/vhost/ssl/$config[name].key;
|
||||
ssl_certificate /www/server/vhost/ssl/$name.pem;
|
||||
ssl_certificate_key /www/server/vhost/ssl/$name.key;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_tickets off;
|
||||
@@ -380,52 +496,55 @@ EOF;
|
||||
if (\$server_port !~ 443){
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
if (\$server_port ~ 443){
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
}
|
||||
error_page 497 https://\$host\$request_uri;
|
||||
# http重定向标记位结束
|
||||
EOF;
|
||||
}
|
||||
$ssl_config .= PHP_EOL . ' ';
|
||||
|
||||
|
||||
$ssl_config_old = $this->cut('# ssl标记位开始', '# ssl标记位结束', $config_raw);
|
||||
if (!empty(trim($ssl_config_old)) && $ssl_config_old != PHP_EOL) {
|
||||
$config_raw_clean = str_replace($ssl_config_old, "", $config_raw);
|
||||
} else {
|
||||
$config_raw_clean = $config_raw;
|
||||
if ($config['hsts'] == '1') {
|
||||
$ssl_config .= PHP_EOL;
|
||||
$ssl_config .= <<<EOF
|
||||
# hsts标记位开始
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
# hsts标记位结束
|
||||
EOF;
|
||||
}
|
||||
$config_raw = str_replace('# ssl标记位开始', $ssl_config, $config_raw_clean);
|
||||
$ssl_config .= PHP_EOL.' ';
|
||||
$ssl_config_old = $this->cut('# ssl标记位开始', '# ssl标记位结束', $configRaw);
|
||||
if (!empty(trim($ssl_config_old)) && $ssl_config_old != PHP_EOL) {
|
||||
$configRaw_clean = str_replace($ssl_config_old, "", $configRaw);
|
||||
} else {
|
||||
$configRaw_clean = $configRaw;
|
||||
}
|
||||
$configRaw = str_replace('# ssl标记位开始', $ssl_config, $configRaw_clean);
|
||||
|
||||
} else {
|
||||
// 更新nginx配置文件
|
||||
$ssl_config_old = $this->cut('# ssl标记位开始', '# ssl标记位结束', $config_raw);
|
||||
$ssl_config_old = $this->cut('# ssl标记位开始', '# ssl标记位结束', $configRaw);
|
||||
if (!empty(trim($ssl_config_old)) && $ssl_config_old != PHP_EOL) {
|
||||
$config_raw = str_replace($ssl_config_old, PHP_EOL . ' ', $config_raw);
|
||||
$configRaw = str_replace($ssl_config_old, PHP_EOL.' ', $configRaw);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果PHP版本不一致,则更新PHP版本
|
||||
$php_old = Website::query()->where('name', $config['name'])->value('php');
|
||||
$php_old = Website::query()->where('name', $name)->value('php');
|
||||
if ($config['php'] != $php_old) {
|
||||
$php_config_old = $this->cut('# php标记位开始', '# php标记位结束', $config_raw);
|
||||
$php_config_old = $this->cut('# php标记位开始', '# php标记位结束', $configRaw);
|
||||
$php_config_new = PHP_EOL;
|
||||
$php_config_new .= <<<EOL
|
||||
include enable-php-$config[php].conf;
|
||||
EOL;
|
||||
$php_config_new .= PHP_EOL . ' ';
|
||||
$php_config_new .= PHP_EOL.' ';
|
||||
|
||||
if (!empty(trim($php_config_old)) && $php_config_old != PHP_EOL) {
|
||||
$config_raw = str_replace($php_config_old, $php_config_new, $config_raw);
|
||||
$configRaw = str_replace($php_config_old, $php_config_new, $configRaw);
|
||||
}
|
||||
}
|
||||
|
||||
// 将数据入库
|
||||
Website::query()->where('name', $config['name'])->update(['php' => $config['php']]);
|
||||
Website::query()->where('name', $config['name'])->update(['ssl' => $config['ssl']]);
|
||||
file_put_contents('/www/server/vhost/' . $config['name'] . '.conf', $config_raw);
|
||||
file_put_contents('/www/server/vhost/rewrite/' . $config['name'] . '.conf', $config['rewrite_raw']);
|
||||
Website::query()->where('name', $name)->update(['php' => $config['php']]);
|
||||
Website::query()->where('name', $name)->update(['ssl' => $config['ssl']]);
|
||||
file_put_contents('/www/server/vhost/'.$name.'.conf', $configRaw);
|
||||
file_put_contents('/www/server/vhost/rewrite/'.$name.'.conf', $config['rewrite']);
|
||||
shell_exec('systemctl reload nginx');
|
||||
return response()->json($res);
|
||||
}
|
||||
@@ -435,15 +554,29 @@ EOL;
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function cleanSiteLog(Request $request): JsonResponse
|
||||
public function clearSiteLog(Request $request): JsonResponse
|
||||
{
|
||||
$name = $request->input('name');
|
||||
shell_exec('echo "" > /www/wwwlogs/' . $name . '.log');
|
||||
shell_exec('echo "" > /www/wwwlogs/'.$name.'.log');
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改网站备注
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function updateSiteNote(Request $request): JsonResponse
|
||||
{
|
||||
$name = $request->input('name');
|
||||
$note = $request->input('note');
|
||||
Website::query()->where('name', $name)->update(['note' => $note]);
|
||||
$res['code'] = 0;
|
||||
$res['msg'] = 'success';
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
// 裁剪字符串
|
||||
private function cut($begin, $end, $str): string
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ACLRepository;
|
||||
|
||||
class FilesACLRepository implements ACLRepository
|
||||
{
|
||||
/**
|
||||
* Get user ID
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserID()
|
||||
{
|
||||
return auth('sanctum')->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ACL rules list for user
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRules(): array
|
||||
{
|
||||
if (auth('sanctum')->check()) {
|
||||
return [
|
||||
['disk' => 'www', 'path' => '*', 'access' => 2],
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
['disk' => 'www', 'path' => '*', 'access' => 0],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,6 @@ use Illuminate\Http\Response;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*/
|
||||
public function handle($request, Closure $next, ...$guards)
|
||||
{
|
||||
/* // 获取请求头中的token
|
||||
$token = $request->header('access_token') ?? $request->input('access_token');
|
||||
// 将token放入请求中
|
||||
$request->headers->set('Authorization', 'Bearer ' . $token);
|
||||
// 验证token
|
||||
$this->authenticate($request, $guards);*/
|
||||
// 验证通过
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*
|
||||
@@ -36,7 +21,7 @@ class Authenticate extends Middleware
|
||||
{
|
||||
abort(response()->json([
|
||||
'code' => 1001,
|
||||
'message' => '登录状态失效'
|
||||
'msg' => '登录状态失效'
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ class ProcessShell implements ShouldQueue, ShouldBeUnique
|
||||
public function handle(): void
|
||||
{
|
||||
// 查询任务
|
||||
$task = Task::query()->where('id', $this->task_id)->get();
|
||||
$task = Task::query()->where('id', $this->task_id)->first();
|
||||
echo $task->name . "开始执行".PHP_EOL;
|
||||
// 更新任务状态为running
|
||||
$task->job_id = $this->job->getJobId();
|
||||
$task->status = 'running';
|
||||
$task->save();
|
||||
//shell_exec($task->shell.' > '.$task->log.' 2>&1 &');
|
||||
shell_exec($task->shell);
|
||||
// 更新任务状态
|
||||
$task->status = 'finished';
|
||||
$task->save();
|
||||
|
||||
18
app/Models/Monitor.php
Normal file
18
app/Models/Monitor.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Monitor extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
// 白名单
|
||||
protected $fillable = [
|
||||
'info',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use Exception;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use App\Services\Plugin;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class PluginServiceProvider extends ServiceProvider
|
||||
@@ -28,10 +29,10 @@ class PluginServiceProvider extends ServiceProvider
|
||||
foreach ($plugins->getPlugins() as $plugin) {
|
||||
|
||||
// 加载视图路径
|
||||
$finder->addNamespace($plugin['name'], $plugin['path']."/views");
|
||||
$finder->addNamespace($plugin['slug'], $plugin['path']."/views");
|
||||
|
||||
// 加载语言包
|
||||
$loader->addNamespace($plugin['name'], $plugin['path']."/lang");
|
||||
$loader->addNamespace($plugin['slug'], $plugin['path']."/lang");
|
||||
}
|
||||
|
||||
// 加载插件Composer装载文件
|
||||
@@ -61,5 +62,8 @@ class PluginServiceProvider extends ServiceProvider
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton('plugins', Plugin::class);
|
||||
// 设置面板名称
|
||||
$name = DB::table('settings')->where('name', 'name')->value('value');
|
||||
$this->app['config']['panel.name'] = !empty($name) ? $name : config('panel.name');
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ class Plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取所有插件
|
||||
*
|
||||
* @return Collection|null
|
||||
* @throws FileNotFoundException
|
||||
* @throws Exception
|
||||
@@ -88,15 +90,18 @@ class Plugin
|
||||
// 初始化插件信息
|
||||
$plugin = [];
|
||||
$plugin['name'] = (Arr::get($package, 'name'));
|
||||
$plugin['slug'] = (Arr::get($package, 'slug'));
|
||||
$plugin['version'] = (Arr::get($package, 'version'));
|
||||
$plugin['path'] = $this->getPluginsDir().DIRECTORY_SEPARATOR.$dirname;
|
||||
|
||||
if ($plugins->has($plugin['name'])) {
|
||||
if ($plugins->has($plugin['slug'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugins->put($plugin['name'], $plugin);
|
||||
$plugins->put($plugin['slug'], $plugin);
|
||||
}
|
||||
|
||||
define('PLUGINS', $plugins->toArray());
|
||||
$this->plugins = $plugins;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1.9",
|
||||
"alexusmai/laravel-file-manager": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^9.19",
|
||||
"laravel/octane": "^1.3",
|
||||
|
||||
278
composer.lock
generated
278
composer.lock
generated
@@ -4,68 +4,8 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "38a554bd11fc94b74f3d7073ea061d23",
|
||||
"content-hash": "e04515c415a76e849e40b5659911865a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alexusmai/laravel-file-manager",
|
||||
"version": "v3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alexusmai/laravel-file-manager.git",
|
||||
"reference": "41d8e1d6020ccb67003f87beb1e3f56b5fa51165"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alexusmai/laravel-file-manager/zipball/41d8e1d6020ccb67003f87beb1e3f56b5fa51165",
|
||||
"reference": "41d8e1d6020ccb67003f87beb1e3f56b5fa51165",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"intervention/image": "^2.7",
|
||||
"intervention/imagecache": "^2.5",
|
||||
"laravel/framework": "^9.0",
|
||||
"league/flysystem": "^3.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Alexusmai\\LaravelFileManager\\FileManagerServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Alexusmai\\LaravelFileManager\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Aleksandr Manekin",
|
||||
"email": "alexusmai@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "File manager for Laravel",
|
||||
"homepage": "https://github.com/alexusami/laravel-file-manager",
|
||||
"keywords": [
|
||||
"file",
|
||||
"laravel",
|
||||
"manager"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/alexusmai/laravel-file-manager/issues",
|
||||
"source": "https://github.com/alexusmai/laravel-file-manager/tree/v3.0.3"
|
||||
},
|
||||
"time": "2022-07-15T12:31:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.10.2",
|
||||
@@ -957,157 +897,6 @@
|
||||
],
|
||||
"time": "2022-10-26T14:07:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "intervention/image",
|
||||
"version": "2.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Intervention/image.git",
|
||||
"reference": "04be355f8d6734c826045d02a1079ad658322dad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad",
|
||||
"reference": "04be355f8d6734c826045d02a1079ad658322dad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-fileinfo": "*",
|
||||
"guzzlehttp/psr7": "~1.1 || ^2.0",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.9.2",
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use GD library based image processing.",
|
||||
"ext-imagick": "to use Imagick based image processing.",
|
||||
"intervention/imagecache": "Caching extension for the Intervention Image library"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Intervention\\Image\\ImageServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Image": "Intervention\\Image\\Facades\\Image"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Intervention\\Image\\": "src/Intervention/Image"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Oliver Vogel",
|
||||
"email": "oliver@intervention.io",
|
||||
"homepage": "https://intervention.io/"
|
||||
}
|
||||
],
|
||||
"description": "Image handling and manipulation library with support for Laravel integration",
|
||||
"homepage": "http://image.intervention.io/",
|
||||
"keywords": [
|
||||
"gd",
|
||||
"image",
|
||||
"imagick",
|
||||
"laravel",
|
||||
"thumbnail",
|
||||
"watermark"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Intervention/image/issues",
|
||||
"source": "https://github.com/Intervention/image/tree/2.7.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://paypal.me/interventionio",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Intervention",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-21T17:30:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "intervention/imagecache",
|
||||
"version": "2.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Intervention/imagecache.git",
|
||||
"reference": "270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Intervention/imagecache/zipball/270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4",
|
||||
"reference": "270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/cache": "^5.5|~6|~7|~8|~9",
|
||||
"illuminate/filesystem": "^5.5|~6|~7|~8|~9",
|
||||
"intervention/image": "~2.2",
|
||||
"nesbot/carbon": "^2.39",
|
||||
"opis/closure": "^3.5",
|
||||
"php": "~7.2|~8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Intervention\\Image\\": "src/Intervention/Image"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Oliver Vogel",
|
||||
"email": "oliver@intervention.io",
|
||||
"homepage": "http://intervention.io/"
|
||||
}
|
||||
],
|
||||
"description": "Caching extension for the Intervention Image Class",
|
||||
"homepage": "https://image.intervention.io",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"gd",
|
||||
"image",
|
||||
"imagick",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Intervention/imagecache/issues",
|
||||
"source": "https://github.com/Intervention/imagecache/tree/2.5.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://paypal.me/interventionio",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Intervention",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-22T11:14:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-diactoros",
|
||||
"version": "2.20.0",
|
||||
@@ -2566,71 +2355,6 @@
|
||||
],
|
||||
"time": "2022-10-28T22:51:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "opis/closure",
|
||||
"version": "3.6.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opis/closure.git",
|
||||
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
|
||||
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.4 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"jeremeamia/superclosure": "^2.0",
|
||||
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Opis\\Closure\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marius Sarca",
|
||||
"email": "marius.sarca@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Sorin Sarca",
|
||||
"email": "sarca_sorin@hotmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
|
||||
"homepage": "https://opis.io/closure",
|
||||
"keywords": [
|
||||
"anonymous functions",
|
||||
"closure",
|
||||
"function",
|
||||
"serializable",
|
||||
"serialization",
|
||||
"serialize"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/opis/closure/issues",
|
||||
"source": "https://github.com/opis/closure/tree/3.6.3"
|
||||
},
|
||||
"time": "2022-01-27T09:35:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "overtrue/laravel-lang",
|
||||
"version": "6.0.3",
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\DefaultConfigRepository;
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ConfigACLRepository;
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* Set Config repository
|
||||
*
|
||||
* Default - DefaultConfigRepository get config from this file
|
||||
*/
|
||||
'configRepository' => DefaultConfigRepository::class,
|
||||
|
||||
/**
|
||||
* ACL rules repository
|
||||
*
|
||||
* Default - ConfigACLRepository (see rules in - aclRules)
|
||||
*/
|
||||
'aclRepository' => \App\Http\FilesACLRepository::class,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository **************
|
||||
|
||||
/**
|
||||
* LFM Route prefix
|
||||
* !!! WARNING - if you change it, you should compile frontend with new prefix(baseUrl) !!!
|
||||
*/
|
||||
'routePrefix' => 'file-manager',
|
||||
|
||||
/**
|
||||
* List of disk names that you want to use
|
||||
* (from config/filesystems)
|
||||
*/
|
||||
'diskList' => ['www'],
|
||||
|
||||
/**
|
||||
* Default disk for left manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'leftDisk' => null,
|
||||
|
||||
/**
|
||||
* Default disk for right manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'rightDisk' => null,
|
||||
|
||||
/**
|
||||
* Default path for left manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'leftPath' => null,
|
||||
|
||||
/**
|
||||
* Default path for right manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'rightPath' => null,
|
||||
|
||||
/**
|
||||
* Image cache ( Intervention Image Cache )
|
||||
*
|
||||
* set null, 0 - if you don't need cache (default)
|
||||
* if you want use cache - set the number of minutes for which the value should be cached
|
||||
*/
|
||||
'cache' => null,
|
||||
|
||||
/**
|
||||
* File manager modules configuration
|
||||
*
|
||||
* 1 - only one file manager window
|
||||
* 2 - one file manager window with directories tree module
|
||||
* 3 - two file manager windows
|
||||
*/
|
||||
'windowsConfig' => 1,
|
||||
|
||||
/**
|
||||
* File upload - Max file size in KB
|
||||
*
|
||||
* null - no restrictions
|
||||
*/
|
||||
'maxUploadFileSize' => null,
|
||||
|
||||
/**
|
||||
* File upload - Allow these file types
|
||||
*
|
||||
* [] - no restrictions
|
||||
*/
|
||||
'allowFileTypes' => [],
|
||||
|
||||
/**
|
||||
* Show / Hide system files and folders
|
||||
*/
|
||||
'hiddenFiles' => true,
|
||||
|
||||
/***************************************************************************
|
||||
* Middleware
|
||||
*
|
||||
* Add your middleware name to array -> ['web', 'auth', 'admin']
|
||||
* !!!! RESTRICT ACCESS FOR NON ADMIN USERS !!!!
|
||||
*/
|
||||
'middleware' => ['web'],
|
||||
|
||||
/***************************************************************************
|
||||
* ACL mechanism ON/OFF
|
||||
*
|
||||
* default - false(OFF)
|
||||
*/
|
||||
'acl' => true,
|
||||
|
||||
/**
|
||||
* Hide files and folders from file-manager if user doesn't have access
|
||||
*
|
||||
* ACL access level = 0
|
||||
*/
|
||||
'aclHideFromFM' => true,
|
||||
|
||||
/**
|
||||
* ACL strategy
|
||||
*
|
||||
* blacklist - Allow everything(access - 2 - r/w) that is not forbidden by the ACL rules list
|
||||
*
|
||||
* whitelist - Deny anything(access - 0 - deny), that not allowed by the ACL rules list
|
||||
*/
|
||||
'aclStrategy' => 'whitelist',
|
||||
|
||||
/**
|
||||
* ACL Rules cache
|
||||
*
|
||||
* null or value in minutes
|
||||
*/
|
||||
'aclRulesCache' => null,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository END **********
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* ACL rules list - used for default ACL repository (ConfigACLRepository)
|
||||
*
|
||||
* 1 it's user ID
|
||||
* null - for not authenticated user
|
||||
*
|
||||
* 'disk' => 'disk-name'
|
||||
*
|
||||
* 'path' => 'folder-name'
|
||||
* 'path' => 'folder1*' - select folder1, folder12, folder1/sub-folder, ...
|
||||
* 'path' => 'folder2/*' - select folder2/sub-folder,... but not select folder2 !!!
|
||||
* 'path' => 'folder-name/file-name.jpg'
|
||||
* 'path' => 'folder-name/*.jpg'
|
||||
*
|
||||
* * - wildcard
|
||||
*
|
||||
* access: 0 - deny, 1 - read, 2 - read/write
|
||||
*/
|
||||
'aclRules' => [
|
||||
null => [
|
||||
//['disk' => 'public', 'path' => '/', 'access' => 2],
|
||||
],
|
||||
1 => [
|
||||
//['disk' => 'public', 'path' => 'images/arch*.jpg', 'access' => 2],
|
||||
//['disk' => 'public', 'path' => 'files/*', 'access' => 1],
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Enable slugification of filenames of uploaded files.
|
||||
*
|
||||
*/
|
||||
'slugifyNames' => false,
|
||||
];
|
||||
@@ -3,6 +3,6 @@ use Illuminate\Support\Facades\Facade;
|
||||
|
||||
return [
|
||||
'name' => '耗子Linux面板',
|
||||
'version' => '20221102',
|
||||
'version' => '20221120',
|
||||
'plugin_dir' => '/www/panel/plugins',
|
||||
];
|
||||
@@ -17,7 +17,6 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->string('slug')->unique()->comment('插件标识');
|
||||
$table->string('name')->comment('插件名称');
|
||||
$table->string('version')->comment('插件版本');
|
||||
$table->boolean('show')->comment('是否首页显示')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('tasks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('job_id')->comment('任务ID');
|
||||
$table->integer('job_id')->nullable()->comment('任务ID');
|
||||
$table->string('name')->comment('任务名');
|
||||
$table->string('status')->default('waiting')->comment('任务状态');
|
||||
$table->string('shell')->nullable()->comment('任务脚本');
|
||||
|
||||
@@ -13,8 +13,9 @@ return new class extends Migration
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('{{ table }}', function (Blueprint $table) {
|
||||
Schema::create('monitors', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->json('info')->comment('监控记录');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
@@ -26,6 +27,6 @@ return new class extends Migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('{{ table }}');
|
||||
Schema::dropIfExists('monitors');
|
||||
}
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* Name: OpenResty插件控制器
|
||||
* Author:耗子
|
||||
* Date: 2022-11-02
|
||||
* Date: 2022-11-21
|
||||
*/
|
||||
|
||||
namespace Plugins\Openresty\Controllers;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"name": "openresty",
|
||||
"version": "20221102"
|
||||
"name": "OpenResty-1.21.4",
|
||||
"slug": "openresty",
|
||||
"version": "1.21.4.1"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* Name: OpenResty插件
|
||||
* Author: 耗子
|
||||
* Date: 2022-11-02
|
||||
* Date: 2022-11-21
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: Openresty管理器
|
||||
Author: 耗子
|
||||
Date: 2022-11-02
|
||||
Date: 2022-11-21
|
||||
-->
|
||||
<title>OpenResty</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
|
||||
@@ -44,7 +44,7 @@ layui.define(['laytpl', 'layer'], function(exports){
|
||||
});
|
||||
|
||||
//跳转到登入页
|
||||
location.hash = '/user/login';
|
||||
location.hash = '/login';
|
||||
};
|
||||
|
||||
//Ajax请求
|
||||
|
||||
File diff suppressed because one or more lines are too long
111
public/vendor/file-manager/js/file-manager.js
vendored
111
public/vendor/file-manager/js/file-manager.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
|
||||
<title>数据库 - PostgreSQL</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">暂不支持!</h1>
|
||||
<h1 style="text-align: center; padding-top: 20px;">管理正在开发中!</h1>
|
||||
@@ -1,25 +1,2 @@
|
||||
<title>文件</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">文件(Beta版)</div>
|
||||
<div class="layui-card-body">
|
||||
<iframe id="panel_fm" src="{{ asset('../../api/fm') }}" style="width: 100%; height: 800px; border: none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
layui.use(['jquery'], function () {
|
||||
var $ = layui.jquery;
|
||||
// 获取iframe的src
|
||||
var src = $('#panel_fm').attr('src');
|
||||
// src后面加上access_token
|
||||
if (layui.data('haozi_panel').access_token !== undefined) {
|
||||
$('#panel_fm').attr('src', src + '?access_token=' + layui.data('haozi_panel').access_token);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<h1 style="text-align: center; padding-top: 20px;">管理正在开发中!</h1>
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="https://cdnjs.cdn.wepublish.cn/bootstrap/5.1.3/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cdn.wepublish.cn/bootstrap-icons/1.8.1/font/bootstrap-icons.min.css">
|
||||
<div id="fm" style="height: 800px;"></div>
|
||||
<link rel="stylesheet" href="{{ asset('../../vendor/file-manager/css/file-manager.css') }}">
|
||||
<script src="{{ asset('../../vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,19 +6,24 @@ Date: 2022-10-14
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div id="address1" class="layui-col-md12">
|
||||
<div style="background: #fff;" class="layui-collapse" lay-filter="home_ad">
|
||||
<div class="layui-colla-content layui-show">
|
||||
<div class="layui-collapse">
|
||||
<div style="background: #fff;" class="layui-colla-content layui-show">
|
||||
<div class="text" style="overflow: hidden;height: 22px;">
|
||||
<ul style="margin-top: -2px;">
|
||||
<li>
|
||||
<a href="https://hzbk.net"
|
||||
<div class="layui-carousel" id="home_ad" lay-filter="home_ad">
|
||||
<div carousel-item="">
|
||||
<a style="background: #fff;" href="https://hzbk.net"
|
||||
title="耗子博客" target="_blank"><i class="layui-icon layui-icon-release"></i> 耗子博客</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<a style="background: #fff;" href="https://weavatar.com"
|
||||
title="WeAvatar" target="_blank"><i class="layui-icon layui-icon-release"></i> WeAvatar - 互联网公共头像服务</a>
|
||||
<a style="background: #fff;" href="https://wepublish.cn"
|
||||
title="WePublish" target="_blank"><i class="layui-icon layui-icon-release"></i> WePublish - WordPress的本土化版本</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="monitor1" class="layui-col-md6">
|
||||
|
||||
@@ -190,7 +195,7 @@ Date: 2022-10-14
|
||||
element.render('progress');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -202,10 +207,19 @@ Date: 2022-10-14
|
||||
clearInterval(home_timer);
|
||||
home_timer = setInterval(refresh_home_info, 3000);
|
||||
// 获取系统信息,这部分信息无需更新。
|
||||
layui.use(['index', 'jquery', 'admin'], function () {
|
||||
layui.use(['index', 'jquery', 'admin', 'carousel'], function () {
|
||||
let $ = layui.jquery
|
||||
, admin = layui.admin
|
||||
, element = layui.element;
|
||||
, element = layui.element
|
||||
, carousel = layui.carousel;
|
||||
carousel.render({
|
||||
elem: '#home_ad'
|
||||
, width: '100%'
|
||||
, height: '200px'
|
||||
, anim: 'fade'
|
||||
, arrow: 'none'
|
||||
, indicator: 'none'
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/panel/info/getSystemInfo"
|
||||
, method: 'get'
|
||||
@@ -217,10 +231,10 @@ Date: 2022-10-14
|
||||
}
|
||||
$('#home_os_name').text(result.data.os_name);
|
||||
$('#home_panel_version').text(result.data.panel_version);
|
||||
$('#home_uptime').text('已不间断运行' + result.data.uptime + '天');
|
||||
$('#home_uptime').text('已不间断运行 ' + result.data.uptime + ' 天');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -254,7 +268,7 @@ Date: 2022-10-14
|
||||
,
|
||||
skin: 'layui-anim layui-anim-upbit'
|
||||
,
|
||||
content: '请在SSH执行<span class="layui-badge-rim">cd /www/panel && php-panel artisan panel update</span>以更新面板!'
|
||||
content: '请在SSH执行<span class="layui-badge-rim">panel update</span>以更新面板!'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// `=---=' //
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
|
||||
// 佛祖保佑 永无Bug 永不宕机 //
|
||||
// Name:耗子Linux面板 Author:耗子 Date:2022-10-14 //
|
||||
// Name:耗子Linux面板 Author:耗子 Date:2022-11-21 //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
@@ -45,17 +45,14 @@
|
||||
base: 'panel/'
|
||||
, version: {{config('panel.version')}}
|
||||
}).use('index', function () {
|
||||
var layer = layui.layer, admin = layui.admin, $ = layui.jquery;
|
||||
let layer = layui.layer, admin = layui.admin, $ = layui.jquery;
|
||||
layer.ready(function () {
|
||||
/**
|
||||
* TODO: 因为开发临时注释了测试版tips,需要取消注释
|
||||
*/
|
||||
/*admin.popup({
|
||||
content: '当前面板为测试版本,如遇到问题请联系耗子反馈!</br>QQ: 823374000'
|
||||
admin.popup({
|
||||
content: '当前面板为公测版本,如遇到问题请联系耗子反馈!</br>QQ: 823374000'
|
||||
, area: '380px'
|
||||
, shade: false
|
||||
, offset: 't'
|
||||
});*/
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -50,6 +50,14 @@
|
||||
, router = layui.router()
|
||||
, search = router.search;
|
||||
|
||||
// 判断并清除定时器
|
||||
if (typeof home_timer !== 'undefined') {
|
||||
clearInterval(home_timer);
|
||||
}
|
||||
if (typeof install_plugin_timer !== 'undefined') {
|
||||
clearInterval(install_plugin_timer);
|
||||
}
|
||||
|
||||
form.render();
|
||||
|
||||
//提交
|
||||
|
||||
@@ -1,2 +1,334 @@
|
||||
<title>监控</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">功能开发中!</h1>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-inline">
|
||||
<span style="margin-right: 10px;">开启监控</span><input type="checkbox" id="monitor-switch"
|
||||
lay-filter="monitor" lay-skin="switch"
|
||||
lay-text="ON|OFF">
|
||||
<span style="margin-left: 40px; margin-right: 10px;">保存天数</span>
|
||||
<div class="layui-input-inline"><input type="number" name="monitor-save-days" class="layui-input"
|
||||
style="height: 30px; margin-top: 5px;" min=0 max=30 disabled>
|
||||
</div>
|
||||
<div class="layui-input-inline">
|
||||
<button id="save_monitor_date" class="layui-btn layui-btn-sm" style="margin-left: 10px;">确定
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="float: right;">
|
||||
<button id="clear_monitor_record" class="layui-btn layui-btn-sm layui-btn-danger">清空监控记录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-row layui-col-space10">
|
||||
<div class="layui-col-xs12 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<span>负载</span>
|
||||
<div style="float: right;">
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">昨天
|
||||
</button>--}}
|
||||
<button class="layui-btn layui-btn-xs">今天</button>
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
最近七天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
最近30天
|
||||
</button>
|
||||
<button id="test" class="layui-btn layui-btn-xs layui-btn-primary">自定义时间
|
||||
</button>--}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div id="load_monitor" style="width: 100%;height: 400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs12 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<span>CPU</span>
|
||||
<div style="float: right;">
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">昨天
|
||||
</button>--}}
|
||||
<button class="layui-btn layui-btn-xs">今天</button>
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
最近七天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">最近30天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">自定义时间
|
||||
</button>--}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div id="cpu_monitor" style="width: 100%;height: 400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-row layui-col-space10">
|
||||
<div class="layui-col-xs12 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<span>内存</span>
|
||||
<div style="float: right;">
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">昨天
|
||||
</button>--}}
|
||||
<button class="layui-btn layui-btn-xs">今天</button>
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
最近七天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
最近30天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">
|
||||
自定义时间
|
||||
</button>--}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div id="memory_monitor" style="width: 100%;height: 400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs12 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<span>网络</span>
|
||||
<div style="float: right;">
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">昨天
|
||||
</button>--}}
|
||||
<button class="layui-btn layui-btn-xs">今天</button>
|
||||
{{--<button class="layui-btn layui-btn-xs layui-btn-primary">最近七天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">最近30天
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-primary">自定义时间
|
||||
</button>--}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<div id="network_monitor" style="width: 100%;height: 400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
layui.use(['admin', 'view', 'form', 'echarts', 'element', 'carousel'], function () {
|
||||
var admin = layui.admin;
|
||||
var view = layui.view;
|
||||
var $ = layui.jquery;
|
||||
var form = layui.form;
|
||||
|
||||
// 获取监控开关和保存天数
|
||||
admin.req({
|
||||
url: '/api/panel/monitor/getMonitorSwitchAndDays',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
if (res.code === 0) {
|
||||
if (res.data.monitor == 1) {
|
||||
$('#monitor-switch').attr('checked', true);
|
||||
} else {
|
||||
$('#monitor-switch').attr('checked', false);
|
||||
}
|
||||
$('input[name="monitor-save-days"]').val(res.data.monitor_days);
|
||||
// 移除禁用
|
||||
$('input[name="monitor-save-days"]').removeAttr('disabled');
|
||||
form.render();
|
||||
}
|
||||
}
|
||||
});
|
||||
// 监听switch开关:是否开启监控
|
||||
form.on('switch(monitor)', function (data) {
|
||||
admin.req({
|
||||
url: '/api/panel/monitor/setMonitorSwitch',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {switch: data.elem.checked},
|
||||
success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('修改成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
// 监听保存天数按钮
|
||||
$('#save_monitor_date').click(function () {
|
||||
var days = $('input[name="monitor-save-days"]').val();
|
||||
if (days == '') {
|
||||
layer.msg('请输入保存天数', {icon: 2});
|
||||
return false;
|
||||
}
|
||||
admin.req({
|
||||
url: '/api/panel/monitor/setMonitorSaveDays',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {days: days},
|
||||
success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('修改成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 监听清除监控数据按钮
|
||||
$('#clear_monitor_record').click(function () {
|
||||
layer.confirm('确定要清除监控数据吗?', function (index) {
|
||||
admin.req({
|
||||
url: '/api/panel/monitor/clearMonitorData',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('清除成功', {icon: 1});
|
||||
setTimeout(function () {
|
||||
admin.render();
|
||||
}, 1000);
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
});
|
||||
|
||||
// 获取监控数据
|
||||
admin.req({
|
||||
url: '/api/panel/monitor/getMonitorData',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
if (res.code !== 0) {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
return false;
|
||||
}
|
||||
renderEcharts('load_monitor', '负载监控', undefined, res.data.times, [{
|
||||
name: '负载',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.uptime.uptime,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}]);
|
||||
renderEcharts('cpu_monitor', 'CPU监控', undefined, res.data.times, [{
|
||||
name: '使用率',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.cpu.use,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}], '{value} %', '单位 %');
|
||||
renderEcharts('memory_monitor', '内存', {
|
||||
x: 'left',
|
||||
data: ["内存", "Swap"]
|
||||
}, res.data.times, [{
|
||||
name: '内存',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.memory.mem_use,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}, {
|
||||
name: 'Swap',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.memory.swap_use,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}], '{value} M', '单位 MB');
|
||||
renderEcharts('network_monitor', '网络', {
|
||||
x: 'left',
|
||||
data: ["出", "入"]
|
||||
}, res.data.times, [{
|
||||
name: '出',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.network.tx_now,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}, {
|
||||
name: '入',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: res.data.network.rx_now,
|
||||
markPoint: {
|
||||
data: [{type: 'max', name: '最大值'}, {type: 'min', name: '最小值'}]
|
||||
},
|
||||
markLine: {
|
||||
data: [{type: 'average', name: '平均值'}]
|
||||
}
|
||||
}], '{value} Kb', '单位 Kb/s');
|
||||
}, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 渲染图表
|
||||
function renderEcharts(element_id, title, legend = undefined, data_xAxis, series, formatter = '{value}', yName = '') {
|
||||
var Chart = echarts.init(document.getElementById(element_id), layui.echartsTheme);
|
||||
var option = {
|
||||
title: {text: title, x: 'center', textStyle: {fontSize: 20}},
|
||||
tooltip: {trigger: 'axis'},
|
||||
legend: legend,
|
||||
xAxis: [{type: 'category', boundaryGap: false, data: data_xAxis}],
|
||||
yAxis: [{
|
||||
name: yName,
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: formatter
|
||||
}
|
||||
}],
|
||||
dataZoom: {
|
||||
show: true,
|
||||
realtime: true,
|
||||
start: 0,
|
||||
end: 100
|
||||
},
|
||||
series: series
|
||||
};
|
||||
|
||||
Chart.setOption(option);
|
||||
window.onresize = Chart.resize;
|
||||
}
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
<title>应用</title>
|
||||
<title>插件</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
@@ -23,16 +23,16 @@
|
||||
<!-- 操作按钮模板 -->
|
||||
<script type="text/html" id="store-control-tpl">
|
||||
@{{# if(d.control.installed == true && d.control.allow_uninstall == true){ }}
|
||||
@{{# if(d.control.update == true){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update">更新</a>
|
||||
@{{# } }}
|
||||
@{{# if(d.control.update == true){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update">更新</a>
|
||||
@{{# } }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="open">管理</a>
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="uninstall">卸载</a>
|
||||
@{{# } else{ }}
|
||||
@{{# if(d.control.installed == true && d.control.allow_uninstall == false){ }}
|
||||
@{{# if(d.control.update == true){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update">更新</a>
|
||||
@{{# } }}
|
||||
@{{# if(d.control.update == true){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update">更新</a>
|
||||
@{{# } }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="open">管理</a>
|
||||
@{{# } else{ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="install">安装</a>
|
||||
@@ -44,8 +44,8 @@
|
||||
<input type="checkbox" name="plugin-show-home" lay-skin="switch" lay-text="ON|OFF"
|
||||
lay-filter="plugin-show-home"
|
||||
value="@{{ d.show }}" data-plugin-slug="@{{ d.slug }}"
|
||||
@{{ d.run==
|
||||
1 ? 'checked' : '' }} />
|
||||
@{{ d.show==
|
||||
1 ? 'checked' : '' }} />
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,14 +86,17 @@
|
||||
layer.confirm('确定安装该插件吗?', function (index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
url: '/api/panel/install',
|
||||
url: '/api/panel/plugin/install',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code == 0) {
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {icon: 1, time: 1000}, function () {
|
||||
if (res.code === 0) {
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
}, function () {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
@@ -101,7 +104,7 @@
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -109,13 +112,13 @@
|
||||
layer.confirm('确定卸载该插件吗?', function (index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
url: '/api/panel/uninstall',
|
||||
url: '/api/panel/plugin/uninstall',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code == 0) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('卸载:' + data.name + ' 成功!', {icon: 1, time: 1000}, function () {
|
||||
location.reload();
|
||||
});
|
||||
@@ -124,7 +127,7 @@
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -132,14 +135,17 @@
|
||||
layer.confirm('确定升级该插件吗?', function (index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
url: '/api/panel/update',
|
||||
url: '/api/panel/plugin/update',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code == 0) {
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {icon: 1, time: 1000}, function () {
|
||||
if (res.code === 0) {
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
}, function () {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
@@ -147,7 +153,7 @@
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -160,6 +166,7 @@
|
||||
let show = obj.elem.checked ? 1 : 0;
|
||||
|
||||
console.log(plugin_slug); //当前行数据
|
||||
|
||||
});
|
||||
/*form.render(null, 'store-form');
|
||||
|
||||
|
||||
@@ -1,2 +1,305 @@
|
||||
<title>安全</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">功能开发中!</h1>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-inline">
|
||||
<span style="margin-right: 10px;">防火墙</span>
|
||||
<input type="checkbox" id="safe_firewall" lay-filter="safe_firewall" lay-skin="switch"
|
||||
lay-text="ON|OFF"/>
|
||||
<span style="margin: 0px 10px;">启用SSH</span>
|
||||
<input type="checkbox" id="safe_ssh" lay-filter="safe_ssh" lay-skin="switch" lay-text="ON|OFF"/>
|
||||
<span style="margin: 0px 10px 0px 20px;">SSH端口</span>
|
||||
<div class="layui-input-inline" style="width: 80px;">
|
||||
<input type="number" id="safe_ssh_port" class="layui-input" style="height: 30px; margin-top: 5px;"
|
||||
min=1
|
||||
max=65535 disabled/>
|
||||
</div>
|
||||
<div class="layui-input-inline">
|
||||
<button id="safe_ssh_port_save" class="layui-btn layui-btn-sm layui-btn-primary">确定
|
||||
</button>
|
||||
</div>
|
||||
<span style="margin: 0px 10px 0px 20px;">允许Ping</span>
|
||||
<input type="checkbox" id="switch_ping" lay-filter="safe_ping" lay-skin="switch" lay-text="ON|OFF"/>
|
||||
</div>
|
||||
<div class="layui-inline" style="float: right;">
|
||||
{{--<button class="layui-btn layui-btn-sm layui-btn-danger">清空 OpenResty 日志
|
||||
</button>--}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="vm_security">
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-inline">
|
||||
<span style="margin-right: 10px;">端口控制</span>
|
||||
<div class="layui-input-inline">
|
||||
<input id="safe_add_firewall_rule_port" type="text" name="safe_add_firewall_rule_port" class="layui-input"
|
||||
placeholder="端口号(如:3306)"
|
||||
min=1 max=65535/>
|
||||
</div>
|
||||
<div class="layui-input-inline">
|
||||
<select id="safe_add_firewall_rule_protocol" lay-filter="safe_add_firewall_rule_protocol"
|
||||
style="height: 30px; margin-top: 5px;">
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="layui-input-inline">
|
||||
<button id="safe_add_firewall_rule" class="layui-btn layui-btn-sm" style="margin-top: -4px;">放行
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="safe-port" lay-filter="safe-port"></table>
|
||||
<!-- 右侧删除端口 -->
|
||||
<script type="text/html" id="safe-port-setting">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'admin', 'form', 'laypage', 'table'], function () {
|
||||
var $ = layui.$;
|
||||
var admin = layui.admin;
|
||||
var table = layui.table;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
|
||||
// 获取防火墙状态
|
||||
admin.req({
|
||||
url: '/api/panel/safe/getFirewallStatus'
|
||||
, type: 'get'
|
||||
, dataType: 'json'
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
// 防火墙
|
||||
if (res.data) {
|
||||
$('#safe_firewall').attr('checked', true);
|
||||
} else {
|
||||
$('#safe_firewall').attr('checked', false);
|
||||
}
|
||||
form.render();
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
// 获取SSH状态
|
||||
admin.req({
|
||||
url: '/api/panel/safe/getSshStatus'
|
||||
, type: 'get'
|
||||
, dataType: 'json'
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
// SSH
|
||||
if (res.data) {
|
||||
$('#safe_ssh').attr('checked', true);
|
||||
} else {
|
||||
$('#safe_ssh').attr('checked', false);
|
||||
}
|
||||
form.render();
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
// 获取SSH端口
|
||||
admin.req({
|
||||
url: '/api/panel/safe/getSshPort'
|
||||
, type: 'get'
|
||||
, dataType: 'json'
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
// SSH端口
|
||||
$('#safe_ssh_port').val(res.data);
|
||||
$('#safe_ssh_port').attr('disabled', false);
|
||||
form.render();
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
// 获取ping状态
|
||||
admin.req({
|
||||
url: '/api/panel/safe/getPingStatus'
|
||||
, type: 'get'
|
||||
, dataType: 'json'
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
// ping
|
||||
if (res.data) {
|
||||
$('#switch_ping').attr('checked', true);
|
||||
} else {
|
||||
$('#switch_ping').attr('checked', false);
|
||||
}
|
||||
form.render();
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 设置防火墙开关
|
||||
form.on('switch(safe_firewall)', function (data) {
|
||||
admin.req({
|
||||
url: '/api/panel/safe/setFirewallStatus'
|
||||
, type: 'post'
|
||||
, dataType: 'json'
|
||||
, data: {
|
||||
status: data.elem.checked === true ? 1 : 0
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 设置SSH开关
|
||||
form.on('switch(safe_ssh)', function (data) {
|
||||
admin.req({
|
||||
url: '/api/panel/safe/setSshStatus'
|
||||
, type: 'post'
|
||||
, dataType: 'json'
|
||||
, data: {
|
||||
status: data.elem.checked === true ? 1 : 0
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 设置ping开关
|
||||
form.on('switch(safe_ping)', function (data) {
|
||||
admin.req({
|
||||
url: '/api/panel/safe/setPingStatus'
|
||||
, type: 'post'
|
||||
, dataType: 'json'
|
||||
, data: {
|
||||
status: data.elem.checked === true ? 1 : 0
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: '#safe-port'
|
||||
, url: '/api/panel/safe/getFirewallRules'
|
||||
//, toolbar: '#website-list-bar'
|
||||
, title: '网站列表'
|
||||
, cols: [[
|
||||
{field: 'port', title: '端口', width: 200}
|
||||
, {field: 'protocol', title: '协议'}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#safe-port-setting', width: 150}
|
||||
]]
|
||||
/**
|
||||
* TODO: 分页
|
||||
*/
|
||||
//, page: true
|
||||
});
|
||||
table.on('tool(safe-port)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除 <b style="color: red;">' + data.protocol + '</b> 端口 <b style="color: red;">' + data.port + '</b> 吗?', function (index) {
|
||||
admin.req({
|
||||
url: "/api/panel/safe/deleteFirewallRule"
|
||||
, method: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:端口删除失败,接口返回' + result);
|
||||
layer.msg('网站删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('<b style="color: red;">' + data.protocol + '</b> 端口 <b style="color: red;">' + data.port + '</b> 删除成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 监听ssh端口保存
|
||||
$('#safe_ssh_port_save').click(function () {
|
||||
var port = Number($('#safe_ssh_port').val());
|
||||
// 判断端口是否合法
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
layer.msg('端口号不合法', {icon: 2});
|
||||
return false;
|
||||
}
|
||||
var index = layer.load(2);
|
||||
admin.req({
|
||||
url: '/api/panel/safe/setSshPort'
|
||||
, type: 'post'
|
||||
, dataType: 'json'
|
||||
, data: {
|
||||
port: port
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.close(index);
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 监听添加端口保存
|
||||
$('#safe_add_firewall_rule').click(function () {
|
||||
var port = Number($('#safe_add_firewall_rule_port').val());
|
||||
var protocol = $('#safe_add_firewall_rule_protocol').val();
|
||||
// 判断端口是否合法
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
layer.msg('端口号不合法', {icon: 2});
|
||||
return false;
|
||||
}
|
||||
var index = layer.load(2);
|
||||
admin.req({
|
||||
url: '/api/panel/safe/addFirewallRule'
|
||||
, type: 'post'
|
||||
, dataType: 'json'
|
||||
, data: {
|
||||
port: port,
|
||||
protocol: protocol
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.close(index);
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
table.reload('safe-port');
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -15,31 +15,17 @@ Date: 2022-10-14
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">面板名称</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="name" value="获取中ing..." class="layui-input">
|
||||
<input type="text" name="name" value="获取中ing..." class="layui-input" disabled/>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">修改面板的显示名称</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="font-size: 13px;">MySQL密码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="mysql_root_password" value="获取中ing..." class="layui-input">
|
||||
<input type="text" name="mysql_root_password" value="获取中ing..." class="layui-input" disabled/>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">修改MySQL的root密码</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">面板账号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="username" value="获取中ing..." class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">修改面板账号</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">面板密码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="password" name="password" value="获取中ing..." class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">修改面板密码</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit lay-filter="panel_setting_submit">确认修改</button>
|
||||
@@ -69,7 +55,7 @@ Date: 2022-10-14
|
||||
|
||||
// ajax获取设置项并赋值
|
||||
admin.req({
|
||||
url: "/api/panel/get_settings"
|
||||
url: "/api/panel/setting/get"
|
||||
, method: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
@@ -80,9 +66,10 @@ Date: 2022-10-14
|
||||
form.val("panel_setting",
|
||||
result.data
|
||||
);
|
||||
$('input').attr('disabled', false);
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -116,7 +103,7 @@ Date: 2022-10-14
|
||||
form.on('submit(panel_setting_submit)', function (obj) {
|
||||
// 提交修改
|
||||
admin.req({
|
||||
url: "/api/panel/save_settings"
|
||||
url: "/api/panel/setting/save"
|
||||
, method: 'post'
|
||||
, data: obj.field
|
||||
, success: function (result) {
|
||||
@@ -126,15 +113,9 @@ Date: 2022-10-14
|
||||
return false;
|
||||
}
|
||||
layer.msg('面板设置保存成功!');
|
||||
if (result.msg == 'change') {
|
||||
layer.msg('您已修改用户名/密码,请重新登录!');
|
||||
setTimeout(function () {
|
||||
location.hash = '/logout';
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -78,7 +78,7 @@ Date: 2022-10-09
|
||||
});
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -136,11 +136,11 @@ Date: 2022-10-09
|
||||
table.on('tool(panel-task-finished)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'remove') {
|
||||
layer.confirm('确定卸载该插件安装记录吗?', function (index) {
|
||||
layer.confirm('确定移除该记录吗?', function (index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
url: '/api/panel/task/deleteTask',
|
||||
type: 'GET',
|
||||
type: 'post',
|
||||
data: {
|
||||
name: data.name
|
||||
}
|
||||
@@ -155,7 +155,7 @@ Date: 2022-10-09
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: 网站 - 添加
|
||||
Author: 耗子
|
||||
Date: 2022-10-14
|
||||
Date: 2022-11-21
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-website-form">
|
||||
@@ -39,7 +39,7 @@ Date: 2022-10-14
|
||||
<label class="layui-form-label">数据库</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="db_type" lay-filter="add-website-db">
|
||||
<option value="" selected=""></option>
|
||||
<option value="" selected="">不使用</option>
|
||||
@{{# layui.each(d.params.db_version, function(index, item){ }}
|
||||
@{{# if(item){ }}
|
||||
<option value="@{{ index }}">@{{ index }}</option>
|
||||
@@ -53,19 +53,19 @@ Date: 2022-10-14
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">数据库名</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="db_name" lay-verify="required" autocomplete="off" class="layui-input">
|
||||
<input type="text" name="db_name" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">数据库用户</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="db_username" lay-verify="required" autocomplete="off" class="layui-input">
|
||||
<input type="text" name="db_username" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">数据库密码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="db_password" lay-verify="required" autocomplete="off" class="layui-input">
|
||||
<input type="text" name="db_password" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,7 +117,12 @@ Date: 2022-10-14
|
||||
if (data.value === 'mysql') {
|
||||
$("#add-website-db-info").show();
|
||||
$('input[name="db_name"]').val($('input[name="name"]').val() + '_mysql');
|
||||
$('input[name="db_user"]').val($('input[name="name"]').val() + '_mysql');
|
||||
$('input[name="db_username"]').val($('input[name="name"]').val() + '_mysql');
|
||||
$('input[name="db_password"]').val($('input[name="name"]').val() + '_password');
|
||||
}else if(data.value === 'postgresql15') {
|
||||
$("#add-website-db-info").show();
|
||||
$('input[name="db_name"]').val($('input[name="name"]').val() + '_postgresql');
|
||||
$('input[name="db_username"]').val($('input[name="name"]').val() + '_postgresql');
|
||||
$('input[name="db_password"]').val($('input[name="name"]').val() + '_password');
|
||||
}
|
||||
});
|
||||
@@ -129,7 +134,6 @@ Date: 2022-10-14
|
||||
}else{
|
||||
data.field.db = 1;
|
||||
}
|
||||
console.log(data.field);
|
||||
admin.req({
|
||||
url: "/api/panel/website/add"
|
||||
, method: 'post'
|
||||
@@ -151,7 +155,7 @@ Date: 2022-10-14
|
||||
});
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
|
||||
306
resources/views/website/edit.blade.php
Normal file
306
resources/views/website/edit.blade.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<!--
|
||||
Name: 网站 - 编辑
|
||||
Author: 耗子
|
||||
Date: 2022-11-21
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">域名端口</li>
|
||||
<li>基本设置</li>
|
||||
<li>防火墙</li>
|
||||
<li>SSL</li>
|
||||
<li>伪静态</li>
|
||||
<li>配置原文</li>
|
||||
<li>访问日志</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<!-- 域名绑定 -->
|
||||
<div class="layui-form layui-form-pane">
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">域名</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="domain" lay-verify="required"
|
||||
placeholder="请输入域名,一行一个支持泛域名"
|
||||
class="layui-textarea">@{{ d.params.config.domain }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">端口</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="port" lay-verify="required"
|
||||
placeholder="请输入访问端口,一行一个"
|
||||
class="layui-textarea">@{{ d.params.config.port }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 基本设置 -->
|
||||
<div class="layui-form layui-form-pane">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">网站目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="path" autocomplete="off" placeholder="请输入网站目录"
|
||||
class="layui-input" value="@{{ d.params.config.path }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">默认文档</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="index" autocomplete="off" placeholder="请输入默认文档,以空格隔开"
|
||||
class="layui-input" value="@{{ d.params.config.index }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">PHP版本</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="php" lay-filter="website-php">
|
||||
@{{# layui.each(d.params.php_version, function(index, item){ }}
|
||||
@{{# if(index == "00"){ }}
|
||||
<option value="@{{ item }}" selected="">@{{ item }}</option>
|
||||
@{{# }else{ }}
|
||||
<option value="@{{ item }}">@{{ item }}</option>
|
||||
@{{# } }}
|
||||
@{{# }); }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item" pane="">
|
||||
<label class="layui-form-label">防跨站攻击</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="open_basedir" lay-skin="switch" lay-text="ON|OFF"
|
||||
@{{ d.params.config.open_basedir== 1 ? 'checked' : '' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 防火墙 -->
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
面板自带开源的 ngx_waf 防火墙<br>文档参考:<a
|
||||
href="https://docs.addesp.com/ngx_waf/zh-cn/advance/directive.html"
|
||||
target="_blank">https://docs.addesp.com/ngx_waf/zh-cn/advance/directive.html</a>
|
||||
</blockquote>
|
||||
<div class="layui-form layui-form-pane">
|
||||
<div class="layui-form-item" pane="">
|
||||
<label class="layui-form-label">总开关</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="checkbox" name="waf" lay-skin="switch" lay-text="ON|OFF"
|
||||
@{{ d.params.config.waf== 1 ? 'checked' : '' }} />
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">只有打开了总开关,下面的设置才会生效!</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">模式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="waf_mode" autocomplete="off" placeholder="DYNAMIC"
|
||||
class="layui-input" value="@{{ d.params.config.waf_mode }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">CC</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="waf_cc_deny" autocomplete="off"
|
||||
placeholder="rate=1000r/m duration=60m"
|
||||
class="layui-input" value="@{{ d.params.config.waf_cc_deny }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">缓存</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="waf_cache" autocomplete="off" placeholder="capacity=50"
|
||||
class="layui-input" value="@{{ d.params.config.waf_cache }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- SSL -->
|
||||
<div class="layui-form layui-form-pane">
|
||||
<div class="layui-form-item" pane="">
|
||||
<label class="layui-form-label">总开关</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="checkbox" name="ssl" lay-skin="switch" lay-text="ON|OFF"
|
||||
@{{ d.params.config.ssl== 1 ? 'checked' : '' }} />
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">只有打开了总开关,下面的设置才会生效!</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">HTTP跳转</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="http_redirect" lay-skin="switch" lay-text="ON|OFF"
|
||||
@{{ d.params.config.http_redirect== 1 ? 'checked' : '' }} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">HSTS</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="checkbox" name="hsts" lay-skin="switch" lay-text="ON|OFF"
|
||||
@{{ d.params.config.hsts== 1 ? 'checked' : '' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">证书</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="ssl_certificate" placeholder="请输入pem证书文件的内容"
|
||||
class="layui-textarea">@{{ d.params.config.ssl_certificate }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">私钥</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="ssl_certificate_key" placeholder="请输入key私钥文件的内容"
|
||||
class="layui-textarea">@{{ d.params.config.ssl_certificate_key }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 伪静态 -->
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
设置伪静态规则,填入 <code>location</code> 部分即可
|
||||
</blockquote>
|
||||
<div id="rewrite-editor" style="height: -webkit-fill-available;">@{{ d.params.config.rewrite }}</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 配置原文 -->
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
如果您不了解配置规则,请勿随意修改,否则可能会导致网站无法访问或面板功能异常!如果已经遇到问题,可尝试:
|
||||
<button id="site-config-restore" class="layui-btn layui-btn-xs">重置配置</button>
|
||||
<br>
|
||||
如果你修改了原文,那么点击保存后,其余的修改将不会生效!
|
||||
</blockquote>
|
||||
<div id="config-editor" style="height: -webkit-fill-available;">@{{ d.params.config.config_raw }}</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 访问日志 -->
|
||||
<button id="clean-site-log" class="layui-btn">清空日志</button>
|
||||
<pre class="layui-code" lay-options="{about: '@{{ d.params.config.name }}.log'}">@{{ d.params.config.log }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-footer">
|
||||
<button id="save-site-config" class="layui-btn">保存设置</button>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
let rewriteEditor = '';
|
||||
let configEditor = '';
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, element = layui.element
|
||||
, layer = layui.layer
|
||||
, laydate = layui.laydate
|
||||
, table = layui.table
|
||||
, form = layui.form;
|
||||
console.log(params);
|
||||
form.render();
|
||||
rewriteEditor = ace.edit("rewrite-editor", {
|
||||
mode: "ace/mode/nginx",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
configEditor = ace.edit("config-editor", {
|
||||
mode: "ace/mode/nginx",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
layui.code({
|
||||
encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
|
||||
$("#clean-site-log").click(function () {
|
||||
layer.confirm('确定要清空日志吗?', function (index) {
|
||||
layer.close(index);
|
||||
layer.load(2);
|
||||
admin.req({
|
||||
url: '/api/panel/website/clearSiteLog'
|
||||
, type: 'post'
|
||||
, data: {name: params.config.name}
|
||||
, success: function (res) {
|
||||
layer.closeAll('loading');
|
||||
if (res.code === 0) {
|
||||
layer.msg('已清空', {icon: 1});
|
||||
setTimeout(function () {
|
||||
admin.render();
|
||||
}, 1000);
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
layer.closeAll('loading');
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#save-site-config').click(function () {
|
||||
layer.load(2);
|
||||
var port = $('textarea[name="port"]').val();
|
||||
var reg = new RegExp(/\n443.*\n?/);
|
||||
// 如果开启了https,就自动添加443端口
|
||||
if ($('input[name="ssl"]').prop('checked') && !reg.test(port)) {
|
||||
console.log(port);
|
||||
port = port + '\n443';
|
||||
}
|
||||
// 如果关闭了https,就自动删除443端口
|
||||
if (!$('input[name="ssl"]').prop('checked') && reg.test(port)) {
|
||||
// 正则替换
|
||||
port = port.replace(/443.*\n?/, '');
|
||||
console.log(port);
|
||||
}
|
||||
admin.req({
|
||||
url: '/api/panel/website/saveSiteSettings'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
name: params.config.name,
|
||||
config: {
|
||||
domain: $('textarea[name="domain"]').val(),
|
||||
port: port,
|
||||
ssl: $('input[name="ssl"]').prop('checked') ? 1 : 0,
|
||||
http_redirect: $('input[name="http_redirect"]').prop('checked') ? 1 : 0,
|
||||
hsts: $('input[name="hsts"]').prop('checked') ? 1 : 0,
|
||||
ssl_certificate: $('textarea[name="ssl_certificate"]').val(),
|
||||
ssl_certificate_key: $('textarea[name="ssl_certificate_key"]').val(),
|
||||
path: $('input[name="path"]').val(),
|
||||
index: $('input[name="index"]').val(),
|
||||
php: $('select[name="php"]').val(),
|
||||
open_basedir: $('input[name="open_basedir"]').prop('checked') ? 1 : 0,
|
||||
waf: $('input[name="waf"]').prop('checked') ? 1 : 0,
|
||||
waf_mode: $('input[name="waf_mode"]').val(),
|
||||
waf_cc_deny: $('input[name="waf_cc_deny"]').val(),
|
||||
waf_cache: $('input[name="waf_cache"]').val(),
|
||||
rewrite: rewriteEditor.getValue(),
|
||||
config_raw: configEditor.getValue()
|
||||
}
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.closeAll('loading');
|
||||
if (res.code === 0) {
|
||||
layer.msg('保存成功', {icon: 1});
|
||||
setTimeout(function () {
|
||||
admin.render();
|
||||
}, 1000);
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: 网站
|
||||
Name: 网站 - 列表
|
||||
Author: 耗子
|
||||
Date: 2022-10-14
|
||||
Date: 2022-11-21
|
||||
-->
|
||||
<title>网站</title>
|
||||
<div class="layui-fluid">
|
||||
@@ -28,9 +28,8 @@ Date: 2022-10-14
|
||||
<script type="text/html" id="website-run">
|
||||
<input type="checkbox" name="run" lay-skin="switch" lay-text="ON|OFF"
|
||||
lay-filter="website-run-checkbox"
|
||||
value="@{{ d.run }}" data-website-name="@{{ d.name }}"
|
||||
@{{ d.run==
|
||||
1 ? 'checked' : '' }} />
|
||||
value="@{{ d.status }}" data-website-name="@{{ d.name }}"
|
||||
@{{ d.status== 1 ? 'checked' : '' }} />
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,7 +64,7 @@ Date: 2022-10-14
|
||||
, icon: 2
|
||||
, content: '已安装的PHP和DB版本获取失败,接口返回' + xhr.status + ' ' + xhr.statusText
|
||||
});
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
table.render({
|
||||
@@ -75,8 +74,8 @@ Date: 2022-10-14
|
||||
, title: '网站列表'
|
||||
, cols: [[
|
||||
{field: 'name', title: '网站名', width: 200, fixed: 'left', unresize: true, sort: true, edit: 'text'}
|
||||
, {field: 'run', title: '运行', width: 90, templet: '#website-run', unresize: true}
|
||||
, {field: 'directory', title: '目录', width: 250}
|
||||
, {field: 'run', title: '运行', width: 100, templet: '#website-run', unresize: true}
|
||||
, {field: 'path', title: '目录', width: 250}
|
||||
, {field: 'php', title: 'PHP', width: 60}
|
||||
, {field: 'ssl', title: 'SSL', width: 110}
|
||||
, {field: 'note', title: '备注', edit: 'textarea'}
|
||||
@@ -140,7 +139,7 @@ Date: 2022-10-14
|
||||
layer.alert('网站' + data.name + '删除成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
@@ -149,7 +148,7 @@ Date: 2022-10-14
|
||||
let config;
|
||||
|
||||
admin.req({
|
||||
url: "/api/panel/get_website_settings?name=" + data.name
|
||||
url: "/api/panel/website/getSiteSettings?name=" + data.name
|
||||
, method: 'get'
|
||||
, beforeSend: function (request) {
|
||||
layer.load();
|
||||
@@ -186,13 +185,28 @@ Date: 2022-10-14
|
||||
}
|
||||
});
|
||||
|
||||
// 单元格编辑
|
||||
table.on('edit(website-run)', function (obj) {
|
||||
var value = obj.value //得到修改后的值
|
||||
, data = obj.data //得到所在行所有键值
|
||||
, field = obj.field; //得到字段
|
||||
layer.msg('[ID: ' + data.id + '] ' + field + ' 字段更改为:' + value, {
|
||||
offset: '15px'
|
||||
// 网站备注编辑
|
||||
table.on('edit(website-list)', function (obj) {
|
||||
var value = obj.value // 得到修改后的值
|
||||
, data = obj.data; // 得到行数据
|
||||
admin.req({
|
||||
url: "/api/panel/website/updateSiteNote"
|
||||
, method: 'post'
|
||||
, data: {
|
||||
name: data.name,
|
||||
note: value
|
||||
}
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站备注更新失败,接口返回' + result);
|
||||
layer.msg('网站备注更新失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('网站 ' + data.name + ' 备注更新成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\MonitorsController;
|
||||
use App\Http\Controllers\Api\PluginsController;
|
||||
use App\Http\Controllers\Api\SafesController;
|
||||
use App\Http\Controllers\Api\SettingsController;
|
||||
use App\Http\Controllers\Api\TasksController;
|
||||
use App\Http\Controllers\Api\UsersController;
|
||||
use App\Http\Controllers\Api\WebsitesController;
|
||||
@@ -23,9 +26,6 @@ use App\Http\Controllers\Api\InfosController;
|
||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
Route::middleware('auth:sanctum')->get('/fm', function () {
|
||||
return view('fm');
|
||||
});
|
||||
Route::prefix('panel')->group(function () {
|
||||
Route::prefix('user')->group(function () {
|
||||
// 登录
|
||||
@@ -73,13 +73,63 @@ Route::prefix('panel')->group(function () {
|
||||
// 获取网站列表
|
||||
Route::get('getList', [WebsitesController::class, 'getList']);
|
||||
Route::post('add', [WebsitesController::class, 'add']);
|
||||
Route::post('delete', [WebsitesController::class, 'delete']);
|
||||
Route::get('getSiteSettings', [WebsitesController::class, 'getSiteSettings']);
|
||||
Route::post('saveSiteSettings', [WebsitesController::class, 'saveSiteSettings']);
|
||||
Route::post('clearSiteLog', [WebsitesController::class, 'clearSiteLog']);
|
||||
Route::post('updateSiteNote', [WebsitesController::class, 'updateSiteNote']);
|
||||
});
|
||||
// 监控
|
||||
Route::middleware('auth:sanctum')->prefix('monitor')->group(function () {
|
||||
// 获取监控数据
|
||||
Route::get('getMonitorData', [MonitorsController::class, 'getMonitorData']);
|
||||
// 获取监控开关和保存天数
|
||||
Route::get('getMonitorSwitchAndDays', [MonitorsController::class, 'getMonitorSwitchAndDays']);
|
||||
// 设置监控开关
|
||||
Route::post('setMonitorSwitch', [MonitorsController::class, 'setMonitorSwitch']);
|
||||
// 设置保存天数
|
||||
Route::post('setMonitorSaveDays', [MonitorsController::class, 'setMonitorSaveDays']);
|
||||
// 清空监控数据
|
||||
Route::post('clearMonitorData', [MonitorsController::class, 'clearMonitorData']);
|
||||
});
|
||||
// 安全
|
||||
Route::middleware('auth:sanctum')->prefix('safe')->group(function () {
|
||||
// 获取防火墙状态
|
||||
Route::get('getFirewallStatus', [SafesController::class, 'getFirewallStatus']);
|
||||
// 设置防火墙状态
|
||||
Route::post('setFirewallStatus', [SafesController::class, 'setFirewallStatus']);
|
||||
// 获取SSH状态
|
||||
Route::get('getSshStatus', [SafesController::class, 'getSshStatus']);
|
||||
// 设置SSH状态
|
||||
Route::post('setSshStatus', [SafesController::class, 'setSshStatus']);
|
||||
// 获取SSH端口
|
||||
Route::get('getSshPort', [SafesController::class, 'getSshPort']);
|
||||
// 设置SSH端口
|
||||
Route::post('setSshPort', [SafesController::class, 'setSshPort']);
|
||||
// 获取ping状态
|
||||
Route::get('getPingStatus', [SafesController::class, 'getPingStatus']);
|
||||
// 设置ping状态
|
||||
Route::post('setPingStatus', [SafesController::class, 'setPingStatus']);
|
||||
// 获取防火墙规则
|
||||
Route::get('getFirewallRules', [SafesController::class, 'getFirewallRules']);
|
||||
// 添加防火墙规则
|
||||
Route::post('addFirewallRule', [SafesController::class, 'addFirewallRule']);
|
||||
// 删除防火墙规则
|
||||
Route::post('deleteFirewallRule', [SafesController::class, 'deleteFirewallRule']);
|
||||
});
|
||||
// 插件
|
||||
Route::middleware('auth:sanctum')->prefix('plugin')->group(function () {
|
||||
// 获取插件列表
|
||||
Route::get('getList', [PluginsController::class, 'getList']);
|
||||
Route::get('install', [PluginsController::class, 'install']);
|
||||
Route::get('uninstall', [PluginsController::class, 'uninstall']);
|
||||
Route::post('install', [PluginsController::class, 'install']);
|
||||
Route::post('uninstall', [PluginsController::class, 'uninstall']);
|
||||
});
|
||||
// 设置
|
||||
Route::middleware('auth:sanctum')->prefix('setting')->group(function () {
|
||||
// 获取设置
|
||||
Route::get('get', [SettingsController::class, 'get']);
|
||||
// 保存设置
|
||||
Route::post('save', [SettingsController::class, 'save']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
vendor/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
.idea
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
> Contributions are welcome, and are accepted via pull requests.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Please ensure all pull requests are made against the `develop` branch on GitHub.
|
||||
22
vendor/alexusmai/laravel-file-manager/LICENSE.md
vendored
22
vendor/alexusmai/laravel-file-manager/LICENSE.md
vendored
@@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Aleksandr Manekin alexusmai@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
74
vendor/alexusmai/laravel-file-manager/README.md
vendored
74
vendor/alexusmai/laravel-file-manager/README.md
vendored
@@ -1,74 +0,0 @@
|
||||
# Laravel File Manager
|
||||
|
||||
[](https://packagist.org/packages/alexusmai/laravel-file-manager)
|
||||
[](https://packagist.org/packages/alexusmai/laravel-file-manager)
|
||||
[](https://packagist.org/packages/alexusmai/laravel-file-manager)
|
||||
[](https://packagist.org/packages/alexusmai/laravel-file-manager)
|
||||
[](https://packagist.org/packages/alexusmai/laravel-file-manager)
|
||||
|
||||

|
||||
|
||||
**DEMO:** [Laravel File Manager](http://file-manager.webmai.ru/)
|
||||
|
||||
**Vue.js Frontend:** [alexusmai/vue-laravel-file-manager](https://github.com/alexusmai/vue-laravel-file-manager)
|
||||
|
||||
## Documentation
|
||||
|
||||
[Laravel File Manager Docs](./docs/index.md)
|
||||
* [Installation](./docs/installation.md)
|
||||
* [Configuration](./docs/configuration.md)
|
||||
* [Integration](./docs/integration.md)
|
||||
* [ACL](./docs/acl.md)
|
||||
* [Events](./docs/events.md)
|
||||
* [Update](./docs/update.md)
|
||||
|
||||
## Features
|
||||
|
||||
* Frontend on Vue.js - [vue-laravel-file-manager](https://github.com/alexusmai/vue-laravel-file-manager)
|
||||
* Work with the file system is organized by the standard means Laravel Flysystem:
|
||||
* Local, FTP, S3, Dropbox ...
|
||||
* The ability to work only with the selected disks
|
||||
* Several options for displaying the file manager:
|
||||
* One-panel view
|
||||
* One-panel + Directory tree
|
||||
* Two-panel
|
||||
* The minimum required set of operations:
|
||||
* Creating files
|
||||
* Creating folders
|
||||
* Copying / Cutting Folders and Files
|
||||
* Renaming
|
||||
* Uploading files (multi-upload)
|
||||
* Downloading files
|
||||
* Two modes of displaying elements - table and grid
|
||||
* Preview for images
|
||||
* Viewing images
|
||||
* Full screen mode
|
||||
* More operations (v.2):
|
||||
* Audio player (mp3, ogg, wav, aac), Video player (webm, mp4) - ([Plyr](https://github.com/sampotts/plyr))
|
||||
* Code editor - ([Code Mirror](https://github.com/codemirror/codemirror))
|
||||
* Image cropper - ([Cropper.js](https://github.com/fengyuanchen/cropperjs))
|
||||
* Zip / Unzip - only for local disks
|
||||
* Integration with WYSIWYG Editors:
|
||||
* CKEditor 4
|
||||
* TinyMCE 4
|
||||
* TinyMCE 5
|
||||
* SummerNote
|
||||
* Standalone button
|
||||
* ACL - access control list
|
||||
* delimiting access to files and folders
|
||||
* two work strategies:
|
||||
* blacklist - Allow everything that is not forbidden by the ACL rules list
|
||||
* whitelist - Deny everything, that not allowed by the ACL rules list
|
||||
* You can use different repositories for the rules - an array (configuration file), a database (there is an example implementation), or you can add your own.
|
||||
* You can hide files and folders that are not accessible.
|
||||
* Events (v2.2)
|
||||
* Thumbnails lazy load
|
||||
* Dynamic configuration (v2.4)
|
||||
* Supported locales : ru, en, ar, sr, cs, de, es, nl, zh-CN, fa, it, tr, fr, pt-BR, zh-TW, pl
|
||||
|
||||
## In a new version 3
|
||||
|
||||
- **Version 3 only works with Laravel 9!**
|
||||
- Vue.js 3
|
||||
- Bootstrap 5
|
||||
- Bootstrap Icons
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "alexusmai/laravel-file-manager",
|
||||
"description": "File manager for Laravel",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"file",
|
||||
"manager"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Aleksandr Manekin",
|
||||
"email": "alexusmai@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/alexusami/laravel-file-manager",
|
||||
"license": "MIT",
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"ext-zip": "*",
|
||||
"ext-json": "*",
|
||||
"laravel/framework": "^9.0",
|
||||
"league/flysystem": "^3.0",
|
||||
"intervention/image": "^2.7",
|
||||
"intervention/imagecache": "^2.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Alexusmai\\LaravelFileManager\\": "src"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Alexusmai\\LaravelFileManager\\FileManagerServiceProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\DefaultConfigRepository;
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ConfigACLRepository;
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* Set Config repository
|
||||
*
|
||||
* Default - DefaultConfigRepository get config from this file
|
||||
*/
|
||||
'configRepository' => DefaultConfigRepository::class,
|
||||
|
||||
/**
|
||||
* ACL rules repository
|
||||
*
|
||||
* Default - ConfigACLRepository (see rules in - aclRules)
|
||||
*/
|
||||
'aclRepository' => ConfigACLRepository::class,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository **************
|
||||
|
||||
/**
|
||||
* LFM Route prefix
|
||||
* !!! WARNING - if you change it, you should compile frontend with new prefix(baseUrl) !!!
|
||||
*/
|
||||
'routePrefix' => 'file-manager',
|
||||
|
||||
/**
|
||||
* List of disk names that you want to use
|
||||
* (from config/filesystems)
|
||||
*/
|
||||
'diskList' => ['public'],
|
||||
|
||||
/**
|
||||
* Default disk for left manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'leftDisk' => null,
|
||||
|
||||
/**
|
||||
* Default disk for right manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'rightDisk' => null,
|
||||
|
||||
/**
|
||||
* Default path for left manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'leftPath' => null,
|
||||
|
||||
/**
|
||||
* Default path for right manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'rightPath' => null,
|
||||
|
||||
/**
|
||||
* Image cache ( Intervention Image Cache )
|
||||
*
|
||||
* set null, 0 - if you don't need cache (default)
|
||||
* if you want use cache - set the number of minutes for which the value should be cached
|
||||
*/
|
||||
'cache' => null,
|
||||
|
||||
/**
|
||||
* File manager modules configuration
|
||||
*
|
||||
* 1 - only one file manager window
|
||||
* 2 - one file manager window with directories tree module
|
||||
* 3 - two file manager windows
|
||||
*/
|
||||
'windowsConfig' => 2,
|
||||
|
||||
/**
|
||||
* File upload - Max file size in KB
|
||||
*
|
||||
* null - no restrictions
|
||||
*/
|
||||
'maxUploadFileSize' => null,
|
||||
|
||||
/**
|
||||
* File upload - Allow these file types
|
||||
*
|
||||
* [] - no restrictions
|
||||
*/
|
||||
'allowFileTypes' => [],
|
||||
|
||||
/**
|
||||
* Show / Hide system files and folders
|
||||
*/
|
||||
'hiddenFiles' => true,
|
||||
|
||||
/***************************************************************************
|
||||
* Middleware
|
||||
*
|
||||
* Add your middleware name to array -> ['web', 'auth', 'admin']
|
||||
* !!!! RESTRICT ACCESS FOR NON ADMIN USERS !!!!
|
||||
*/
|
||||
'middleware' => ['web'],
|
||||
|
||||
/***************************************************************************
|
||||
* ACL mechanism ON/OFF
|
||||
*
|
||||
* default - false(OFF)
|
||||
*/
|
||||
'acl' => false,
|
||||
|
||||
/**
|
||||
* Hide files and folders from file-manager if user doesn't have access
|
||||
*
|
||||
* ACL access level = 0
|
||||
*/
|
||||
'aclHideFromFM' => true,
|
||||
|
||||
/**
|
||||
* ACL strategy
|
||||
*
|
||||
* blacklist - Allow everything(access - 2 - r/w) that is not forbidden by the ACL rules list
|
||||
*
|
||||
* whitelist - Deny anything(access - 0 - deny), that not allowed by the ACL rules list
|
||||
*/
|
||||
'aclStrategy' => 'blacklist',
|
||||
|
||||
/**
|
||||
* ACL Rules cache
|
||||
*
|
||||
* null or value in minutes
|
||||
*/
|
||||
'aclRulesCache' => null,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository END **********
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* ACL rules list - used for default ACL repository (ConfigACLRepository)
|
||||
*
|
||||
* 1 it's user ID
|
||||
* null - for not authenticated user
|
||||
*
|
||||
* 'disk' => 'disk-name'
|
||||
*
|
||||
* 'path' => 'folder-name'
|
||||
* 'path' => 'folder1*' - select folder1, folder12, folder1/sub-folder, ...
|
||||
* 'path' => 'folder2/*' - select folder2/sub-folder,... but not select folder2 !!!
|
||||
* 'path' => 'folder-name/file-name.jpg'
|
||||
* 'path' => 'folder-name/*.jpg'
|
||||
*
|
||||
* * - wildcard
|
||||
*
|
||||
* access: 0 - deny, 1 - read, 2 - read/write
|
||||
*/
|
||||
'aclRules' => [
|
||||
null => [
|
||||
//['disk' => 'public', 'path' => '/', 'access' => 2],
|
||||
],
|
||||
1 => [
|
||||
//['disk' => 'public', 'path' => 'images/arch*.jpg', 'access' => 2],
|
||||
//['disk' => 'public', 'path' => 'files/*', 'access' => 1],
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Enable slugification of filenames of uploaded files.
|
||||
*
|
||||
*/
|
||||
'slugifyNames' => false,
|
||||
];
|
||||
185
vendor/alexusmai/laravel-file-manager/docs/acl.md
vendored
185
vendor/alexusmai/laravel-file-manager/docs/acl.md
vendored
@@ -1,185 +0,0 @@
|
||||
# ACL
|
||||
|
||||
You can use the access control system to differentiate access to files and folders for different users.
|
||||
For this you need to make the following settings.
|
||||
Open configuration file - config/file-manager.php
|
||||
|
||||
1. Turn ON ACL system (fm-acl middleware will turn ON automatically)
|
||||
|
||||
```php
|
||||
// set true
|
||||
'acl' => true,
|
||||
```
|
||||
|
||||
2. You can hide files and folders to which the user does not have access(access = 0).
|
||||
|
||||
```php
|
||||
'aclHideFromFM' => true,
|
||||
```
|
||||
3. ACL system operation strategies:
|
||||
|
||||
```php
|
||||
/**
|
||||
* ACL strategy
|
||||
*
|
||||
* blacklist - Allow everything(access - 2 - r/w) that is not forbidden by the ACL rules list
|
||||
*
|
||||
* whitelist - Deny anything(access - 0 - deny), that not allowed by the ACL rules list
|
||||
*/
|
||||
'aclStrategy' => 'blacklist',
|
||||
```
|
||||
|
||||
4. Set the rule repository, the default is the configuration file.
|
||||
|
||||
```php
|
||||
/**
|
||||
* ACL rules repository
|
||||
*
|
||||
* default - config file(ConfigACLRepository)
|
||||
*/
|
||||
'aclRepository' => \Alexusmai\LaravelFileManager\Services\ACLService\ConfigACLRepository::class,
|
||||
```
|
||||
|
||||
Now you can add your rules in 'aclRules' array. But if you want to store your rules in another place, such as a database, you need to create your own class, and implements two functions from ACLRepository.
|
||||
|
||||
I have already made a similar class for an example, and if it suits you, you can use it. You only need to replace the repository name in the configuration file. And add a new migration to the database.
|
||||
|
||||
```php
|
||||
php artisan vendor:publish --tag=fm-migrations
|
||||
```
|
||||
|
||||
See [/src/Services/ACLService/DBACLRepository.php](../src/Services/ACLService/DBACLRepository.php) and [/migrations/2019_02_06_174631_make_acl_rules_table.php](./../migrations/2019_02_06_174631_make_acl_rules_table.php)
|
||||
|
||||
## Example 1
|
||||
|
||||
I have disk 'images' in /config/filesystems.php for folder /public/images
|
||||
|
||||
```php
|
||||
'disks' => [
|
||||
|
||||
'images' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('images'),
|
||||
'url' => env('APP_URL').'/images/',
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
This disk contain:
|
||||
|
||||
```php
|
||||
/ // disk root folder
|
||||
|-- nature // folder
|
||||
|-- cars // folder
|
||||
|-- icons
|
||||
|-- image1.jpg // file
|
||||
|-- image2.jpg
|
||||
|-- avatar.png
|
||||
```
|
||||
|
||||
I add this disk to file-manager config file
|
||||
|
||||
```php
|
||||
'diskList' => ['images'],
|
||||
|
||||
'aclStrategy' => 'blacklist',
|
||||
|
||||
// now it's a black list
|
||||
'aclRules' => [
|
||||
// null - for not authenticated users
|
||||
null => [
|
||||
['disk' => 'images', 'path' => 'nature', 'access' => 0], // guest don't have access for this folder
|
||||
['disk' => 'images', 'path' => 'icons', 'access' => 1], // only read - guest can't change folder - rename, delete
|
||||
['disk' => 'images', 'path' => 'icons/*', 'access' => 1], // only read all files and foders in this folder
|
||||
['disk' => 'images', 'path' => 'image*.jpg', 'access' => 0], // can't read and write (preview, rename, delete..)
|
||||
['disk' => 'images', 'path' => 'avatar.png', 'access' => 1], // only read (view)
|
||||
|
||||
],
|
||||
// for user with ID = 1
|
||||
1 => [
|
||||
['disk' => 'images', 'path' => 'cars', 'access' => 0], // don't have access
|
||||
['disk' => 'public', 'path' => 'image*.jpg', 'access' => 1], // only read (view)
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
> Task: For each registered user, a new folder is created with his name(in folder /users). You want to allow users access only to their folders. But for an administrator with ID = 1, allow access to all folders.
|
||||
|
||||
- You need to create a new repository for ACL rules, for example, in the / app / Http folder
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ACLRepository;
|
||||
|
||||
class UsersACLRepository implements ACLRepository
|
||||
{
|
||||
/**
|
||||
* Get user ID
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserID()
|
||||
{
|
||||
return \Auth::id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ACL rules list for user
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRules(): array
|
||||
{
|
||||
if (\Auth::id() === 1) {
|
||||
return [
|
||||
['disk' => 'disk-name', 'path' => '*', 'access' => 2],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
['disk' => 'disk-name', 'path' => '/', 'access' => 1], // main folder - read
|
||||
['disk' => 'disk-name', 'path' => 'users', 'access' => 1], // only read
|
||||
['disk' => 'disk-name', 'path' => 'users/'. \Auth::user()->name, 'access' => 1], // only read
|
||||
['disk' => 'disk-name', 'path' => 'users/'. \Auth::user()->name .'/*', 'access' => 2], // read and write
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- disk-name - you need to replace for your disk name
|
||||
|
||||
- now in the config file we will change the repository to a new one, and set aclStrategy in whitelist - we will deny everything that is not allowed by the rules. You can also hide folders and files that are not available.
|
||||
|
||||
```php
|
||||
/**
|
||||
* Hide files and folders from file-manager if user doesn't have access
|
||||
* ACL access level = 0
|
||||
*/
|
||||
'aclHideFromFM' => true,
|
||||
|
||||
/**
|
||||
* ACL strategy
|
||||
*
|
||||
* blacklist - Allow everything(access - 2 - r/w) that is not forbidden by the ACL rules list
|
||||
*
|
||||
* whitelist - Deny anything(access - 0 - deny), that not allowed by the ACL rules list
|
||||
*/
|
||||
'aclStrategy' => 'whitelist',
|
||||
|
||||
/**
|
||||
* ACL rules repository
|
||||
*
|
||||
* default - config file(ConfigACLRepository)
|
||||
*/
|
||||
'aclRepository' => \App\Http\UsersACLRepository::class,
|
||||
```
|
||||
|
||||
|
||||
## What's next
|
||||
|
||||
[Events](./events.md)
|
||||
@@ -1,161 +0,0 @@
|
||||
# Configuration
|
||||
|
||||
Open configuration file - config/file-manager.php
|
||||
|
||||
- fill the disk list from config/filesystem.php (select the desired drive names)
|
||||
- set cache
|
||||
- select file manager windows configuration
|
||||
|
||||
**!!! Be sure to add your middleware to restrict access to the application !!!**
|
||||
|
||||
**Don't forget to configure your php and Nginx**
|
||||
|
||||
```
|
||||
// PHP
|
||||
upload_max_filesize,
|
||||
post_max_size
|
||||
|
||||
// Nginx
|
||||
client_max_body_size
|
||||
```
|
||||
|
||||
### You can set default disk and default path
|
||||
|
||||
You have two variants for how to do it:
|
||||
|
||||
1. Add this params to the config file (config/file-manager.php)
|
||||
|
||||
```php
|
||||
/**
|
||||
* Default disk for left manager
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'leftDisk' => 'public',
|
||||
|
||||
/**
|
||||
* Default disk for right manager
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'rightDisk' => null,
|
||||
|
||||
/**
|
||||
* Default path for left manager
|
||||
* null - root directory
|
||||
*/
|
||||
'leftPath' => 'directory/sub-directory',
|
||||
|
||||
/**
|
||||
* Default path for right manager
|
||||
* null - root directory
|
||||
*/
|
||||
'rightPath' => null,
|
||||
```
|
||||
|
||||
2 Or you can add this params in URL
|
||||
|
||||
```
|
||||
http://site.name/?leftDisk=public
|
||||
|
||||
http://site.name/?leftDisk=public&rightDisk=images
|
||||
|
||||
http://site.name/?leftDisk=public&leftPath=directory/sub-directory
|
||||
|
||||
http://site.name/?leftDisk=public&leftPath=directory2&rightDisk=images&rightPath=cars/vw/golf
|
||||
// %2F - /, %20 - space
|
||||
http://site.name/?leftDisk=public&leftPath=directory2&rightDisk=images&rightPath=cars%2Fvw%2Fgolf
|
||||
```
|
||||
|
||||
leftDisk and leftPath is default for the file manager windows configuration - 1,2
|
||||
|
||||
**You can't add a disk that does not exist in the diskList array !**
|
||||
|
||||
**! Params in URL have more weight than params in config file. It means that URL params can overwrite your config params. !**
|
||||
|
||||
## Disk settings example
|
||||
|
||||
- config/filesystems.php
|
||||
|
||||
```php
|
||||
// Filesystem Disks
|
||||
'disks' => [
|
||||
// images folder in public path
|
||||
'images' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('images'),
|
||||
'url' => env('APP_URL').'/images',
|
||||
],
|
||||
|
||||
// public folder in storage/app/public
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage', // https://laravel.com/docs/5.7/filesystem#file-urls
|
||||
'visibility' => 'public',
|
||||
],
|
||||
|
||||
// ftp
|
||||
'dd-wrt' => [
|
||||
'driver' => 'ftp',
|
||||
'host' => 'ftp.dd-wrt.com',
|
||||
'username' => 'anonymous',
|
||||
'passive' => true,
|
||||
'timeout' => 30,
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
- config/file-manager.php
|
||||
|
||||
```php
|
||||
// You need to enter the disks you want to use in the file manager
|
||||
'diskList' => ['images', 'public'],
|
||||
```
|
||||
|
||||
## Normalization of uploaded file names
|
||||
|
||||
If you expect to work with files that may have filenames that are not considered *normal* `f.e.: "DCIM_2021 - čšč& (1).jpg.jpg"` so basically any time you give the option to upload files to users, you can set `slugifyNames` to `true` and have the names ran through `Str::slug()` before saving it so you file will look something like `dcim-2021-csc-1.jpg`
|
||||
|
||||
## Dynamic configuration
|
||||
|
||||
You can create your own configuration, for example for different users or their roles.
|
||||
|
||||
Create new class - example - TestConfigRepository
|
||||
|
||||
```php
|
||||
namespace App\Http;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
|
||||
class TestConfigRepository implements ConfigRepository
|
||||
{
|
||||
// implement all methods from interface
|
||||
|
||||
/**
|
||||
* Get disk list
|
||||
*
|
||||
* ['public', 'local', 's3']
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDiskList(): array
|
||||
{
|
||||
if (\Auth::id() === 1) {
|
||||
return [
|
||||
['public', 'local', 's3'],
|
||||
];
|
||||
}
|
||||
|
||||
return ['public'];
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For example see [src/Services/ConfigService/DefaultConfigRepository.php](https://github.com/alexusmai/laravel-file-manager/blob/master/src/Services/ConfigService/DefaultConfigRepository.php)
|
||||
|
||||
## What's next
|
||||
|
||||
[ACL](./acl.md)
|
||||
|
||||
[Integration](./integration.md)
|
||||
321
vendor/alexusmai/laravel-file-manager/docs/events.md
vendored
321
vendor/alexusmai/laravel-file-manager/docs/events.md
vendored
@@ -1,321 +0,0 @@
|
||||
# Events
|
||||
|
||||
### BeforeInitialization
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\BeforeInitialization
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\BeforeInitialization',
|
||||
function ($event) {
|
||||
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### DiskSelected
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\DiskSelected
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\DiskSelected',
|
||||
function ($event) {
|
||||
\Log::info('DiskSelected:', [$event->disk()]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### FilesUploading
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\FilesUploading
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\FilesUploading',
|
||||
function ($event) {
|
||||
\Log::info('FilesUploading:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->files(),
|
||||
$event->overwrite(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### FilesUploaded
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\FilesUploaded
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\FilesUploaded',
|
||||
function ($event) {
|
||||
\Log::info('FilesUploaded:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->files(),
|
||||
$event->overwrite(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Deleting
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Deleting
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Deleting',
|
||||
function ($event) {
|
||||
\Log::info('Deleting:', [
|
||||
$event->disk(),
|
||||
$event->items(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Deleted
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Deleted
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Deleted',
|
||||
function ($event) {
|
||||
\Log::info('Deleted:', [
|
||||
$event->disk(),
|
||||
$event->items(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Paste
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Paste
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Paste',
|
||||
function ($event) {
|
||||
\Log::info('Paste:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->clipboard(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Rename
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Rename
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Rename',
|
||||
function ($event) {
|
||||
\Log::info('Rename:', [
|
||||
$event->disk(),
|
||||
$event->newName(),
|
||||
$event->oldName(),
|
||||
$event->type(), // 'file' or 'dir'
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Download
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Download
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Download',
|
||||
function ($event) {
|
||||
\Log::info('Download:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
*When using a text editor, the file you are editing is also downloaded! And this event is triggered!*
|
||||
|
||||
### DirectoryCreating
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\DirectoryCreating
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\DirectoryCreating',
|
||||
function ($event) {
|
||||
\Log::info('DirectoryCreating:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### DirectoryCreated
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\DirectoryCreated
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\DirectoryCreated',
|
||||
function ($event) {
|
||||
\Log::info('DirectoryCreated:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### FileCreating
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\FileCreating
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\FileCreating',
|
||||
function ($event) {
|
||||
\Log::info('FileCreating:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### FileCreated
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\FileCreated
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\FileCreated',
|
||||
function ($event) {
|
||||
\Log::info('FileCreated:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### FileUpdate
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\FileUpdate
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\FileUpdate',
|
||||
function ($event) {
|
||||
\Log::info('FileUpdate:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Zip
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Zip
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Zip',
|
||||
function ($event) {
|
||||
\Log::info('Zip:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
$event->elements(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### ZipCreated
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\ZipCreated
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\ZipCreated',
|
||||
function ($event) {
|
||||
\Log::info('ZipCreated:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
$event->elements(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### ZipFailed
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\ZipCreated
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\ZipFailed',
|
||||
function ($event) {
|
||||
\Log::info('ZipFailed:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->name(),
|
||||
$event->elements(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Unzip
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\Unzip
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\Unzip',
|
||||
function ($event) {
|
||||
\Log::info('Unzip:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->folder(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### UnzipCreated
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\UnzipCreated
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\UnzipCreated',
|
||||
function ($event) {
|
||||
\Log::info('UnzipCreated:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->folder(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### UnzipFailed
|
||||
|
||||
> Alexusmai\LaravelFileManager\Events\UnzipFailed
|
||||
|
||||
```php
|
||||
\Event::listen('Alexusmai\LaravelFileManager\Events\UnzipFailed',
|
||||
function ($event) {
|
||||
\Log::info('UnzipFailed:', [
|
||||
$event->disk(),
|
||||
$event->path(),
|
||||
$event->folder(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
```
|
||||
@@ -1,18 +0,0 @@
|
||||
# Laravel File Manager
|
||||
|
||||
## Docs
|
||||
|
||||
* [Installation](./installation.md)
|
||||
* [Configuration](./configuration.md)
|
||||
* [Integration](./integration.md)
|
||||
* [ACL](./acl.md)
|
||||
* [Events](./events.md)
|
||||
* [Update](./update.md)
|
||||
|
||||
## Requirements
|
||||
* PHP >= 8.0
|
||||
* ext-zip - for zip and unzip functions
|
||||
* Laravel 9 or higher
|
||||
* GD Library or Imagick for [intervention/image](https://github.com/Intervention/image)
|
||||
* requires [intervention/image](https://github.com/Intervention/image) and [intervention/imagecache](https://github.com/Intervention/imagecache)
|
||||
* Bootstrap 5 and Bootstrap Icons v1.8.0 and higher
|
||||
@@ -1,91 +0,0 @@
|
||||
# Installation
|
||||
|
||||
1. Install package - using composer
|
||||
|
||||
```
|
||||
composer require alexusmai/laravel-file-manager
|
||||
```
|
||||
|
||||
For Laravel 5 - 8 use v2.5.4
|
||||
|
||||
```
|
||||
composer require alexusmai/laravel-file-manager "2.5.4"
|
||||
```
|
||||
|
||||
2. If you use Laravel 5.4, then add service provider to config/app.php (for the Laravel 5.5 and higher skip this step):
|
||||
|
||||
```php
|
||||
Alexusmai\LaravelFileManager\FileManagerServiceProvider::class,
|
||||
```
|
||||
|
||||
3. Publish configuration file
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag=fm-config
|
||||
```
|
||||
|
||||
4. You can install npm package directly and use it in your vue application - more information about it -
|
||||
[vue-laravel-file-manager](https://github.com/alexusmai/vue-laravel-file-manager)
|
||||
|
||||
> OR
|
||||
|
||||
Publish compiled and minimized js and css files
|
||||
|
||||
```
|
||||
php artisan vendor:publish --tag=fm-assets
|
||||
```
|
||||
|
||||
Open the view file where you want to place the application block, and add:
|
||||
|
||||
* add a csrf token to head block if you did not do it before
|
||||
|
||||
```
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
```
|
||||
|
||||
* For version 3 and higher - the frontend package uses **Bootstrap 5** and **Bootstrap Icons** styles, if you already use it,
|
||||
then you do not need to connect any styles. Otherwise, add -
|
||||
|
||||
```
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
```
|
||||
|
||||
* For old versions - the frontend package uses **Bootstrap 4** and **Font Awesome 5** styles, if you already use it,
|
||||
then you do not need to connect any styles. Otherwise, add -
|
||||
|
||||
```
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
|
||||
```
|
||||
|
||||
* add file manager styles
|
||||
|
||||
```
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
```
|
||||
|
||||
* add file manager js
|
||||
|
||||
```
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
```
|
||||
|
||||
* For version 3 and higher - add div for application (set application height!)
|
||||
|
||||
```
|
||||
<div id="fm" style="height: 600px;"></div>
|
||||
```
|
||||
|
||||
* For old versions - add div for application (set application height!)
|
||||
|
||||
```
|
||||
<div style="height: 600px;">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## What's next
|
||||
|
||||
[Configuration](./configuration.md)
|
||||
@@ -1,229 +0,0 @@
|
||||
# Integration
|
||||
|
||||
> See examples in [examples](./../examples) folder
|
||||
|
||||
### CKEditor 4
|
||||
|
||||
Add to CKEditor config
|
||||
|
||||
```js
|
||||
CKEDITOR.replace('editor-id', {filebrowserImageBrowseUrl: '/file-manager/ckeditor'});
|
||||
```
|
||||
|
||||
OR in to the config.js
|
||||
|
||||
```js
|
||||
CKEDITOR.editorConfig = function( config ) {
|
||||
|
||||
//...
|
||||
|
||||
// Upload image
|
||||
config.filebrowserImageBrowseUrl = '/file-manager/ckeditor';
|
||||
};
|
||||
```
|
||||
|
||||
After these actions, you will be able to call the file manager from the CKEditor editor menu (Image -> Selection on the server).
|
||||
The file manager will appear in a new window.
|
||||
|
||||
### TinyMCE 4
|
||||
|
||||
Add to TinyMCE configuration
|
||||
|
||||
```js
|
||||
tinymce.init({
|
||||
selector: '#my-textarea',
|
||||
// ...
|
||||
file_browser_callback: function(field_name, url, type, win) {
|
||||
tinyMCE.activeEditor.windowManager.open({
|
||||
file: '/file-manager/tinymce',
|
||||
title: 'Laravel File Manager',
|
||||
width: window.innerWidth * 0.8,
|
||||
height: window.innerHeight * 0.8,
|
||||
resizable: 'yes',
|
||||
close_previous: 'no',
|
||||
}, {
|
||||
setUrl: function(url) {
|
||||
win.document.getElementById(field_name).value = url;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### TinyMCE 5
|
||||
|
||||
Add to TinyMCE 5 configuration
|
||||
|
||||
```js
|
||||
tinymce.init({
|
||||
selector: '#my-textarea',
|
||||
// ...
|
||||
file_picker_callback (callback, value, meta) {
|
||||
let x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth
|
||||
let y = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight
|
||||
|
||||
tinymce.activeEditor.windowManager.openUrl({
|
||||
url : '/file-manager/tinymce5',
|
||||
title : 'Laravel File manager',
|
||||
width : x * 0.8,
|
||||
height : y * 0.8,
|
||||
onMessage: (api, message) => {
|
||||
callback(message.content, { text: message.text })
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### SummerNote
|
||||
|
||||
Create and add new button
|
||||
|
||||
```js
|
||||
// File manager button (image icon)
|
||||
const FMButton = function(context) {
|
||||
const ui = $.summernote.ui;
|
||||
const button = ui.button({
|
||||
contents: '<i class="note-icon-picture"></i> ',
|
||||
tooltip: 'File Manager',
|
||||
click: function() {
|
||||
window.open('/file-manager/summernote', 'fm', 'width=1400,height=800');
|
||||
}
|
||||
});
|
||||
return button.render();
|
||||
};
|
||||
|
||||
$('#summernote').summernote({
|
||||
toolbar: [
|
||||
// [groupName, [list of button]]
|
||||
// your settings
|
||||
['fm-button', ['fm']],
|
||||
],
|
||||
buttons: {
|
||||
fm: FMButton
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
And add this function
|
||||
|
||||
```js
|
||||
// set file link
|
||||
function fmSetLink(url) {
|
||||
$('#summernote').summernote('insertImage', url);
|
||||
}
|
||||
```
|
||||
|
||||
See [example](./../examples/wysiwyg/summernote.blade.php)
|
||||
|
||||
### Standalone button
|
||||
|
||||
Add button
|
||||
|
||||
```html
|
||||
<div class="input-group">
|
||||
<input type="text" id="image_label" class="form-control" name="image"
|
||||
aria-label="Image" aria-describedby="button-image">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="button" id="button-image">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
and js script
|
||||
|
||||
```js
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
document.getElementById('button-image').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
window.open('/file-manager/fm-button', 'fm', 'width=1400,height=800');
|
||||
});
|
||||
});
|
||||
|
||||
// set file link
|
||||
function fmSetLink($url) {
|
||||
document.getElementById('image_label').value = $url;
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple standalone buttons
|
||||
|
||||
```html
|
||||
<!-- HTML -->
|
||||
<div class="container">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="image_label">Image</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="image1" class="form-control" name="image"
|
||||
aria-label="Image" aria-describedby="button-image">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="button" id="button-image">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="image_label">Image2</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="image2" class="form-control" name="image"
|
||||
aria-label="Image" aria-describedby="button-image">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="button" id="button-image2">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JS -->
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
document.getElementById('button-image').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
inputId = 'image1';
|
||||
|
||||
window.open('/file-manager/fm-button', 'fm', 'width=1400,height=800');
|
||||
});
|
||||
|
||||
// second button
|
||||
document.getElementById('button-image2').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
inputId = 'image2';
|
||||
|
||||
window.open('/file-manager/fm-button', 'fm', 'width=1400,height=800');
|
||||
});
|
||||
});
|
||||
|
||||
// input
|
||||
let inputId = '';
|
||||
|
||||
// set file link
|
||||
function fmSetLink($url) {
|
||||
document.getElementById(inputId).value = $url;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Modifications
|
||||
|
||||
To change standard views(with file manager), publish them.
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag=fm-views
|
||||
```
|
||||
|
||||
You will get:
|
||||
|
||||
```
|
||||
resources/views/vendor/file-manager/ckeditor.blade.php
|
||||
resources/views/vendor/file-manager/tinymce.blade.php
|
||||
resources/views/vendor/file-manager/summernote.blade.php
|
||||
resources/views/vendor/file-manager/fmButton.blade.php
|
||||
```
|
||||
|
||||
Now you can change styles and any more..
|
||||
@@ -1,23 +0,0 @@
|
||||
# How to update to the latest version?
|
||||
|
||||
- Backup your settings - config/file-manager.php
|
||||
- Will be better if you delete configuration file from "config" folder before updating
|
||||
- download a new version
|
||||
|
||||
```
|
||||
composer update alexusmai/laravel-file-manager
|
||||
```
|
||||
|
||||
- Update config file and assets(js)
|
||||
|
||||
```
|
||||
// config
|
||||
php artisan vendor:publish --tag=fm-config --force
|
||||
// js, css
|
||||
php artisan vendor:publish --tag=fm-assets --force
|
||||
```
|
||||
|
||||
- set your settings in to the config/file-manager.php
|
||||
- if you implementing "ConfigRepository" and you see a new settings in
|
||||
the config file - don't forget to add new functions in to your class
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
|
||||
<title>File manager and CKeditor</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<textarea name="editor"></textarea>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.ckeditor.com/4.11.2/standard/ckeditor.js"></script>
|
||||
<script>
|
||||
CKEDITOR.replace( 'editor', {filebrowserImageBrowseUrl: '/file-manager/ckeditor'});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,46 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
|
||||
<title>File Manager and standalone button</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="image_label">Image</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="image_label" class="form-control" name="image"
|
||||
aria-label="Image" aria-describedby="button-image">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="button" id="button-image">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
document.getElementById('button-image').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
window.open('/file-manager/fm-button', 'fm', 'width=1400,height=800');
|
||||
});
|
||||
});
|
||||
|
||||
// set file link
|
||||
function fmSetLink($url) {
|
||||
document.getElementById('image_label').value = $url;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,66 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<!-- SummerNote -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote-bs4.css" rel="stylesheet">
|
||||
|
||||
<title>File Manager and SummerNote</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div id="summernote"></div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
|
||||
<!-- SummerNote js -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote-bs4.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// File manager button (image icon)
|
||||
const FMButton = function(context) {
|
||||
const ui = $.summernote.ui;
|
||||
const button = ui.button({
|
||||
contents: '<i class="note-icon-picture"></i> ',
|
||||
tooltip: 'File Manager',
|
||||
click: function() {
|
||||
window.open('/file-manager/summernote', 'fm', 'width=1400,height=800');
|
||||
}
|
||||
});
|
||||
return button.render();
|
||||
};
|
||||
|
||||
$('#summernote').summernote({
|
||||
toolbar: [
|
||||
// [groupName, [list of button]]
|
||||
['style', ['bold', 'italic', 'underline', 'clear']],
|
||||
['font', ['strikethrough', 'superscript', 'subscript']],
|
||||
['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['height', ['height']],
|
||||
['fm-button', ['fm']],
|
||||
],
|
||||
buttons: {
|
||||
fm: FMButton
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// set file link
|
||||
function fmSetLink(url) {
|
||||
$('#summernote').summernote('insertImage', url);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,53 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
|
||||
<title>File manager and TinyMCE</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<form method="post">
|
||||
<textarea id="my-textarea"></textarea>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- TinyMCE -->
|
||||
<script src='https://cloud.tinymce.com/stable/tinymce.min.js'></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
tinymce.init({
|
||||
selector: '#my-textarea',
|
||||
plugins: [
|
||||
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
|
||||
'searchreplace wordcount visualblocks visualchars code fullscreen',
|
||||
'insertdatetime media nonbreaking save table contextmenu directionality',
|
||||
'emoticons template paste textcolor colorpicker textpattern',
|
||||
],
|
||||
toolbar: 'insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media',
|
||||
relative_urls: false,
|
||||
file_browser_callback: function(field_name, url, type, win) {
|
||||
tinyMCE.activeEditor.windowManager.open({
|
||||
file: '/file-manager/tinymce',
|
||||
title: 'Laravel File Manager',
|
||||
width: window.innerWidth * 0.8,
|
||||
height: window.innerHeight * 0.8,
|
||||
resizable: 'yes',
|
||||
close_previous: 'no',
|
||||
}, {
|
||||
setUrl: function(url) {
|
||||
win.document.getElementById(field_name).value = url;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MakeAclRulesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('acl_rules', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('user_id')->nullable();
|
||||
$table->string('disk');
|
||||
$table->string('path');
|
||||
$table->tinyInteger('access');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('acl_rules');
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,51 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'File Manager') }}</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="fm-main-block">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File manager -->
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// set fm height
|
||||
document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');
|
||||
|
||||
// Helper function to get parameters from the query string.
|
||||
function getUrlParam(paramName) {
|
||||
const reParam = new RegExp('(?:[\?&]|&)' + paramName + '=([^&]+)', 'i');
|
||||
const match = window.location.search.match(reParam);
|
||||
|
||||
return (match && match.length > 1) ? match[1] : null;
|
||||
}
|
||||
|
||||
// Add callback to file manager
|
||||
fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
|
||||
const funcNum = getUrlParam('CKEditorFuncNum');
|
||||
|
||||
window.opener.CKEDITOR.tools.callFunction(funcNum, fileUrl);
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'File Manager') }}</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="fm-main-block">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File manager -->
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// set fm height
|
||||
document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');
|
||||
|
||||
// Add callback to file manager
|
||||
fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
|
||||
window.opener.fmSetLink(fileUrl);
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'File Manager') }}</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="fm-main-block">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File manager -->
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// set fm height
|
||||
document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');
|
||||
|
||||
// Add callback to file manager
|
||||
fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
|
||||
window.opener.fmSetLink(fileUrl);
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'File Manager') }}</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="fm-main-block">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File manager -->
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// set fm height
|
||||
document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');
|
||||
|
||||
const FileBrowserDialogue = {
|
||||
init: function() {
|
||||
// Here goes your code for setting your custom things onLoad.
|
||||
},
|
||||
mySubmit: function (URL) {
|
||||
// pass selected file path to TinyMCE
|
||||
parent.tinymce.activeEditor.windowManager.getParams().setUrl(URL);
|
||||
// close popup window
|
||||
parent.tinymce.activeEditor.windowManager.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Add callback to file manager
|
||||
fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
|
||||
FileBrowserDialogue.mySubmit(fileUrl);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'File Manager') }}</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="fm-main-block">
|
||||
<div id="fm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File manager -->
|
||||
<script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// set fm height
|
||||
document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');
|
||||
|
||||
const FileBrowserDialogue = {
|
||||
init: function() {
|
||||
// Here goes your code for setting your custom things onLoad.
|
||||
},
|
||||
mySubmit: function (URL) {
|
||||
// pass selected file path to TinyMCE
|
||||
parent.postMessage({
|
||||
mceAction: 'insert',
|
||||
content: URL,
|
||||
text: URL.split('/').pop()
|
||||
})
|
||||
// close popup window
|
||||
parent.postMessage({ mceAction: 'close' });
|
||||
}
|
||||
};
|
||||
|
||||
// Add callback to file manager
|
||||
fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
|
||||
FileBrowserDialogue.mySubmit(fileUrl);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,427 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Controllers;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Events\BeforeInitialization;
|
||||
use Alexusmai\LaravelFileManager\Events\Deleting;
|
||||
use Alexusmai\LaravelFileManager\Events\DirectoryCreated;
|
||||
use Alexusmai\LaravelFileManager\Events\DirectoryCreating;
|
||||
use Alexusmai\LaravelFileManager\Events\DiskSelected;
|
||||
use Alexusmai\LaravelFileManager\Events\Download;
|
||||
use Alexusmai\LaravelFileManager\Events\FileCreated;
|
||||
use Alexusmai\LaravelFileManager\Events\FileCreating;
|
||||
use Alexusmai\LaravelFileManager\Events\FilesUploaded;
|
||||
use Alexusmai\LaravelFileManager\Events\FilesUploading;
|
||||
use Alexusmai\LaravelFileManager\Events\FileUpdate;
|
||||
use Alexusmai\LaravelFileManager\Events\Paste;
|
||||
use Alexusmai\LaravelFileManager\Events\Rename;
|
||||
use Alexusmai\LaravelFileManager\Events\Zip as ZipEvent;
|
||||
use Alexusmai\LaravelFileManager\Events\Unzip as UnzipEvent;
|
||||
use Alexusmai\LaravelFileManager\Requests\RequestValidator;
|
||||
use Alexusmai\LaravelFileManager\FileManager;
|
||||
use Alexusmai\LaravelFileManager\Services\Zip;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\View\View;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class FileManagerController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var FileManager
|
||||
*/
|
||||
public $fm;
|
||||
|
||||
/**
|
||||
* FileManagerController constructor.
|
||||
*
|
||||
* @param FileManager $fm
|
||||
*/
|
||||
public function __construct(FileManager $fm)
|
||||
{
|
||||
$this->fm = $fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize file manager
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function initialize(): JsonResponse
|
||||
{
|
||||
event(new BeforeInitialization());
|
||||
|
||||
return response()->json(
|
||||
$this->fm->initialize()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files and directories for the selected path and disk
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public function content(RequestValidator $request): JsonResponse
|
||||
{
|
||||
return response()->json(
|
||||
$this->fm->content(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory tree
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public function tree(RequestValidator $request): JsonResponse
|
||||
{
|
||||
return response()->json(
|
||||
$this->fm->tree(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the selected disk
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function selectDisk(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new DiskSelected($request->input('disk')));
|
||||
|
||||
return response()->json([
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'diskSelected',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload files
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function upload(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new FilesUploading($request));
|
||||
|
||||
$uploadResponse = $this->fm->upload(
|
||||
$request->input('disk'),
|
||||
$request->input('path'),
|
||||
$request->file('files'),
|
||||
$request->input('overwrite')
|
||||
);
|
||||
|
||||
event(new FilesUploaded($request));
|
||||
|
||||
return response()->json($uploadResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files and folders
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new Deleting($request));
|
||||
|
||||
$deleteResponse = $this->fm->delete(
|
||||
$request->input('disk'),
|
||||
$request->input('items')
|
||||
);
|
||||
|
||||
return response()->json($deleteResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy / Cut files and folders
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function paste(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new Paste($request));
|
||||
|
||||
return response()->json(
|
||||
$this->fm->paste(
|
||||
$request->input('disk'),
|
||||
$request->input('path'),
|
||||
$request->input('clipboard')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function rename(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new Rename($request));
|
||||
|
||||
return response()->json(
|
||||
$this->fm->rename(
|
||||
$request->input('disk'),
|
||||
$request->input('newName'),
|
||||
$request->input('oldName')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function download(RequestValidator $request): StreamedResponse
|
||||
{
|
||||
event(new Download($request));
|
||||
|
||||
return $this->fm->download(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create thumbnails
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return Response|mixed
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function thumbnails(RequestValidator $request): mixed
|
||||
{
|
||||
return $this->fm->thumbnails(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Image preview
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function preview(RequestValidator $request): mixed
|
||||
{
|
||||
return $this->fm->preview(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* File url
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function url(RequestValidator $request): JsonResponse
|
||||
{
|
||||
return response()->json(
|
||||
$this->fm->url(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new directory
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function createDirectory(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new DirectoryCreating($request));
|
||||
|
||||
$createDirectoryResponse = $this->fm->createDirectory(
|
||||
$request->input('disk'),
|
||||
$request->input('path'),
|
||||
$request->input('name')
|
||||
);
|
||||
|
||||
if ($createDirectoryResponse['result']['status'] === 'success') {
|
||||
event(new DirectoryCreated($request));
|
||||
}
|
||||
|
||||
return response()->json($createDirectoryResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new file
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function createFile(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new FileCreating($request));
|
||||
|
||||
$createFileResponse = $this->fm->createFile(
|
||||
$request->input('disk'),
|
||||
$request->input('path'),
|
||||
$request->input('name')
|
||||
);
|
||||
|
||||
if ($createFileResponse['result']['status'] === 'success') {
|
||||
event(new FileCreated($request));
|
||||
}
|
||||
|
||||
return response()->json($createFileResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update file
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function updateFile(RequestValidator $request): JsonResponse
|
||||
{
|
||||
event(new FileUpdate($request));
|
||||
|
||||
return response()->json(
|
||||
$this->fm->updateFile(
|
||||
$request->input('disk'),
|
||||
$request->input('path'),
|
||||
$request->file('file')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream file
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function streamFile(RequestValidator $request): mixed
|
||||
{
|
||||
return $this->fm->streamFile(
|
||||
$request->input('disk'),
|
||||
$request->input('path')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create zip archive
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
* @param Zip $zip
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function zip(RequestValidator $request, Zip $zip)
|
||||
{
|
||||
event(new ZipEvent($request));
|
||||
|
||||
return $zip->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract zip archive
|
||||
*
|
||||
* @param RequestValidator $request
|
||||
* @param Zip $zip
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function unzip(RequestValidator $request, Zip $zip)
|
||||
{
|
||||
event(new UnzipEvent($request));
|
||||
|
||||
return $zip->extract();
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration with ckeditor 4
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function ckeditor(): Factory|View
|
||||
{
|
||||
return view('file-manager::ckeditor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration with TinyMCE v4
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function tinymce(): Factory|View
|
||||
{
|
||||
return view('file-manager::tinymce');
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration with TinyMCE v5
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function tinymce5(): Factory|View
|
||||
{
|
||||
return view('file-manager::tinymce5');
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration with SummerNote
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function summernote(): Factory|View
|
||||
{
|
||||
return view('file-manager::summernote');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple integration with input field
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function fmButton(): Factory|View
|
||||
{
|
||||
return view('file-manager::fmButton');
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
class BeforeInitialization
|
||||
{
|
||||
/**
|
||||
* BeforeInitialization constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Deleted
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $items;
|
||||
|
||||
/**
|
||||
* Deleted constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct($disk, $items)
|
||||
{
|
||||
$this->disk = $disk;
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Deleting
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $items;
|
||||
|
||||
/**
|
||||
* Deleting constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->items = $request->input('items');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DirectoryCreated
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* DirectoryCreated constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DirectoryCreating
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* DirectoryCreating constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
|
||||
class DiskSelected
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* DiskSelected constructor.
|
||||
*
|
||||
* @param $disk
|
||||
*/
|
||||
public function __construct($disk)
|
||||
{
|
||||
$this->disk = $disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Download
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Download constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FileCreated
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* FileCreated constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FileCreating
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* FileCreating constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FileUpdate
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Http\UploadedFile
|
||||
*/
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* FileUpdate constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->file = $request->file('file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
if ($this->path) {
|
||||
return $this->path.'/'.$this->file->getClientOriginalName();
|
||||
}
|
||||
|
||||
return $this->file->getClientOriginalName();
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FilesUploaded
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Http\UploadedFile
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $overwrite;
|
||||
|
||||
/**
|
||||
* FilesUploaded constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->files = $request->file('files');
|
||||
$this->overwrite = $request->input('overwrite');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return array_map(function ($file) {
|
||||
return [
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'path' => $this->path.'/'.$file->getClientOriginalName(),
|
||||
'extension' => $file->extension(),
|
||||
];
|
||||
}, $this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function overwrite()
|
||||
{
|
||||
return !!$this->overwrite;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FilesUploading
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Http\UploadedFile
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $overwrite;
|
||||
|
||||
/**
|
||||
* FilesUploading constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->files = $request->file('files');
|
||||
$this->overwrite = $request->input('overwrite');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return array_map(function ($file) {
|
||||
return [
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'path' => $this->path.'/'.$file->getClientOriginalName(),
|
||||
'extension' => $file->extension(),
|
||||
];
|
||||
}, $this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function overwrite()
|
||||
{
|
||||
return !!$this->overwrite;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Paste
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $clipboard;
|
||||
|
||||
/**
|
||||
* Paste constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->clipboard = $request->input('clipboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function clipboard()
|
||||
{
|
||||
return $this->clipboard;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Rename
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $newName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $oldName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* Rename constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->newName = $request->input('newName');
|
||||
$this->oldName = $request->input('oldName');
|
||||
$this->type = $request->input('type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function newName()
|
||||
{
|
||||
return $this->newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function oldName()
|
||||
{
|
||||
return $this->oldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
/*
|
||||
* $info = Storage::disk($this->disk)->getMetadata($this->oldName);
|
||||
* return $info['type'];
|
||||
*/
|
||||
|
||||
return $this->type;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Unzip
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $folder;
|
||||
|
||||
/**
|
||||
* Unzip constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->folder = $request->input('folder');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function folder()
|
||||
{
|
||||
return $this->folder;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UnzipCreated
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $folder;
|
||||
|
||||
/**
|
||||
* Unzip constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->folder = $request->input('folder');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function folder()
|
||||
{
|
||||
return $this->folder;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UnzipFailed
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $folder;
|
||||
|
||||
/**
|
||||
* UnzipFailed constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->folder = $request->input('folder');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function folder()
|
||||
{
|
||||
return $this->folder;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Zip
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var array|string|null
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* Zip constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
$this->elements = $request->input('elements');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string|null
|
||||
*/
|
||||
public function elements()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ZipCreated
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var array|string|null
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* ZipCreated constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
$this->elements = $request->input('elements');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string|null
|
||||
*/
|
||||
public function elements()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Events;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ZipFailed
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $disk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var array|string|null
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* ZipFailed constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->disk = $request->input('disk');
|
||||
$this->path = $request->input('path');
|
||||
$this->name = $request->input('name');
|
||||
$this->elements = $request->input('elements');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function disk()
|
||||
{
|
||||
return $this->disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string|null
|
||||
*/
|
||||
public function elements()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Events\Deleted;
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
use Alexusmai\LaravelFileManager\Services\TransferService\TransferFactory;
|
||||
use Alexusmai\LaravelFileManager\Traits\CheckTrait;
|
||||
use Alexusmai\LaravelFileManager\Traits\ContentTrait;
|
||||
use Alexusmai\LaravelFileManager\Traits\PathTrait;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class FileManager
|
||||
{
|
||||
use PathTrait, ContentTrait, CheckTrait;
|
||||
|
||||
/**
|
||||
* @var ConfigRepository
|
||||
*/
|
||||
public ConfigRepository $configRepository;
|
||||
|
||||
/**
|
||||
* FileManager constructor.
|
||||
*
|
||||
* @param ConfigRepository $configRepository
|
||||
*/
|
||||
public function __construct(ConfigRepository $configRepository)
|
||||
{
|
||||
$this->configRepository = $configRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize App
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function initialize(): array
|
||||
{
|
||||
if (!config()->has('file-manager')) {
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'danger',
|
||||
'message' => 'noConfig',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$config = [
|
||||
'acl' => $this->configRepository->getAcl(),
|
||||
'leftDisk' => $this->configRepository->getLeftDisk(),
|
||||
'rightDisk' => $this->configRepository->getRightDisk(),
|
||||
'leftPath' => $this->configRepository->getLeftPath(),
|
||||
'rightPath' => $this->configRepository->getRightPath(),
|
||||
'windowsConfig' => $this->configRepository->getWindowsConfig(),
|
||||
'hiddenFiles' => $this->configRepository->getHiddenFiles(),
|
||||
];
|
||||
|
||||
// disk list
|
||||
foreach ($this->configRepository->getDiskList() as $disk) {
|
||||
if (array_key_exists($disk, config('filesystems.disks'))) {
|
||||
$config['disks'][$disk] = Arr::only(
|
||||
config('filesystems.disks')[$disk], ['driver']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// get language
|
||||
$config['lang'] = 'zh-CN';
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => null,
|
||||
],
|
||||
'config' => $config,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files and directories for the selected path and disk
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return array
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public function content($disk, $path): array
|
||||
{
|
||||
$content = $this->getContent($disk, $path);
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => null,
|
||||
],
|
||||
'directories' => $content['directories'],
|
||||
'files' => $content['files'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get part of the directory tree
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return array
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public function tree($disk, $path): array
|
||||
{
|
||||
$directories = $this->getDirectoriesTree($disk, $path);
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => null,
|
||||
],
|
||||
'directories' => $directories,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload files
|
||||
*
|
||||
* @param string|null $disk
|
||||
* @param string|null $path
|
||||
* @param array|null $files
|
||||
* @param bool $overwrite
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function upload($disk, $path, $files, $overwrite): array
|
||||
{
|
||||
$fileNotUploaded = false;
|
||||
|
||||
foreach ($files as $file) {
|
||||
// skip or overwrite files
|
||||
if (!$overwrite && Storage::disk($disk)->exists($path . '/' . $file->getClientOriginalName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check file size
|
||||
if ($this->configRepository->getMaxUploadFileSize()
|
||||
&& $file->getSize() / 1024 > $this->configRepository->getMaxUploadFileSize()
|
||||
) {
|
||||
$fileNotUploaded = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check file type
|
||||
if ($this->configRepository->getAllowFileTypes()
|
||||
&& !in_array(
|
||||
$file->getClientOriginalExtension(),
|
||||
$this->configRepository->getAllowFileTypes()
|
||||
)
|
||||
) {
|
||||
$fileNotUploaded = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $file->getClientOriginalName();
|
||||
if ($this->configRepository->getSlugifyNames()) {
|
||||
$name = Str::slug(
|
||||
Str::replace(
|
||||
'.' . $file->getClientOriginalExtension(),
|
||||
'',
|
||||
$name
|
||||
)
|
||||
) . '.' . $file->getClientOriginalExtension();
|
||||
}
|
||||
// overwrite or save file
|
||||
Storage::disk($disk)->putFileAs(
|
||||
$path,
|
||||
$file,
|
||||
$name
|
||||
);
|
||||
}
|
||||
|
||||
if ($fileNotUploaded) {
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'warning',
|
||||
'message' => 'notAllUploaded',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'uploaded',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files and folders
|
||||
*
|
||||
* @param $disk
|
||||
* @param $items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function delete($disk, $items): array
|
||||
{
|
||||
$deletedItems = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (!Storage::disk($disk)->exists($item['path'])) {
|
||||
continue;
|
||||
} else {
|
||||
if ($item['type'] === 'dir') {
|
||||
Storage::disk($disk)->deleteDirectory($item['path']);
|
||||
} else {
|
||||
Storage::disk($disk)->delete($item['path']);
|
||||
}
|
||||
}
|
||||
|
||||
$deletedItems[] = $item;
|
||||
}
|
||||
|
||||
event(new Deleted($disk, $deletedItems));
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'deleted',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy / Cut - Files and Directories
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
* @param $clipboard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function paste($disk, $path, $clipboard): array
|
||||
{
|
||||
// compare disk names
|
||||
if ($disk !== $clipboard['disk']) {
|
||||
|
||||
if (!$this->checkDisk($clipboard['disk'])) {
|
||||
return $this->notFoundMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$transferService = TransferFactory::build($disk, $path, $clipboard);
|
||||
|
||||
return $transferService->filesTransfer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename file or folder
|
||||
*
|
||||
* @param $disk
|
||||
* @param $newName
|
||||
* @param $oldName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rename($disk, $newName, $oldName): array
|
||||
{
|
||||
Storage::disk($disk)->move($oldName, $newName);
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'renamed',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Download selected file
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function download($disk, $path): StreamedResponse
|
||||
{
|
||||
// if file name not in ASCII format
|
||||
if (!preg_match('/^[\x20-\x7e]*$/', basename($path))) {
|
||||
$filename = Str::ascii(basename($path));
|
||||
} else {
|
||||
$filename = basename($path);
|
||||
}
|
||||
|
||||
return Storage::disk($disk)->download($path, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create thumbnails
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return Response|mixed
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function thumbnails($disk, $path): mixed
|
||||
{
|
||||
if ($this->configRepository->getCache()) {
|
||||
$thumbnail = Image::cache(function ($image) use ($disk, $path) {
|
||||
$image->make(Storage::disk($disk)->get($path))->fit(80);
|
||||
}, $this->configRepository->getCache());
|
||||
|
||||
// output
|
||||
return response()->make(
|
||||
$thumbnail,
|
||||
200,
|
||||
['Content-Type' => Storage::disk($disk)->mimeType($path)]
|
||||
);
|
||||
}
|
||||
|
||||
$thumbnail = Image::make(Storage::disk($disk)->get($path))->fit(80);
|
||||
|
||||
return $thumbnail->response();
|
||||
}
|
||||
|
||||
/**
|
||||
* Image preview
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function preview($disk, $path): mixed
|
||||
{
|
||||
$preview = Image::make(Storage::disk($disk)->get($path));
|
||||
|
||||
return $preview->response();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file URL
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function url($disk, $path): array
|
||||
{
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => null,
|
||||
],
|
||||
'url' => Storage::disk($disk)->url($path),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new directory
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
* @param $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createDirectory($disk, $path, $name)
|
||||
{
|
||||
$directoryName = $this->newPath($path, $name);
|
||||
|
||||
if (Storage::disk($disk)->exists($directoryName)) {
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'warning',
|
||||
'message' => 'dirExist',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
Storage::disk($disk)->makeDirectory($directoryName);
|
||||
$directoryProperties = $this->directoryProperties(
|
||||
$disk,
|
||||
$directoryName
|
||||
);
|
||||
|
||||
// add directory properties for the tree module
|
||||
$tree = $directoryProperties;
|
||||
$tree['props'] = ['hasSubdirectories' => false];
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'dirCreated',
|
||||
],
|
||||
'directory' => $directoryProperties,
|
||||
'tree' => [$tree],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new file
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
* @param $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createFile($disk, $path, $name): array
|
||||
{
|
||||
$path = $this->newPath($path, $name);
|
||||
|
||||
if (Storage::disk($disk)->exists($path)) {
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'warning',
|
||||
'message' => 'fileExist',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
Storage::disk($disk)->put($path, '');
|
||||
$fileProperties = $this->fileProperties($disk, $path);
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'fileCreated',
|
||||
],
|
||||
'file' => $fileProperties,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update file
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
* @param $file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function updateFile($disk, $path, $file): array
|
||||
{
|
||||
Storage::disk($disk)->putFileAs(
|
||||
$path,
|
||||
$file,
|
||||
$file->getClientOriginalName()
|
||||
);
|
||||
|
||||
$filePath = $this->newPath($path, $file->getClientOriginalName());
|
||||
$fileProperties = $this->fileProperties($disk, $filePath);
|
||||
|
||||
return [
|
||||
'result' => [
|
||||
'status' => 'success',
|
||||
'message' => 'fileUpdated',
|
||||
],
|
||||
'file' => $fileProperties,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream file - for audio and video
|
||||
*
|
||||
* @param $disk
|
||||
* @param $path
|
||||
*
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function streamFile($disk, $path): StreamedResponse
|
||||
{
|
||||
// if file name not in ASCII format
|
||||
if (!preg_match('/^[\x20-\x7e]*$/', basename($path))) {
|
||||
$filename = Str::ascii(basename($path));
|
||||
} else {
|
||||
$filename = basename($path);
|
||||
}
|
||||
|
||||
return Storage::disk($disk)->response($path, $filename, ['Accept-Ranges' => 'bytes']);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Middleware\FileManagerACL;
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ACLRepository;
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class FileManagerServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
// routes
|
||||
$this->loadRoutesFrom(__DIR__.'/routes.php');
|
||||
|
||||
// views
|
||||
$this->loadViewsFrom(__DIR__.'/../resources/views', 'file-manager');
|
||||
|
||||
// publish config
|
||||
$this->publishes([
|
||||
__DIR__
|
||||
.'/../config/file-manager.php' => config_path('file-manager.php'),
|
||||
], 'fm-config');
|
||||
|
||||
// publish views
|
||||
$this->publishes([
|
||||
__DIR__
|
||||
.'/../resources/views' => resource_path('views/vendor/file-manager'),
|
||||
], 'fm-views');
|
||||
|
||||
// publish js and css files - vue-file-manager module
|
||||
$this->publishes([
|
||||
__DIR__
|
||||
.'/../resources/assets' => public_path('vendor/file-manager'),
|
||||
], 'fm-assets');
|
||||
|
||||
// publish migrations
|
||||
$this->publishes([
|
||||
__DIR__
|
||||
.'/../migrations' => database_path('migrations'),
|
||||
], 'fm-migrations');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->mergeConfigFrom(
|
||||
__DIR__.'/../config/file-manager.php',
|
||||
'file-manager'
|
||||
);
|
||||
|
||||
// Config Repository
|
||||
$this->app->bind(
|
||||
ConfigRepository::class,
|
||||
$this->app['config']['file-manager.configRepository']
|
||||
);
|
||||
|
||||
// ACL Repository
|
||||
$this->app->bind(
|
||||
ACLRepository::class,
|
||||
$this->app->make(ConfigRepository::class)->getAclRepository()
|
||||
);
|
||||
|
||||
// register ACL middleware
|
||||
$this->app['router']->aliasMiddleware('fm-acl', FileManagerACL::class);
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Middleware;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ACL;
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
use Alexusmai\LaravelFileManager\Traits\PathTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Closure;
|
||||
|
||||
class FileManagerACL
|
||||
{
|
||||
use PathTrait;
|
||||
|
||||
/**
|
||||
* Check method names
|
||||
*/
|
||||
const CHECKERS = [
|
||||
'fm.tree' => 'checkContent',
|
||||
'fm.content' => 'checkContent',
|
||||
'fm.preview' => 'checkContent',
|
||||
'fm.thumbnails' => 'checkContent',
|
||||
'fm.url' => 'checkContent',
|
||||
'fm.stream-file' => 'checkContent',
|
||||
'fm.download' => 'checkDownload',
|
||||
'fm.create-file' => 'checkCreate',
|
||||
'fm.create-directory' => 'checkCreate',
|
||||
'fm.update-file' => 'checkUpdate',
|
||||
'fm.upload' => 'checkUpload',
|
||||
'fm.delete' => 'checkDelete',
|
||||
'fm.paste' => 'checkPaste',
|
||||
'fm.rename' => 'checkRename',
|
||||
'fm.zip' => 'checkZip',
|
||||
'fm.unzip' => 'checkUnzip',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $disk;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var ACL|mixed
|
||||
*/
|
||||
protected $acl;
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* FileManagerACL constructor.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ACL $acl
|
||||
*/
|
||||
public function __construct(Request $request, ACL $acl)
|
||||
{
|
||||
$this->disk = $request->has('disk') ? $request->input('disk') : null;
|
||||
$this->path = $request->has('path') ? $request->input('path') : '/';
|
||||
|
||||
$this->acl = $acl;
|
||||
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$routeName = $request->route()->getName();
|
||||
|
||||
// if ACL is OFF or route name wasn't found
|
||||
if ( ! resolve(ConfigRepository::class)->getAcl()
|
||||
|| ! array_key_exists($routeName, self::CHECKERS)
|
||||
) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ( ! call_user_func([$this, self::CHECKERS[$routeName]])) {
|
||||
return $this->errorMessage();
|
||||
}
|
||||
|
||||
// return request
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* ACL Error message
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function errorMessage()
|
||||
{
|
||||
return response()->json([
|
||||
'result' => [
|
||||
'status' => 'error',
|
||||
'message' => 'aclError',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check content actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkContent()
|
||||
{
|
||||
// need r access
|
||||
return ! ($this->acl->getAccessLevel($this->disk, $this->path) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check download actions
|
||||
*/
|
||||
protected function checkDownload()
|
||||
{
|
||||
// need r access
|
||||
abort_if(
|
||||
$this->acl->getAccessLevel($this->disk, $this->path) === 0,
|
||||
403
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check create actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkCreate()
|
||||
{
|
||||
$name = $this->request->input('name');
|
||||
$pathToWrite = $this->request->input('path')
|
||||
? $this->request->input('path').'/' : '';
|
||||
|
||||
// need r/w access
|
||||
return ! ($this->acl->getAccessLevel($this->disk, $pathToWrite.$name) !== 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check update actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkUpdate()
|
||||
{
|
||||
$pathToWrite = $this->request->input('path')
|
||||
? $this->request->input('path').'/' : '';
|
||||
|
||||
$name = $this->request->file('file')->getClientOriginalName();
|
||||
|
||||
// need r/w access
|
||||
return ! ($this->acl->getAccessLevel($this->disk, $pathToWrite.$name) !== 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check upload actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkUpload()
|
||||
{
|
||||
$pathToWrite = $this->request->input('path')
|
||||
? $this->request->input('path').'/' : '';
|
||||
|
||||
// filter
|
||||
$firstFall = Arr::first($this->request->file('files'),
|
||||
function ($value) use ($pathToWrite) {
|
||||
// need r/w access
|
||||
return $this->acl->getAccessLevel(
|
||||
$this->disk,
|
||||
$pathToWrite.$value->getClientOriginalName()
|
||||
) !== 2;
|
||||
}, null);
|
||||
|
||||
// if founded one access error
|
||||
if ($firstFall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check delete actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkDelete()
|
||||
{
|
||||
$firstFall = Arr::first($this->request->input('items'),
|
||||
function ($value) {
|
||||
// need r/w access
|
||||
return $this->acl->getAccessLevel($this->disk, $value['path']) !== 2;
|
||||
}, null);
|
||||
|
||||
if ($firstFall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check paste action
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkPaste()
|
||||
{
|
||||
// get clipboard data
|
||||
$clipboard = $this->request->input('clipboard');
|
||||
|
||||
// copy - r, cut - rw
|
||||
$getLevel = $clipboard['type'] === 'copy' ? 1 : 2;
|
||||
|
||||
// can user copy or cut selected files and folders
|
||||
$checkDirs = Arr::first($clipboard['directories'],
|
||||
function ($value) use ($clipboard, $getLevel) {
|
||||
return $this->acl->getAccessLevel($clipboard['disk'], $value) < $getLevel;
|
||||
}, null);
|
||||
|
||||
$checkFiles = Arr::first($clipboard['files'],
|
||||
function ($value) use ($clipboard, $getLevel) {
|
||||
return $this->acl->getAccessLevel($clipboard['disk'], $value) < $getLevel;
|
||||
}, null);
|
||||
|
||||
// can user write to selected folder?
|
||||
$writeToFolder = $this->acl->getAccessLevel($this->disk, $this->path);
|
||||
|
||||
return ! ($checkDirs || $checkFiles || $writeToFolder !== 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rename actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkRename()
|
||||
{
|
||||
// old path
|
||||
$oldPath = $this->request->input('oldName');
|
||||
|
||||
// new path
|
||||
$newPath = $this->request->input('newName');
|
||||
|
||||
// need r/w access
|
||||
return ! ($this->acl->getAccessLevel($this->disk, $oldPath) !== 2
|
||||
|| $this->acl->getAccessLevel($this->disk, $newPath) !== 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check zip actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkZip()
|
||||
{
|
||||
// can user write to selected folder?
|
||||
$writeToFolder = $this->acl->getAccessLevel(
|
||||
$this->disk,
|
||||
$this->newPath(
|
||||
$this->request->input('path'),
|
||||
$this->request->input('name')
|
||||
)
|
||||
);
|
||||
|
||||
// need r/w access
|
||||
if ($writeToFolder !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// data to zip
|
||||
$elements = $this->request->input('elements');
|
||||
|
||||
// can user read selected files and folders?
|
||||
$checkDirs = Arr::first($elements['directories'],
|
||||
function ($value) {
|
||||
// need r access
|
||||
return $this->acl->getAccessLevel($this->disk, $value) === 0;
|
||||
}, null);
|
||||
|
||||
|
||||
$checkFiles = Arr::first($elements['files'],
|
||||
function ($value) {
|
||||
// need r access
|
||||
return $this->acl->getAccessLevel($this->disk, $value) === 0;
|
||||
}, null);
|
||||
|
||||
return ! ($checkDirs || $checkFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check unzip actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkUnzip()
|
||||
{
|
||||
if ($this->request->input('folder')) {
|
||||
$dirname = dirname($this->path) === '.' ? ''
|
||||
: dirname($this->path).'/';
|
||||
$pathToWrite = $dirname.$this->request->input('folder');
|
||||
} else {
|
||||
$pathToWrite = dirname($this->path) === '.' ? '/'
|
||||
: dirname($this->path);
|
||||
}
|
||||
|
||||
return ! ($this->acl->getAccessLevel($this->disk, $pathToWrite) !== 2
|
||||
|| $this->acl->getAccessLevel($this->disk, $this->path) === 0);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Requests;
|
||||
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
trait CustomErrorMessage
|
||||
{
|
||||
/**
|
||||
* Validation error response
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$message = (method_exists($this, 'message'))
|
||||
? $this->container->call([$this, 'message'])
|
||||
: 'The given data was invalid.';
|
||||
|
||||
throw new HttpResponseException(response()->json([
|
||||
'errors' => $validator->errors(),
|
||||
'message' => $message,
|
||||
], 422));
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Requests;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class RequestValidator extends FormRequest
|
||||
{
|
||||
use CustomErrorMessage;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$config = resolve(ConfigRepository::class);
|
||||
|
||||
return [
|
||||
'disk' => [
|
||||
'sometimes',
|
||||
'string',
|
||||
function ($attribute, $value, $fail) use($config) {
|
||||
if (!in_array($value, $config->getDiskList()) ||
|
||||
!array_key_exists($value, config('filesystems.disks'))
|
||||
) {
|
||||
return $fail('diskNotFound');
|
||||
}
|
||||
},
|
||||
],
|
||||
'path' => [
|
||||
'sometimes',
|
||||
'string',
|
||||
'nullable',
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($value && !Storage::disk($this->input('disk'))->exists($value)
|
||||
) {
|
||||
return $fail('pathNotFound');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Not found message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return 'notFound';
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Services\ACLService;
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ACL
|
||||
{
|
||||
/**
|
||||
* @var ACLRepository
|
||||
*/
|
||||
public $aclRepository;
|
||||
|
||||
/**
|
||||
* @var ConfigRepository
|
||||
*/
|
||||
public $configRepository;
|
||||
|
||||
/**
|
||||
* ACL constructor.
|
||||
*
|
||||
* @param ACLRepository $aclRepository
|
||||
* @param ConfigRepository $configRepository
|
||||
*/
|
||||
public function __construct(
|
||||
ACLRepository $aclRepository,
|
||||
ConfigRepository $configRepository
|
||||
) {
|
||||
$this->aclRepository = $aclRepository;
|
||||
$this->configRepository = $configRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access level for selected path
|
||||
*
|
||||
* @param $disk
|
||||
* @param string $path
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAccessLevel($disk, $path = '/'): int
|
||||
{
|
||||
$rules = $this->rulesForDisk($disk);
|
||||
|
||||
// find the first rule where the paths are equal
|
||||
$firstRule = Arr::first($rules, function ($value) use ($path) {
|
||||
return $value['path'] === '*' || $value['path'] === $path;
|
||||
});
|
||||
|
||||
if ($firstRule) {
|
||||
return $firstRule['access'];
|
||||
}
|
||||
|
||||
// blacklist or whitelist (ACL strategy)
|
||||
return $this->configRepository->getAclStrategy() === 'blacklist' ? 2 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select rules for disk
|
||||
*
|
||||
* @param $disk
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function rulesForDisk($disk): array
|
||||
{
|
||||
return Arr::where($this->rulesList(),
|
||||
function ($value) use ($disk) {
|
||||
return $value['disk'] === $disk;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules list from ACL Repository
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function rulesList(): mixed
|
||||
{
|
||||
// if cache on
|
||||
if ($minutes = $this->configRepository->getAclRulesCache()) {
|
||||
$cacheName = get_class($this->aclRepository) . '_' .$this->aclRepository->getUserID();
|
||||
|
||||
return Cache::remember($cacheName, $minutes, function () {
|
||||
return $this->aclRepository->getRules();
|
||||
});
|
||||
}
|
||||
|
||||
return $this->aclRepository->getRules();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Alexusmai\LaravelFileManager\Services\ACLService;
|
||||
|
||||
/**
|
||||
* Interface ACLRepository
|
||||
*
|
||||
* @package Alexusmai\LaravelFileManager\Services\ACLService
|
||||
*/
|
||||
interface ACLRepository
|
||||
{
|
||||
/**
|
||||
* Get user ID
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserID();
|
||||
|
||||
/**
|
||||
* Get ACL rules list for user
|
||||
*
|
||||
* You need to return an array, like this:
|
||||
*
|
||||
* 0 => [
|
||||
* "disk" => "public"
|
||||
* "path" => "music"
|
||||
* "access" => 0
|
||||
* ],
|
||||
* 1 => [
|
||||
* "disk" => "public"
|
||||
* "path" => "images"
|
||||
* "access" => 1
|
||||
* ]
|
||||
*
|
||||
* OR [] - if no results for selected user
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRules(): array;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user