mirror of
https://github.com/veops/oneterm.git
synced 2025-10-09 17:20:16 +08:00
feat: file manager
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||||
package docs
|
package docs
|
||||||
|
|
||||||
import (
|
import "github.com/swaggo/swag"
|
||||||
"github.com/swaggo/swag"
|
|
||||||
)
|
|
||||||
|
|
||||||
const docTemplate = `{
|
const docTemplate = `{
|
||||||
"schemes": {{ marshal .Schemes }},
|
"schemes": {{ marshal .Schemes }},
|
||||||
@@ -38,7 +36,7 @@ const docTemplate = `{
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or account",
|
"description": "name or account",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -211,7 +209,7 @@ const docTemplate = `{
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or ip",
|
"description": "name or ip",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -485,7 +483,7 @@ const docTemplate = `{
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or cmds",
|
"description": "name or cmds",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -719,6 +717,12 @@ const docTemplate = `{
|
|||||||
"description": "height",
|
"description": "height",
|
||||||
"name": "h",
|
"name": "h",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "dpi",
|
||||||
|
"name": "dpi",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -733,7 +737,7 @@ const docTemplate = `{
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"data": {
|
"data": {
|
||||||
"$ref": "#/definitions/model.Session"
|
"$ref": "#/definitions/session.Session"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -749,6 +753,24 @@ const docTemplate = `{
|
|||||||
"connect"
|
"connect"
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "width",
|
||||||
|
"name": "w",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "height",
|
||||||
|
"name": "h",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "dpi",
|
||||||
|
"name": "dpi",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "session id",
|
"description": "session id",
|
||||||
@@ -797,6 +819,271 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/file/download/:asset_id/:account_id": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "filename",
|
||||||
|
"name": "filename",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/history": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page_index",
|
||||||
|
"name": "page_index",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page_size",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "search",
|
||||||
|
"name": "search",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "saction",
|
||||||
|
"name": "action",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "start, RFC3339",
|
||||||
|
"name": "start",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "end, RFC3339",
|
||||||
|
"name": "end",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "uid",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account id",
|
||||||
|
"name": "accout_id",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "client_ip",
|
||||||
|
"name": "client_ip",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/controller.ListData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.Session"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/ls/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/mkdir/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir ",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/upload/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "path",
|
||||||
|
"name": "path",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/gateway": {
|
"/gateway": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -818,7 +1105,7 @@ const docTemplate = `{
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or host or account or port",
|
"description": "name or host or account or port",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -1033,6 +1320,12 @@ const docTemplate = `{
|
|||||||
"description": "end time, RFC3339",
|
"description": "end time, RFC3339",
|
||||||
"name": "end",
|
"name": "end",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "search",
|
||||||
|
"name": "search",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -1296,7 +1589,7 @@ const docTemplate = `{
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or mac",
|
"description": "name or mac",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -2613,6 +2906,9 @@ const docTemplate = `{
|
|||||||
"count": {
|
"count": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -2681,6 +2977,68 @@ const docTemplate = `{
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"session.Session": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"account_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"account_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"asset_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"asset_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"client_ip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"closed_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cmd_count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"gateway_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"gateway_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"session_type": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"uid": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"user_name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or account",
|
"description": "name or account",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -198,7 +198,7 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or ip",
|
"description": "name or ip",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -472,7 +472,7 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or cmds",
|
"description": "name or cmds",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -706,6 +706,12 @@
|
|||||||
"description": "height",
|
"description": "height",
|
||||||
"name": "h",
|
"name": "h",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "dpi",
|
||||||
|
"name": "dpi",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -720,7 +726,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"data": {
|
"data": {
|
||||||
"$ref": "#/definitions/model.Session"
|
"$ref": "#/definitions/session.Session"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -736,6 +742,24 @@
|
|||||||
"connect"
|
"connect"
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "width",
|
||||||
|
"name": "w",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "height",
|
||||||
|
"name": "h",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "dpi",
|
||||||
|
"name": "dpi",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "session id",
|
"description": "session id",
|
||||||
@@ -784,6 +808,271 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/file/download/:asset_id/:account_id": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "filename",
|
||||||
|
"name": "filename",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/history": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page_index",
|
||||||
|
"name": "page_index",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page_size",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "search",
|
||||||
|
"name": "search",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "saction",
|
||||||
|
"name": "action",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "start, RFC3339",
|
||||||
|
"name": "start",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "end, RFC3339",
|
||||||
|
"name": "end",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "uid",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account id",
|
||||||
|
"name": "accout_id",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "client_ip",
|
||||||
|
"name": "client_ip",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/controller.ListData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.Session"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/ls/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/mkdir/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "dir ",
|
||||||
|
"name": "dir",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/file/upload/:asset_id/:account_id": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "asset_id",
|
||||||
|
"name": "asset_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "account_id",
|
||||||
|
"name": "account_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "path",
|
||||||
|
"name": "path",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controller.HttpResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/gateway": {
|
"/gateway": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -805,7 +1094,7 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or host or account or port",
|
"description": "name or host or account or port",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -1020,6 +1309,12 @@
|
|||||||
"description": "end time, RFC3339",
|
"description": "end time, RFC3339",
|
||||||
"name": "end",
|
"name": "end",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "search",
|
||||||
|
"name": "search",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -1283,7 +1578,7 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "string",
|
||||||
"description": "name or mac",
|
"description": "name or mac",
|
||||||
"name": "search",
|
"name": "search",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
@@ -2600,6 +2895,9 @@
|
|||||||
"count": {
|
"count": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -2668,6 +2966,68 @@
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"session.Session": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"account_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"account_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"asset_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"asset_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"client_ip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"closed_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cmd_count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"gateway_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"gateway_info": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"session_type": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"uid": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"user_name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -372,6 +372,8 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
count:
|
count:
|
||||||
type: integer
|
type: integer
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
@@ -417,6 +419,47 @@ definitions:
|
|||||||
type_id:
|
type_id:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
session.Session:
|
||||||
|
properties:
|
||||||
|
account_id:
|
||||||
|
type: integer
|
||||||
|
account_info:
|
||||||
|
type: string
|
||||||
|
asset_id:
|
||||||
|
type: integer
|
||||||
|
asset_info:
|
||||||
|
type: string
|
||||||
|
client_ip:
|
||||||
|
type: string
|
||||||
|
closed_at:
|
||||||
|
type: string
|
||||||
|
cmd_count:
|
||||||
|
type: integer
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
duration:
|
||||||
|
type: integer
|
||||||
|
gateway_id:
|
||||||
|
type: integer
|
||||||
|
gateway_info:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
protocol:
|
||||||
|
type: string
|
||||||
|
session_id:
|
||||||
|
type: string
|
||||||
|
session_type:
|
||||||
|
type: integer
|
||||||
|
status:
|
||||||
|
type: integer
|
||||||
|
uid:
|
||||||
|
type: integer
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
user_name:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
paths:
|
paths:
|
||||||
@@ -436,7 +479,7 @@ paths:
|
|||||||
- description: name or account
|
- description: name or account
|
||||||
in: query
|
in: query
|
||||||
name: search
|
name: search
|
||||||
type: integer
|
type: string
|
||||||
- description: account id
|
- description: account id
|
||||||
in: query
|
in: query
|
||||||
name: id
|
name: id
|
||||||
@@ -542,7 +585,7 @@ paths:
|
|||||||
- description: name or ip
|
- description: name or ip
|
||||||
in: query
|
in: query
|
||||||
name: search
|
name: search
|
||||||
type: integer
|
type: string
|
||||||
- description: asset id
|
- description: asset id
|
||||||
in: query
|
in: query
|
||||||
name: id
|
name: id
|
||||||
@@ -708,7 +751,7 @@ paths:
|
|||||||
- description: name or cmds
|
- description: name or cmds
|
||||||
in: query
|
in: query
|
||||||
name: search
|
name: search
|
||||||
type: integer
|
type: string
|
||||||
- description: command id
|
- description: command id
|
||||||
in: query
|
in: query
|
||||||
name: id
|
name: id
|
||||||
@@ -847,6 +890,10 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: h
|
name: h
|
||||||
type: integer
|
type: integer
|
||||||
|
- description: dpi
|
||||||
|
in: query
|
||||||
|
name: dpi
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
@@ -855,13 +902,25 @@ paths:
|
|||||||
- $ref: '#/definitions/controller.HttpResponse'
|
- $ref: '#/definitions/controller.HttpResponse'
|
||||||
- properties:
|
- properties:
|
||||||
data:
|
data:
|
||||||
$ref: '#/definitions/model.Session'
|
$ref: '#/definitions/session.Session'
|
||||||
type: object
|
type: object
|
||||||
tags:
|
tags:
|
||||||
- connect
|
- connect
|
||||||
/connect/:session_id:
|
/connect/:session_id:
|
||||||
get:
|
get:
|
||||||
parameters:
|
parameters:
|
||||||
|
- description: width
|
||||||
|
in: query
|
||||||
|
name: w
|
||||||
|
type: integer
|
||||||
|
- description: height
|
||||||
|
in: query
|
||||||
|
name: h
|
||||||
|
type: integer
|
||||||
|
- description: dpi
|
||||||
|
in: query
|
||||||
|
name: dpi
|
||||||
|
type: integer
|
||||||
- description: session id
|
- description: session id
|
||||||
in: path
|
in: path
|
||||||
name: session_id
|
name: session_id
|
||||||
@@ -892,6 +951,175 @@ paths:
|
|||||||
$ref: '#/definitions/controller.HttpResponse'
|
$ref: '#/definitions/controller.HttpResponse'
|
||||||
tags:
|
tags:
|
||||||
- connect
|
- connect
|
||||||
|
/file/download/:asset_id/:account_id:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: asset_id
|
||||||
|
in: path
|
||||||
|
name: asset_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: account_id
|
||||||
|
in: path
|
||||||
|
name: account_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: dir
|
||||||
|
in: query
|
||||||
|
name: dir
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- description: filename
|
||||||
|
in: query
|
||||||
|
name: filename
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controller.HttpResponse'
|
||||||
|
tags:
|
||||||
|
- file
|
||||||
|
/file/history:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: page_index
|
||||||
|
in: query
|
||||||
|
name: page_index
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: page_size
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: search
|
||||||
|
in: query
|
||||||
|
name: search
|
||||||
|
type: string
|
||||||
|
- description: saction
|
||||||
|
in: query
|
||||||
|
name: action
|
||||||
|
type: integer
|
||||||
|
- description: start, RFC3339
|
||||||
|
in: query
|
||||||
|
name: start
|
||||||
|
type: string
|
||||||
|
- description: end, RFC3339
|
||||||
|
in: query
|
||||||
|
name: end
|
||||||
|
type: string
|
||||||
|
- description: uid
|
||||||
|
in: query
|
||||||
|
name: uid
|
||||||
|
type: integer
|
||||||
|
- description: asset id
|
||||||
|
in: query
|
||||||
|
name: asset_id
|
||||||
|
type: integer
|
||||||
|
- description: account id
|
||||||
|
in: query
|
||||||
|
name: accout_id
|
||||||
|
type: integer
|
||||||
|
- description: client_ip
|
||||||
|
in: query
|
||||||
|
name: client_ip
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/controller.HttpResponse'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/controller.ListData'
|
||||||
|
- properties:
|
||||||
|
list:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/model.Session'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
tags:
|
||||||
|
- file
|
||||||
|
/file/ls/:asset_id/:account_id:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: asset_id
|
||||||
|
in: path
|
||||||
|
name: asset_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: account_id
|
||||||
|
in: path
|
||||||
|
name: account_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: dir
|
||||||
|
in: query
|
||||||
|
name: dir
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controller.HttpResponse'
|
||||||
|
tags:
|
||||||
|
- file
|
||||||
|
/file/mkdir/:asset_id/:account_id:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: asset_id
|
||||||
|
in: path
|
||||||
|
name: asset_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: account_id
|
||||||
|
in: path
|
||||||
|
name: account_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 'dir '
|
||||||
|
in: query
|
||||||
|
name: dir
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controller.HttpResponse'
|
||||||
|
tags:
|
||||||
|
- account
|
||||||
|
/file/upload/:asset_id/:account_id:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: asset_id
|
||||||
|
in: path
|
||||||
|
name: asset_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: account_id
|
||||||
|
in: path
|
||||||
|
name: account_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: path
|
||||||
|
in: query
|
||||||
|
name: path
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controller.HttpResponse'
|
||||||
|
tags:
|
||||||
|
- file
|
||||||
/gateway:
|
/gateway:
|
||||||
get:
|
get:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -908,7 +1136,7 @@ paths:
|
|||||||
- description: name or host or account or port
|
- description: name or host or account or port
|
||||||
in: query
|
in: query
|
||||||
name: search
|
name: search
|
||||||
type: integer
|
type: string
|
||||||
- description: gateway id
|
- description: gateway id
|
||||||
in: query
|
in: query
|
||||||
name: id
|
name: id
|
||||||
@@ -1042,6 +1270,10 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: end
|
name: end
|
||||||
type: string
|
type: string
|
||||||
|
- description: search
|
||||||
|
in: query
|
||||||
|
name: search
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
@@ -1199,7 +1431,7 @@ paths:
|
|||||||
- description: name or mac
|
- description: name or mac
|
||||||
in: query
|
in: query
|
||||||
name: search
|
name: search
|
||||||
type: integer
|
type: string
|
||||||
- description: publicKey id
|
- description: publicKey id
|
||||||
in: query
|
in: query
|
||||||
name: id
|
name: id
|
||||||
|
@@ -10,10 +10,12 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"go.uber.org/zap"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/veops/oneterm/pkg/conf"
|
"github.com/veops/oneterm/pkg/conf"
|
||||||
|
"github.com/veops/oneterm/pkg/logger"
|
||||||
"github.com/veops/oneterm/pkg/server/auth/acl"
|
"github.com/veops/oneterm/pkg/server/auth/acl"
|
||||||
"github.com/veops/oneterm/pkg/server/model"
|
"github.com/veops/oneterm/pkg/server/model"
|
||||||
"github.com/veops/oneterm/pkg/server/storage/db/mysql"
|
"github.com/veops/oneterm/pkg/server/storage/db/mysql"
|
||||||
@@ -151,3 +153,15 @@ func GetAutorizationResourceIds(ctx *gin.Context) (resourceIds []int, err error)
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HasAuthorization(ctx *gin.Context) (ok bool) {
|
||||||
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
rs, err := acl.GetRoleResources(ctx, currentUser.Acl.Rid, conf.RESOURCE_AUTHORIZATION)
|
||||||
|
if err != nil {
|
||||||
|
logger.L.Error("check authorization failed", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
k := fmt.Sprintf("%d-%d", ctx.Param("asset_id"), ctx.Param("account_id"))
|
||||||
|
_, ok = lo.Find(rs, func(r *acl.Resource) bool { return k == r.Name })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
// GetFileHistory godoc
|
// GetFileHistory godoc
|
||||||
//
|
//
|
||||||
// @Tags session
|
// @Tags file
|
||||||
// @Param page_index query int true "page_index"
|
// @Param page_index query int true "page_index"
|
||||||
// @Param page_size query int true "page_size"
|
// @Param page_size query int true "page_size"
|
||||||
// @Param search query string false "search"
|
// @Param search query string false "search"
|
||||||
@@ -53,21 +53,27 @@ func (c *Controller) GetFileHistory(ctx *gin.Context) {
|
|||||||
|
|
||||||
// FileLS godoc
|
// FileLS godoc
|
||||||
//
|
//
|
||||||
// @Tags account
|
// @Tags file
|
||||||
// @Param asset_id path int true "asset_id"
|
// @Param asset_id path int true "asset_id"
|
||||||
// @Param account_id path int true "account_id"
|
// @Param account_id path int true "account_id"
|
||||||
// @Param dir query string true "dir"
|
// @Param dir query string true "dir"
|
||||||
// @Success 200 {object} HttpResponse
|
// @Success 200 {object} HttpResponse
|
||||||
// @Router /file/ls/:asset_id/:account_id [post]
|
// @Router /file/ls/:asset_id/:account_id [post]
|
||||||
func (c *Controller) FileLS(ctx *gin.Context) {
|
func (c *Controller) FileLS(ctx *gin.Context) {
|
||||||
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
if !acl.IsAdmin(currentUser) && !HasAuthorization(ctx) {
|
||||||
|
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrInternal, Data: map[string]any{}})
|
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrInternal, Data: map[string]any{"err": err}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
info, err := cli.ReadDir(ctx.Query("dir"))
|
info, err := cli.ReadDir(ctx.Query("dir"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrInvalidArgument, Data: map[string]any{"err": err}})
|
ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +91,7 @@ func (c *Controller) FileLS(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusOK, NewHttpResponseWithData(res))
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileMkdir godoc
|
// FileMkdir file
|
||||||
//
|
//
|
||||||
// @Tags account
|
// @Tags account
|
||||||
// @Param asset_id path int true "asset_id"
|
// @Param asset_id path int true "asset_id"
|
||||||
@@ -95,6 +101,10 @@ func (c *Controller) FileLS(ctx *gin.Context) {
|
|||||||
// @Router /file/mkdir/:asset_id/:account_id [post]
|
// @Router /file/mkdir/:asset_id/:account_id [post]
|
||||||
func (c *Controller) FileMkdir(ctx *gin.Context) {
|
func (c *Controller) FileMkdir(ctx *gin.Context) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
if !acl.IsAdmin(currentUser) && !HasAuthorization(ctx) {
|
||||||
|
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,7 +133,7 @@ func (c *Controller) FileMkdir(ctx *gin.Context) {
|
|||||||
|
|
||||||
// FileUpload godoc
|
// FileUpload godoc
|
||||||
//
|
//
|
||||||
// @Tags account
|
// @Tags file
|
||||||
// @Param asset_id path int true "asset_id"
|
// @Param asset_id path int true "asset_id"
|
||||||
// @Param account_id path int true "account_id"
|
// @Param account_id path int true "account_id"
|
||||||
// @Param path query string true "path"
|
// @Param path query string true "path"
|
||||||
@@ -131,6 +141,10 @@ func (c *Controller) FileMkdir(ctx *gin.Context) {
|
|||||||
// @Router /file/upload/:asset_id/:account_id [post]
|
// @Router /file/upload/:asset_id/:account_id [post]
|
||||||
func (c *Controller) FileUpload(ctx *gin.Context) {
|
func (c *Controller) FileUpload(ctx *gin.Context) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
if !acl.IsAdmin(currentUser) && !HasAuthorization(ctx) {
|
||||||
|
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
f, fh, err := ctx.Request.FormFile("file")
|
f, fh, err := ctx.Request.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,15 +192,19 @@ func (c *Controller) FileUpload(ctx *gin.Context) {
|
|||||||
|
|
||||||
// FileDownload godoc
|
// FileDownload godoc
|
||||||
//
|
//
|
||||||
// @Tags account
|
// @Tags file
|
||||||
// @Param asset_id path int true "asset_id"
|
// @Param asset_id path int true "asset_id"
|
||||||
// @Param account_id path int true "account_id"
|
// @Param account_id path int true "account_id"
|
||||||
// @Param dir query string true "dir"
|
// @Param dir query string true "dir"
|
||||||
// @Param failename query string true "filename"
|
// @Param filename query string true "filename"
|
||||||
// @Success 200 {object} HttpResponse
|
// @Success 200 {object} HttpResponse
|
||||||
// @Router /file/ls/:asset_id/:account_id [get]
|
// @Router /file/download/:asset_id/:account_id [get]
|
||||||
func (c *Controller) FileDownload(ctx *gin.Context) {
|
func (c *Controller) FileDownload(ctx *gin.Context) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
if !acl.IsAdmin(currentUser) && !HasAuthorization(ctx) {
|
||||||
|
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
cli, err := file.GetFileManager().GetFileClient(cast.ToInt(ctx.Param("asset_id")), cast.ToInt(ctx.Param("account_id")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
ggateway "github.com/veops/oneterm/pkg/server/global/gateway"
|
ggateway "github.com/veops/oneterm/pkg/server/global/gateway"
|
||||||
"github.com/veops/oneterm/pkg/server/model"
|
"github.com/veops/oneterm/pkg/server/model"
|
||||||
"github.com/veops/oneterm/pkg/server/storage/db/mysql"
|
"github.com/veops/oneterm/pkg/server/storage/db/mysql"
|
||||||
|
"github.com/veops/oneterm/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -80,18 +81,33 @@ func (fm *FileManager) GetFileClient(assetId, accountId int) (cli *sftp.Client,
|
|||||||
if err = mysql.DB.Model(account).Where("id = ?", accountId).First(account).Error; err != nil {
|
if err = mysql.DB.Model(account).Where("id = ?", accountId).First(account).Error; err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
account.Password = util.DecryptAES(account.Password)
|
||||||
|
account.Pk = util.DecryptAES(account.Pk)
|
||||||
|
account.Phrase = util.DecryptAES(account.Phrase)
|
||||||
if asset.GatewayId != 0 {
|
if asset.GatewayId != 0 {
|
||||||
if err = mysql.DB.Model(gateway).Where("id = ?", asset.GatewayId).First(gateway).Error; err != nil {
|
if err = mysql.DB.Model(gateway).Where("id = ?", asset.GatewayId).First(gateway).Error; err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
gateway.Password = util.DecryptAES(gateway.Password)
|
||||||
|
gateway.Pk = util.DecryptAES(gateway.Pk)
|
||||||
|
gateway.Phrase = util.DecryptAES(gateway.Phrase)
|
||||||
}
|
}
|
||||||
ip, port := asset.Ip, 22
|
ip, port := asset.Ip, 0
|
||||||
for _, p := range asset.Protocols {
|
for _, p := range asset.Protocols {
|
||||||
if strings.HasPrefix(p, "sftp") {
|
if strings.HasPrefix(p, "sftp") {
|
||||||
port = cast.ToInt(strings.Split(p, ":")[1])
|
port = cast.ToInt(strings.Split(p, ":")[1])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if port == 0 {
|
||||||
|
for _, p := range asset.Protocols {
|
||||||
|
if strings.HasPrefix(p, "ssh") {
|
||||||
|
port = cast.ToInt(strings.Split(p, ":")[1])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if asset.GatewayId != 0 {
|
if asset.GatewayId != 0 {
|
||||||
sid, _ := uuid.NewUUID()
|
sid, _ := uuid.NewUUID()
|
||||||
var g *ggateway.GatewayTunnel
|
var g *ggateway.GatewayTunnel
|
||||||
@@ -106,7 +122,7 @@ func (fm *FileManager) GetFileClient(assetId, accountId int) (cli *sftp.Client,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
sshCli, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", ip, port), &ssh.ClientConfig{
|
sshCli, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", ip, port), &ssh.ClientConfig{
|
||||||
User: gateway.Account,
|
User: account.Account,
|
||||||
Auth: []ssh.AuthMethod{auth},
|
Auth: []ssh.AuthMethod{auth},
|
||||||
Timeout: time.Second * 3,
|
Timeout: time.Second * 3,
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
@@ -1,78 +1,31 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SESSIONTYPE_WEB = iota + 1
|
FILE_ACTION_LS = iota + 1
|
||||||
SESSIONTYPE_CLIENT
|
FILE_ACTION_MKDIR
|
||||||
|
FILE_ACTION_UPLOAD
|
||||||
|
FILE_ACTION_DOWNLOAD
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type FileHistory struct {
|
||||||
SESSIONSTATUS_ONLINE = iota + 1
|
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||||
SESSIONSTATUS_OFFLINE
|
Uid int `json:"uid" gorm:"column:uid"`
|
||||||
)
|
UserName string `json:"user_name" gorm:"column:user_name"`
|
||||||
|
AssetId int `json:"asset_id" gorm:"column:asset_id"`
|
||||||
const (
|
AccountId int `json:"account_id" gorm:"column:account_id"`
|
||||||
SESSIONACTION_NEW = iota + 1
|
ClientIp string `json:"client_ip" gorm:"column:client_ip"`
|
||||||
SESSIONACTION_MONITOR
|
Action int `json:"action" gorm:"column:action"`
|
||||||
SESSIONACTION_CLOSE
|
Dir string `json:"dir" gorm:"column:dir"`
|
||||||
)
|
Filename string `json:"filename" gorm:"column:filename"`
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
|
||||||
SessionType int `json:"session_type" gorm:"column:session_type"`
|
|
||||||
SessionId string `json:"session_id" gorm:"column:session_id"`
|
|
||||||
Uid int `json:"uid" gorm:"column:uid"`
|
|
||||||
UserName string `json:"user_name" gorm:"column:user_name"`
|
|
||||||
AssetId int `json:"asset_id" gorm:"column:asset_id"`
|
|
||||||
AssetInfo string `json:"asset_info" gorm:"column:asset_info"`
|
|
||||||
AccountId int `json:"account_id" gorm:"column:account_id"`
|
|
||||||
AccountInfo string `json:"account_info" gorm:"column:account_info"`
|
|
||||||
GatewayId int `json:"gateway_id" gorm:"column:gateway_id"`
|
|
||||||
GatewayInfo string `json:"gateway_info" gorm:"column:gateway_info"`
|
|
||||||
ClientIp string `json:"client_ip" gorm:"column:client_ip"`
|
|
||||||
Protocol string `json:"protocol" gorm:"column:protocol"`
|
|
||||||
Status int `json:"status" gorm:"column:status"`
|
|
||||||
Duration int64 `json:"duration" gorm:"-"`
|
|
||||||
ClosedAt *time.Time `json:"closed_at" gorm:"column:closed_at"`
|
|
||||||
|
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
|
||||||
CmdCount int64 `json:"cmd_count" gorm:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Session) TableName() string {
|
func (m *FileHistory) TableName() string {
|
||||||
return "filehistory"
|
return "file_history"
|
||||||
}
|
|
||||||
|
|
||||||
type SessionCmd struct {
|
|
||||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
|
||||||
SessionId string `json:"session_id" gorm:"column:session_id"`
|
|
||||||
Cmd string `json:"cmd" gorm:"column:cmd"`
|
|
||||||
Result string `json:"result" gorm:"column:result"`
|
|
||||||
Level int `json:"level" gorm:"column:level"`
|
|
||||||
|
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *SessionCmd) TableName() string {
|
|
||||||
return "session_cmd"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Session) IsSsh() bool {
|
|
||||||
return strings.HasPrefix(m.Protocol, "ssh")
|
|
||||||
}
|
|
||||||
|
|
||||||
type CmdCount struct {
|
|
||||||
SessionId string `gorm:"column:session_id"`
|
|
||||||
Count int64 `gorm:"column:count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SessionOptionAsset struct {
|
|
||||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
|
||||||
Name string `json:"name" gorm:"column:name"`
|
|
||||||
}
|
}
|
||||||
|
@@ -1,31 +1,78 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FILE_ACTION_LS = iota + 1
|
SESSIONTYPE_WEB = iota + 1
|
||||||
FILE_ACTION_MKDIR
|
SESSIONTYPE_CLIENT
|
||||||
FILE_ACTION_UPLOAD
|
|
||||||
FILE_ACTION_DOWNLOAD
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileHistory struct {
|
const (
|
||||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
SESSIONSTATUS_ONLINE = iota + 1
|
||||||
Uid int `json:"uid" gorm:"column:uid"`
|
SESSIONSTATUS_OFFLINE
|
||||||
UserName string `json:"user_name" gorm:"column:user_name"`
|
)
|
||||||
AssetId int `json:"asset_id" gorm:"column:asset_id"`
|
|
||||||
AccountId int `json:"account_id" gorm:"column:account_id"`
|
const (
|
||||||
ClientIp string `json:"client_ip" gorm:"column:client_ip"`
|
SESSIONACTION_NEW = iota + 1
|
||||||
Action int `json:"action" gorm:"column:action"`
|
SESSIONACTION_MONITOR
|
||||||
Dir string `json:"dir" gorm:"column:dir"`
|
SESSIONACTION_CLOSE
|
||||||
Filename string `json:"filename" gorm:"column:filename"`
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||||
|
SessionType int `json:"session_type" gorm:"column:session_type"`
|
||||||
|
SessionId string `json:"session_id" gorm:"column:session_id"`
|
||||||
|
Uid int `json:"uid" gorm:"column:uid"`
|
||||||
|
UserName string `json:"user_name" gorm:"column:user_name"`
|
||||||
|
AssetId int `json:"asset_id" gorm:"column:asset_id"`
|
||||||
|
AssetInfo string `json:"asset_info" gorm:"column:asset_info"`
|
||||||
|
AccountId int `json:"account_id" gorm:"column:account_id"`
|
||||||
|
AccountInfo string `json:"account_info" gorm:"column:account_info"`
|
||||||
|
GatewayId int `json:"gateway_id" gorm:"column:gateway_id"`
|
||||||
|
GatewayInfo string `json:"gateway_info" gorm:"column:gateway_info"`
|
||||||
|
ClientIp string `json:"client_ip" gorm:"column:client_ip"`
|
||||||
|
Protocol string `json:"protocol" gorm:"column:protocol"`
|
||||||
|
Status int `json:"status" gorm:"column:status"`
|
||||||
|
Duration int64 `json:"duration" gorm:"-"`
|
||||||
|
ClosedAt *time.Time `json:"closed_at" gorm:"column:closed_at"`
|
||||||
|
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
|
||||||
|
CmdCount int64 `json:"cmd_count" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *FileHistory) TableName() string {
|
func (m *Session) TableName() string {
|
||||||
return "session"
|
return "session"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionCmd struct {
|
||||||
|
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||||
|
SessionId string `json:"session_id" gorm:"column:session_id"`
|
||||||
|
Cmd string `json:"cmd" gorm:"column:cmd"`
|
||||||
|
Result string `json:"result" gorm:"column:result"`
|
||||||
|
Level int `json:"level" gorm:"column:level"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SessionCmd) TableName() string {
|
||||||
|
return "session_cmd"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Session) IsSsh() bool {
|
||||||
|
return strings.HasPrefix(m.Protocol, "ssh")
|
||||||
|
}
|
||||||
|
|
||||||
|
type CmdCount struct {
|
||||||
|
SessionId string `gorm:"column:session_id"`
|
||||||
|
Count int64 `gorm:"column:count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionOptionAsset struct {
|
||||||
|
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||||
|
Name string `json:"name" gorm:"column:name"`
|
||||||
|
}
|
||||||
|
@@ -404,29 +404,29 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "file action ls",
|
Name: "file action ls",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Pattern: "file/ls/:asset_id:/:account_id",
|
Pattern: "file/ls/:asset_id/:account_id",
|
||||||
HandlerFunc: c.FileLS,
|
HandlerFunc: c.FileLS,
|
||||||
Middleware: gin.HandlersChain{middleware.Auth()},
|
Middleware: gin.HandlersChain{middleware.Auth()},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file action mkdir",
|
Name: "file action mkdir",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Pattern: "file/mkdir/:asset_id:/:account_id",
|
Pattern: "file/mkdir/:asset_id/:account_id",
|
||||||
HandlerFunc: c.FileMkdir,
|
HandlerFunc: c.FileMkdir,
|
||||||
Middleware: gin.HandlersChain{middleware.Auth()},
|
Middleware: gin.HandlersChain{middleware.Auth()},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file action upload",
|
Name: "file action upload",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Pattern: "file/upload/:asset_id:/:account_id",
|
Pattern: "file/upload/:asset_id/:account_id",
|
||||||
HandlerFunc: c.FileUpload,
|
HandlerFunc: c.FileUpload,
|
||||||
Middleware: gin.HandlersChain{middleware.Auth()},
|
Middleware: gin.HandlersChain{middleware.Auth()},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file action download",
|
Name: "file action download",
|
||||||
Method: "Get",
|
Method: "GET",
|
||||||
Pattern: "file/upload/:asset_id:/:account_id",
|
Pattern: "file/download/:asset_id/:account_id",
|
||||||
HandlerFunc: c.FileUpload,
|
HandlerFunc: c.FileDownload,
|
||||||
Middleware: gin.HandlersChain{middleware.Auth()},
|
Middleware: gin.HandlersChain{middleware.Auth()},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
19
docs/api.sql
19
docs/api.sql
@@ -214,4 +214,21 @@ CREATE TABLE
|
|||||||
UNIQUE KEY `deleted_at` (`deleted_at`)
|
UNIQUE KEY `deleted_at` (`deleted_at`)
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
INSERT INTO oneterm.config (timeout) VALUES (7200);
|
INSERT INTO oneterm.config (timeout) VALUES (7200);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE
|
||||||
|
IF NOT EXISTS oneterm.file_history(
|
||||||
|
`id` INT NOT NULL AUTO_INCREMENT,
|
||||||
|
`uid` INT NOT NULL DEFAULT 0,
|
||||||
|
`user_name` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`asset_id` INT NOT NULL DEFAULT 0,
|
||||||
|
`account_id` INT NOT NULL DEFAULT 0,
|
||||||
|
`client_ip` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`action` INT NOT NULL DEFAULT 0,
|
||||||
|
`dir` VARCHAR(256) NOT NULL DEFAULT '',
|
||||||
|
`filename` VARCHAR(256) NOT NULL DEFAULT '',
|
||||||
|
`created_at` TIMESTAMP NOT NULL,
|
||||||
|
`updated_at` TIMESTAMP NOT NULL,
|
||||||
|
PRIMARY KEY(`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
Reference in New Issue
Block a user