mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
Fix the api filtering method
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
@@ -42,8 +41,12 @@ func APIListHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// 获取应用UUID参数(用于按应用筛选接口)
|
||||
appUUID := strings.TrimSpace(r.URL.Query().Get("app_uuid"))
|
||||
|
||||
// 获取搜索参数
|
||||
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||
// 获取接口类型参数(用于按接口类型筛选)
|
||||
apiTypeStr := strings.TrimSpace(r.URL.Query().Get("api_type"))
|
||||
var apiType int
|
||||
if apiTypeStr != "" {
|
||||
apiType, _ = strconv.Atoi(apiTypeStr)
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
db, err := database.GetDB()
|
||||
@@ -61,9 +64,9 @@ func APIListHandler(w http.ResponseWriter, r *http.Request) {
|
||||
query = query.Where("app_uuid = ?", appUUID)
|
||||
}
|
||||
|
||||
// 如果有搜索条件,添加搜索
|
||||
if search != "" {
|
||||
query = query.Where("api_key LIKE ? OR app_uuid LIKE ?", "%"+search+"%", "%"+search+"%")
|
||||
// 如果指定了接口类型,则按接口类型筛选
|
||||
if apiType > 0 {
|
||||
query = query.Where("api_type = ?", apiType)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
@@ -274,6 +277,48 @@ func APIGetAppsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// APIGetTypesHandler 获取接口类型列表API处理器
|
||||
func APIGetTypesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建接口类型列表
|
||||
type APITypeItem struct {
|
||||
Value int `json:"value"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var apiTypes []APITypeItem
|
||||
|
||||
// 获取所有有效的API类型
|
||||
validTypes := []int{
|
||||
models.APITypeGetBulletin, models.APITypeGetUpdateUrl, models.APITypeCheckAppVersion, models.APITypeGetCardInfo,
|
||||
models.APITypeSingleLogin,
|
||||
models.APITypeUserLogin, models.APITypeUserRegin, models.APITypeUserRecharge, models.APITypeCardRegin,
|
||||
models.APITypeLogOut,
|
||||
models.APITypeGetExpired, models.APITypeCheckUserStatus, models.APITypeGetAppData, models.APITypeGetVariable,
|
||||
models.APITypeUpdatePwd, models.APITypeMacChangeBind, models.APITypeIPChangeBind,
|
||||
models.APITypeDisableUser, models.APITypeBlackUser, models.APITypeUserDeductedTime,
|
||||
}
|
||||
|
||||
for _, apiType := range validTypes {
|
||||
apiTypes = append(apiTypes, APITypeItem{
|
||||
Value: apiType,
|
||||
Name: models.GetAPITypeName(apiType),
|
||||
})
|
||||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"data": apiTypes,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func APIGenerateKeysHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
@@ -375,64 +420,3 @@ func APIGenerateKeysHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func APIResetKeyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
http.Error(w, "接口ID不能为空", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
db, err := database.GetDB()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to get database connection")
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var api models.API
|
||||
if err := db.First(&api, req.ID).Error; err != nil {
|
||||
http.Error(w, "接口不存在", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 生成新的16位大写十六进制密钥
|
||||
bytes := make([]byte, 8)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
logrus.WithError(err).Error("Failed to generate random API key")
|
||||
http.Error(w, "生成密钥失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
newKey := strings.ToUpper(hex.EncodeToString(bytes))
|
||||
|
||||
if err := db.Model(&api).Update("api_key", newKey).Error; err != nil {
|
||||
logrus.WithError(err).Error("Failed to update API key")
|
||||
http.Error(w, "更新密钥失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "接口密钥重置成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": api.ID,
|
||||
"api_key": newKey,
|
||||
},
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// API 接口表模型
|
||||
@@ -21,9 +16,6 @@ type API struct {
|
||||
// API类型(int型)
|
||||
APIType int `gorm:"not null;comment:API类型" json:"api_type"`
|
||||
|
||||
// API密钥
|
||||
APIKey string `gorm:"size:255;not null;uniqueIndex;comment:API密钥,唯一标识" json:"api_key"`
|
||||
|
||||
// 应用UUID,关联到App表
|
||||
AppUUID string `gorm:"size:36;not null;index;comment:关联的应用UUID" json:"app_uuid"`
|
||||
|
||||
@@ -55,17 +47,6 @@ type API struct {
|
||||
UpdatedAt time.Time `gorm:"comment:更新时间" json:"updated_at"`
|
||||
}
|
||||
|
||||
// BeforeCreate 在创建记录前自动生成API密钥
|
||||
func (api *API) BeforeCreate(tx *gorm.DB) error {
|
||||
if api.APIKey == "" {
|
||||
// 生成16位大写十六进制API密钥
|
||||
bytes := make([]byte, 8) // 8字节 = 16位十六进制字符
|
||||
rand.Read(bytes)
|
||||
api.APIKey = strings.ToUpper(hex.EncodeToString(bytes))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (API) TableName() string {
|
||||
return "apis"
|
||||
|
||||
@@ -76,7 +76,7 @@ func RegisterAdminRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/admin/api/apis/list", adminctl.AdminAuthRequired(adminctl.APIListHandler))
|
||||
mux.HandleFunc("/admin/api/apis/update", adminctl.AdminAuthRequired(adminctl.APIUpdateHandler))
|
||||
mux.HandleFunc("/admin/api/apis/apps", adminctl.AdminAuthRequired(adminctl.APIGetAppsHandler))
|
||||
mux.HandleFunc("/admin/api/apis/reset_key", adminctl.AdminAuthRequired(adminctl.APIResetKeyHandler))
|
||||
mux.HandleFunc("/admin/api/apis/types", adminctl.AdminAuthRequired(adminctl.APIGetTypesHandler))
|
||||
mux.HandleFunc("/admin/api/apis/generate_keys", adminctl.AdminAuthRequired(adminctl.APIGenerateKeysHandler))
|
||||
|
||||
// 系统信息API(用于仪表盘定时刷新)
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">搜索</label>
|
||||
<label class="layui-form-label">接口类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="search" placeholder="API接口/应用UUID" autocomplete="off" class="layui-input" />
|
||||
<select name="api_type" lay-filter="apiTypeSelect">
|
||||
<option value="">请选择接口类型</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button type="button" class="layui-btn" id="btnSearchAPIs">查询</button>
|
||||
<button type="button" class="layui-btn layui-btn-primary" id="btnResetAPIs">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,7 +39,6 @@
|
||||
<script type="text/html" id="tpl-apis-ops">
|
||||
<div style="white-space: nowrap;">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="reset">重置接口</a>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="tpl-apis-status">
|
||||
@@ -139,7 +139,6 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var dropdown = layui.dropdown;
|
||||
|
||||
// 格式化时间函数
|
||||
function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
@@ -212,21 +211,9 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
loading: true,
|
||||
cols: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'app_name', title: '应用名称', width: 180 },
|
||||
{ field: 'api_type_name', title: '接口类型', width: 120 },
|
||||
{
|
||||
field: 'api_key',
|
||||
title: 'API接口',
|
||||
minWidth: 350,
|
||||
templet: (d) => {
|
||||
const baseUrl = window.location.protocol + '//' + window.location.host;
|
||||
const fullUrl = baseUrl + '/api/v1/' + d.api_key;
|
||||
return '<span style="font-family: monospace; font-size: 12px; word-break: break-all; cursor: pointer; color: #1E9FFF; text-decoration: underline;" ' +
|
||||
'onclick="copyToClipboard(\'' + fullUrl + '\')" title="点击复制接口地址">' +
|
||||
fullUrl +
|
||||
'</span>';
|
||||
}
|
||||
},
|
||||
{ field: 'app_name', title: '应用名称', mixWidth: 180 },
|
||||
{ field: 'api_type_name', title: '接口类型', mixWidth: 120 },
|
||||
|
||||
{
|
||||
field: 'status_name',
|
||||
title: '状态',
|
||||
@@ -254,7 +241,13 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
width: 180,
|
||||
templet: (d) => formatDateTime(d.created_at)
|
||||
},
|
||||
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 150 }
|
||||
{
|
||||
field: 'updated_at',
|
||||
title: '修改时间',
|
||||
width: 180,
|
||||
templet: (d) => formatDateTime(d.updated_at)
|
||||
},
|
||||
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 70 }
|
||||
]]
|
||||
});
|
||||
|
||||
@@ -283,8 +276,34 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化加载应用列表
|
||||
// 加载接口类型列表到筛选器
|
||||
function loadAPITypes() {
|
||||
$.ajax({
|
||||
url: '/admin/api/apis/types',
|
||||
type: 'GET',
|
||||
success: function(res) {
|
||||
if (res.success && res.data) {
|
||||
var typeSelect = $('select[name="api_type"]').eq(0);
|
||||
// 清空现有选项(保留默认选项)
|
||||
typeSelect.find('option:not(:first)').remove();
|
||||
// 添加接口类型选项
|
||||
res.data.forEach(function(type) {
|
||||
var option = '<option value="' + type.value + '">' + type.name + '</option>';
|
||||
typeSelect.append(option);
|
||||
});
|
||||
// 刷新下拉
|
||||
form.render('select');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
layer.msg('加载接口类型列表失败', {icon: 2});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化加载应用列表和接口类型列表
|
||||
loadApps();
|
||||
loadAPITypes();
|
||||
|
||||
// 监听应用选择变化
|
||||
form.on('select(appSelect)', function(data) {
|
||||
@@ -292,7 +311,7 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
apisTable.reload({
|
||||
where: {
|
||||
app_uuid: currentAppUUID,
|
||||
search: $('input[name="search"]').val()
|
||||
api_type: $('select[name="api_type"]').val()
|
||||
},
|
||||
page: {
|
||||
curr: 1
|
||||
@@ -300,14 +319,12 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
});
|
||||
});
|
||||
|
||||
// 搜索功能
|
||||
$('#btnSearchAPIs').on('click', function() {
|
||||
const search = $('input[name="search"]').val();
|
||||
const appUUID = $('select[name="app_uuid"]').eq(0).val();
|
||||
// 监听接口类型选择变化
|
||||
form.on('select(apiTypeSelect)', function(data) {
|
||||
apisTable.reload({
|
||||
where: {
|
||||
app_uuid: appUUID,
|
||||
search: search
|
||||
app_uuid: $('select[name="app_uuid"]').val(),
|
||||
api_type: data.value
|
||||
},
|
||||
page: {
|
||||
curr: 1
|
||||
@@ -315,7 +332,7 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
});
|
||||
});
|
||||
|
||||
// 重置搜索
|
||||
// 重置筛选
|
||||
$('#btnResetAPIs').on('click', function() {
|
||||
$('#apiFilterForm')[0].reset();
|
||||
form.render();
|
||||
@@ -586,37 +603,6 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() {
|
||||
},
|
||||
shadeClose: false
|
||||
});
|
||||
} else if (obj.event === 'reset') {
|
||||
layer.confirm('确定重置该接口吗?', {icon: 3, title: '提示'}, function(index) {
|
||||
$.ajax({
|
||||
url: '/admin/api/apis/reset_key',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ id: data.id }),
|
||||
success: function(res) {
|
||||
if (res.success) {
|
||||
layer.msg('接口重置成功', {icon: 1});
|
||||
// 更新当前行的密钥显示
|
||||
if (res.data && res.data.api_key) {
|
||||
obj.update({ api_key: res.data.api_key });
|
||||
} else {
|
||||
// 兜底刷新表格
|
||||
apisTable.reload();
|
||||
}
|
||||
} else {
|
||||
layer.msg(res.message || '重置失败', {icon: 2});
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
let msg = '重置失败';
|
||||
if (xhr.responseText) {
|
||||
msg = xhr.responseText;
|
||||
}
|
||||
layer.msg(msg, {icon: 2});
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user