mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 23:27:17 +08:00
feat: s3fs 插件
This commit is contained in:
222
app/http/controllers/plugins/s3fs/s3fs_controller.go
Normal file
222
app/http/controllers/plugins/s3fs/s3fs_controller.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package s3fs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type S3fsController struct {
|
||||
setting services.Setting
|
||||
}
|
||||
|
||||
type s3fs struct {
|
||||
ID int64 `json:"id"`
|
||||
Path string `json:"path"`
|
||||
Bucket string `json:"bucket"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
func NewS3fsController() *S3fsController {
|
||||
return &S3fsController{
|
||||
setting: services.NewSettingImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// List 所有 S3fs 挂载
|
||||
func (c *S3fsController) List(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "s3fs") {
|
||||
return
|
||||
}
|
||||
|
||||
page := ctx.Request().QueryInt("page", 1)
|
||||
limit := ctx.Request().QueryInt("limit", 10)
|
||||
|
||||
var s3fsList []s3fs
|
||||
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
startIndex := (page - 1) * limit
|
||||
endIndex := page * limit
|
||||
if startIndex > len(s3fsList) {
|
||||
controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []s3fs{},
|
||||
})
|
||||
return
|
||||
}
|
||||
if endIndex > len(s3fsList) {
|
||||
endIndex = len(s3fsList)
|
||||
}
|
||||
pagedS3fsList := s3fsList[startIndex:endIndex]
|
||||
|
||||
controllers.Success(ctx, http.Json{
|
||||
"total": len(s3fsList),
|
||||
"items": pagedS3fsList,
|
||||
})
|
||||
}
|
||||
|
||||
// Add 添加 S3fs 挂载
|
||||
func (c *S3fsController) Add(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "s3fs") {
|
||||
return
|
||||
}
|
||||
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"ak": "required|regex:^[a-zA-Z0-9]*$",
|
||||
"sk": "required|regex:^[a-zA-Z0-9]*$",
|
||||
"bucket": "required|regex:^[a-zA-Z0-9_-]*$",
|
||||
"url": "required|full_url",
|
||||
"path": "required|regex:^/[a-zA-Z0-9_-]+$",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
ak := ctx.Request().Input("ak")
|
||||
sk := ctx.Request().Input("sk")
|
||||
path := ctx.Request().Input("path")
|
||||
bucket := ctx.Request().Input("bucket")
|
||||
url := ctx.Request().Input("url")
|
||||
|
||||
// 检查下地域节点中是否包含bucket,如果包含了,肯定是错误的
|
||||
if strings.Contains(url, bucket) {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "地域节点不能包含 Bucket 名称")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查挂载目录是否存在且为空
|
||||
if !tools.Exists(path) {
|
||||
tools.Mkdir(path, 0755)
|
||||
}
|
||||
if !tools.Empty(path) {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
|
||||
return
|
||||
}
|
||||
|
||||
var s3fsList []s3fs
|
||||
err = sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range s3fsList {
|
||||
if s.Path == path {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "路径已存在")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
id := carbon.Now().TimestampMilli()
|
||||
password := ak + ":" + sk
|
||||
tools.WriteFile("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600)
|
||||
tools.ExecShell(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
|
||||
check := tools.ExecShell("mount -a 2>&1")
|
||||
if len(check) != 0 {
|
||||
tools.ExecShell(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+check)
|
||||
return
|
||||
}
|
||||
check2 := tools.ExecShell("df -h | grep " + path + " 2>&1")
|
||||
if len(check2) == 0 {
|
||||
tools.ExecShell(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
|
||||
return
|
||||
}
|
||||
|
||||
s3fsList = append(s3fsList, s3fs{
|
||||
ID: id,
|
||||
Path: path,
|
||||
Bucket: bucket,
|
||||
Url: url,
|
||||
})
|
||||
json, err := sonic.MarshalString(s3fsList)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
err = c.setting.Set("s3fs", json)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Delete 删除 S3fs 挂载
|
||||
func (c *S3fsController) Delete(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "s3fs") {
|
||||
return
|
||||
}
|
||||
|
||||
id := ctx.Request().Input("id")
|
||||
if len(id) == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var s3fsList []s3fs
|
||||
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
var mount s3fs
|
||||
for _, s := range s3fsList {
|
||||
if cast.ToString(s.ID) == id {
|
||||
mount = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if mount.ID == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在")
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell(`fusermount -u '` + mount.Path + `'`)
|
||||
tools.ExecShell(`umount '` + mount.Path + `'`)
|
||||
tools.ExecShell(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`)
|
||||
check := tools.ExecShell("mount -a 2>&1")
|
||||
if len(check) != 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+check)
|
||||
return
|
||||
}
|
||||
tools.RemoveFile("/etc/passwd-s3fs-" + cast.ToString(mount.ID))
|
||||
|
||||
var newS3fsList []s3fs
|
||||
for _, s := range s3fsList {
|
||||
if s.ID != mount.ID {
|
||||
newS3fsList = append(newS3fsList, s)
|
||||
}
|
||||
}
|
||||
json, err := sonic.MarshalString(newS3fsList)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
err = c.setting.Set("s3fs", json)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
14
app/plugins/s3fs/s3fs.go
Normal file
14
app/plugins/s3fs/s3fs.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package s3fs
|
||||
|
||||
var (
|
||||
Name = "S3fs"
|
||||
Author = "耗子"
|
||||
Description = "S3fs 通过 FUSE 挂载兼容 S3 标准的存储桶,例如Amazon S3、阿里云OSS、腾讯云COS、七牛云Kodo等。"
|
||||
Slug = "s3fs"
|
||||
Version = "1.9"
|
||||
Requires = []string{}
|
||||
Excludes = []string{}
|
||||
Install = `bash /www/panel/scripts/s3fs/install.sh`
|
||||
Uninstall = `bash /www/panel/scripts/s3fs/uninstall.sh`
|
||||
Update = `bash /www/panel/scripts/s3fs/install.sh`
|
||||
)
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"panel/app/plugins/php74"
|
||||
"panel/app/plugins/php80"
|
||||
"panel/app/plugins/phpmyadmin"
|
||||
"panel/app/plugins/s3fs"
|
||||
)
|
||||
|
||||
// PanelPlugin 插件元数据结构
|
||||
@@ -127,6 +128,18 @@ func (r *PluginImpl) All() []PanelPlugin {
|
||||
Uninstall: phpmyadmin.Uninstall,
|
||||
Update: phpmyadmin.Update,
|
||||
})
|
||||
p = append(p, PanelPlugin{
|
||||
Name: s3fs.Name,
|
||||
Author: s3fs.Author,
|
||||
Description: s3fs.Description,
|
||||
Slug: s3fs.Slug,
|
||||
Version: s3fs.Version,
|
||||
Requires: s3fs.Requires,
|
||||
Excludes: s3fs.Excludes,
|
||||
Install: s3fs.Install,
|
||||
Uninstall: s3fs.Uninstall,
|
||||
Update: s3fs.Update,
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -5,36 +5,10 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Empty 类似于 PHP 的 empty() 函数
|
||||
func Empty(val interface{}) bool {
|
||||
if val == nil {
|
||||
return true
|
||||
}
|
||||
v := reflect.ValueOf(val)
|
||||
switch v.Kind() {
|
||||
case reflect.String, reflect.Array:
|
||||
return v.Len() == 0
|
||||
case reflect.Map, reflect.Slice:
|
||||
return v.Len() == 0 || v.IsNil()
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
// FirstElement 安全地获取 args[0],避免 panic: runtime error: index out of range
|
||||
func FirstElement(args []string) string {
|
||||
if len(args) > 0 {
|
||||
|
||||
@@ -14,23 +14,6 @@ func TestStringHelperTestSuite(t *testing.T) {
|
||||
suite.Run(t, &StringHelperTestSuite{})
|
||||
}
|
||||
|
||||
func (s *StringHelperTestSuite) TestEmpty() {
|
||||
s.True(Empty(""))
|
||||
s.True(Empty(nil))
|
||||
s.True(Empty([]string{}))
|
||||
s.True(Empty(map[string]string{}))
|
||||
s.True(Empty(0))
|
||||
s.True(Empty(0.0))
|
||||
s.True(Empty(false))
|
||||
|
||||
s.False(Empty(" "))
|
||||
s.False(Empty([]string{"Panel"}))
|
||||
s.False(Empty(map[string]string{"Panel": "HaoZi"}))
|
||||
s.False(Empty(1))
|
||||
s.False(Empty(1.0))
|
||||
s.False(Empty(true))
|
||||
}
|
||||
|
||||
func (s *StringHelperTestSuite) TestFirstElement() {
|
||||
s.Equal("HaoZi", FirstElement([]string{"HaoZi"}))
|
||||
}
|
||||
|
||||
@@ -114,3 +114,13 @@ func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Empty 判断路径是否为空
|
||||
func Empty(path string) bool {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return len(files) == 0
|
||||
}
|
||||
|
||||
@@ -99,3 +99,8 @@ func (s *SystemHelperTestSuite) TestExists() {
|
||||
s.True(Exists("/tmp"))
|
||||
s.False(Exists("/tmp/123"))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestEmpty() {
|
||||
s.True(Empty("/tmp/123"))
|
||||
s.False(Empty("/tmp"))
|
||||
}
|
||||
|
||||
102
public/panel/views/plugins/s3fs.html
Normal file
102
public/panel/views/plugins/s3fs.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<!--
|
||||
Name: S3fs
|
||||
Author: 耗子
|
||||
Date: 2023-07-25
|
||||
-->
|
||||
<title>S3fs</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">S3fs 挂载列表</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="s3fs-list" lay-filter="s3fs-list"></table>
|
||||
<!-- 顶部工具栏 -->
|
||||
<script type="text/html" id="s3fs-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_mount">新建挂载</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 右侧管理 -->
|
||||
<script type="text/html" id="s3fs-list-control">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="umount">卸载</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['index', 'code', 'table'], function () {
|
||||
let admin = layui.admin
|
||||
, table = layui.table
|
||||
, form = layui.form
|
||||
, view = layui.view;
|
||||
|
||||
// 获取备份任务列表
|
||||
table.render({
|
||||
elem: '#s3fs-list'
|
||||
, url: '/api/plugins/s3fs/list'
|
||||
, toolbar: '#s3fs-list-bar'
|
||||
, title: 'S3fs挂载列表'
|
||||
, cols: [[
|
||||
{field: 'id', hide: true, title: 'ID', sort: true}
|
||||
, {field: 'bucket', title: 'Bucket', fixed: 'left', unresize: true, sort: true}
|
||||
, {field: 'url', title: 'URL', sort: true}
|
||||
, {field: 'path', title: '挂载目录', sort: true}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#s3fs-list-control', width: 150}
|
||||
]]
|
||||
, page: true
|
||||
, text: {
|
||||
none: '无挂载'
|
||||
}
|
||||
, parseData: function (res) {
|
||||
return {
|
||||
"code": res.code,
|
||||
"msg": res.message,
|
||||
"count": res.data.total,
|
||||
"data": res.data.items
|
||||
};
|
||||
}
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(s3fs-list)', function (obj) {
|
||||
if (obj.event === 'add_mount') {
|
||||
admin.popup({
|
||||
title: '新建挂载'
|
||||
, area: ['600px', '600px']
|
||||
, id: 'LAY-popup-s3fs-mount-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/s3fs/add_mount', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-s3fs-mount-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(s3fs-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'umount') {
|
||||
layer.confirm('确定要卸载 <b style="color: red;">' + data.path + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/s3fs/delete"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert(data.path + ' 卸载成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
104
public/panel/views/plugins/s3fs/add_mount.html
Normal file
104
public/panel/views/plugins/s3fs/add_mount.html
Normal file
@@ -0,0 +1,104 @@
|
||||
<!--
|
||||
Name: S3fs - 新增挂载
|
||||
Author: 耗子
|
||||
Date: 2023-07-25
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-mount-s3fs-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Bucket</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="bucket" id="add-mount-s3fs-bucket"
|
||||
lay-verify="required" placeholder="请输入Bucket名" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">输入Bucket名字,腾讯云COS为(xxxx-用户ID)</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">AK</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="ak" id="add-mount-s3fs-ak"
|
||||
lay-verify="required" placeholder="请输入AK密钥" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">访问密钥中的Access Key,需具备Bucket操作权限,腾讯云为SecretId
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">SK</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="sk" id="add-mount-s3fs-sk"
|
||||
lay-verify="required" placeholder="请输入SK密钥" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">访问密钥中的Access Key
|
||||
Secret,需具备Bucket操作权限,腾讯云为SecretKey
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">地域节点</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="url" id="add-mount-s3fs-url"
|
||||
lay-verify="required" placeholder="请输入Bucket地域节点" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">
|
||||
地域节点可在<a target="_blank" href="https://github.com/s3fs-fuse/s3fs-fuse/wiki/Non-Amazon-S3">https://github.com/s3fs-fuse/s3fs-fuse/wiki/Non-Amazon-S3</a>查找
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">挂载目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="path" id="add-mount-s3fs-path"
|
||||
lay-verify="required" placeholder="请输入挂载目录" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">挂载目录,如/data,不存在将会自动创建</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-mount-s3fs-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'jquery', 'cron'], function () {
|
||||
var admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table;
|
||||
|
||||
form.render();
|
||||
|
||||
form.on('submit(add-mount-s3fs-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/s3fs/add"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
table.reload('s3fs-list');
|
||||
layer.alert('S3fs挂载已提交,请自行检查是否挂载成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"panel/app/http/controllers/plugins/php74"
|
||||
"panel/app/http/controllers/plugins/php80"
|
||||
"panel/app/http/controllers/plugins/phpmyadmin"
|
||||
"panel/app/http/controllers/plugins/s3fs"
|
||||
"panel/app/http/middleware"
|
||||
)
|
||||
|
||||
@@ -129,4 +130,10 @@ func Plugin() {
|
||||
route.Get("info", phpMyAdminController.Info)
|
||||
route.Post("port", phpMyAdminController.SetPort)
|
||||
})
|
||||
facades.Route().Prefix("api/plugins/s3fs").Middleware(middleware.Jwt()).Group(func(route route.Route) {
|
||||
s3fsController := s3fs.NewS3fsController()
|
||||
route.Get("list", s3fsController.List)
|
||||
route.Post("add", s3fsController.Add)
|
||||
route.Post("delete", s3fsController.Delete)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,4 +30,4 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
panel writePlugin s3fs
|
||||
panel writePlugin s3fs 1.9
|
||||
|
||||
Reference in New Issue
Block a user