refactor(global): move config to global package and consolidate handlers
- Move Config struct and related functions to global package - Consolidate handler utilities into utils package - Remove deprecated handlers and instance packages - Add new handlers package with settings and proxy functionality - Update config.json to include watchdog enabled flag
This commit is contained in:
@@ -1,122 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"super-frpc/database"
|
||||
"super-frpc/global"
|
||||
"super-frpc/postLog"
|
||||
"super-frpc/session"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func SendErrorResponse(w http.ResponseWriter, statusCode int, message string, data ...interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
resp := Response{
|
||||
Success: false,
|
||||
Message: message,
|
||||
}
|
||||
if len(data) > 0 {
|
||||
resp.Data = data[0]
|
||||
}
|
||||
jsonResp, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("Failed to marshal error response: %v", err))
|
||||
return
|
||||
}
|
||||
w.Write(jsonResp)
|
||||
}
|
||||
|
||||
func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
resp := Response{
|
||||
Success: true,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
jsonResp, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("Failed to marshal success response: %v", err))
|
||||
return
|
||||
}
|
||||
w.Write(jsonResp)
|
||||
}
|
||||
|
||||
func Auth(w http.ResponseWriter, r *http.Request, targetMethod string, allowedUserLevels ...string) (int, error) {
|
||||
if r.Method != targetMethod {
|
||||
return 0, fmt.Errorf("Method not allowed: %s", targetMethod)
|
||||
}
|
||||
|
||||
if !global.Is.Debug && !session.ValidateTimeStamp(r.Header, global.Is.Debug) {
|
||||
return 0, fmt.Errorf("Invalid or missing X-Timestamp in header")
|
||||
}
|
||||
|
||||
userID, err := session.ExtractUserIDFromToken(r.Header.Get("X-Token"))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Invalid token format: %w", err)
|
||||
}
|
||||
|
||||
if err := session.ValidateToken(userID, r.Header.Get("X-Token")); err != nil {
|
||||
return 0, fmt.Errorf("Token validation failed: %w", err)
|
||||
}
|
||||
|
||||
if len(allowedUserLevels) > 0 {
|
||||
currentUser, err := database.DBQuerySpecificUser(userID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Failed to query user: %w", err)
|
||||
}
|
||||
allowed := false
|
||||
for _, level := range allowedUserLevels {
|
||||
if currentUser.Type == level {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
return 0, fmt.Errorf("User level not allowed: required one of %v, got %s", allowedUserLevels, currentUser.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func GetUserType(userID int) (string, error) {
|
||||
user, err := database.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to get user type: %w", err)
|
||||
}
|
||||
return user.Type, nil
|
||||
}
|
||||
|
||||
func GetClientIP(r *http.Request) string {
|
||||
forwarded := r.Header.Get("X-Forwarded-For")
|
||||
if forwarded != "" {
|
||||
return forwarded
|
||||
}
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
||||
func LogRequest(r *http.Request, userID int) {
|
||||
postLog.Info(fmt.Sprintf("[%s] %s %s - UserID: %d - IP: %s",
|
||||
time.Now().Format("2006-01-02 15:04:05"),
|
||||
r.Method,
|
||||
r.URL.Path,
|
||||
userID,
|
||||
GetClientIP(r),
|
||||
))
|
||||
}
|
||||
|
||||
func IntToString(i int) string {
|
||||
return strconv.Itoa(i)
|
||||
}
|
||||
@@ -0,0 +1,964 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"super-frpc/config"
|
||||
"super-frpc/database"
|
||||
"super-frpc/utils"
|
||||
"super-frpc/postLog"
|
||||
"super-frpc/service"
|
||||
)
|
||||
|
||||
func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[CreateInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
bootAtStart := false
|
||||
if bas, ok := reqMap["bootAtStart"]; ok {
|
||||
switch v := bas.(type) {
|
||||
case bool:
|
||||
bootAtStart = v
|
||||
case string:
|
||||
if v == "true" {
|
||||
bootAtStart = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{})
|
||||
if !ok {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceInfo := config.InstanceInfo{
|
||||
Name: getStringFromMap(instanceInfoMap, "name"),
|
||||
ServerAddr: getStringFromMap(instanceInfoMap, "serverAddr"),
|
||||
ServerPort: getStringFromMap(instanceInfoMap, "serverPort"),
|
||||
AuthMethod: getStringFromMap(instanceInfoMap, "auth_method"),
|
||||
RunUser: getStringFromMap(instanceInfoMap, "runUser"),
|
||||
}
|
||||
|
||||
if additional, ok := reqMap["additionalProperties"].(map[string]interface{}); ok {
|
||||
instanceInfo.Additional = additional
|
||||
}
|
||||
|
||||
req := config.CreateInstanceRequest{
|
||||
InstanceInfo: instanceInfo,
|
||||
BootAtStart: bootAtStart,
|
||||
RunUser: instanceInfo.RunUser,
|
||||
Additional: instanceInfo.Additional,
|
||||
}
|
||||
|
||||
if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" ||
|
||||
req.InstanceInfo.ServerPort == "" || req.InstanceInfo.AuthMethod == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in instanceInfo")
|
||||
return
|
||||
}
|
||||
|
||||
runUser := req.RunUser
|
||||
if runUser == "" {
|
||||
runUser = "root"
|
||||
}
|
||||
|
||||
user, err := database.GetUserByID(userID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get user info: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
return
|
||||
}
|
||||
|
||||
configDir, err := service.GetConfigDir()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get config directory: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory")
|
||||
return
|
||||
}
|
||||
|
||||
configFileName := fmt.Sprintf("superfrpc_%s_%s.toml", user.Username, req.InstanceInfo.Name)
|
||||
configPath := filepath.Join(configDir, configFileName)
|
||||
|
||||
setKeyTextWrapper := func(configPath, key, section, value string) error {
|
||||
return config.SetKeyText(configPath, key, section, value, os.ReadFile, os.WriteFile)
|
||||
}
|
||||
|
||||
if err := config.HandleConfigFileCreate(configPath, req.InstanceInfo, setKeyTextWrapper); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create config file %s: %v", configPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create config file")
|
||||
return
|
||||
}
|
||||
|
||||
instance := database.FrpcInstance{
|
||||
UserID: userID,
|
||||
Name: req.InstanceInfo.Name,
|
||||
BootAtStart: req.BootAtStart,
|
||||
RunUser: runUser,
|
||||
ConfigPath: configPath,
|
||||
Watchdog: 0,
|
||||
}
|
||||
|
||||
if err := database.DBAddFrpcInstance(instance); err != nil {
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to save instance %s to database: %v", req.InstanceInfo.Name, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to save instance to database")
|
||||
return
|
||||
}
|
||||
|
||||
createdInstance, err := database.DBQueryFrpcInstance(userID, req.InstanceInfo.Name)
|
||||
if err != nil {
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to query created instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance")
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.CreateBootService(createdInstance.ID); err != nil {
|
||||
database.DBRemoveFrpcInstanceByID(createdInstance.ID)
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
|
||||
return
|
||||
}
|
||||
|
||||
if req.BootAtStart {
|
||||
if err := service.SetBootAtStart(createdInstance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{
|
||||
"name": req.InstanceInfo.Name,
|
||||
"configPath": configPath,
|
||||
"bootAtStart": req.BootAtStart,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[CreateInstanceHandler] Instance %s created successfully: configPath=%s, bootAtStart=%v, runUser=%s, additionalProperties=%v", req.InstanceInfo.Name, configPath, req.BootAtStart, runUser, req.Additional))
|
||||
}
|
||||
|
||||
func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[DeleteInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceIDStr := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.RemoveBootService(instanceID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(instance.ConfigPath); err == nil {
|
||||
if err := os.Remove(instance.ConfigPath); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove config file")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.DBRemoveFrpcInstanceByID(instanceID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to delete instance %d from database: %v", instanceID, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to delete instance from database")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{
|
||||
"id": instanceID,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[DeleteInstanceHandler] Instance %d deleted successfully", instanceID))
|
||||
}
|
||||
|
||||
func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ModifyInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceIDStr := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
modifyType := getStringFromMap(reqMap, "type")
|
||||
if modifyType == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "type is required")
|
||||
return
|
||||
}
|
||||
|
||||
if modifyType != "configFile" && modifyType != "systemConfig" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Unknown modify type %s", modifyType))
|
||||
return
|
||||
}
|
||||
|
||||
modifiedData, ok := reqMap["modifiedData"].(map[string]interface{})
|
||||
if !ok || modifiedData == nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "modifiedData is required and must be an object")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d tried to modify a not existed instance: %d", userID, instanceID))
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d does not have permission to modify instance %d", userID, instanceID))
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Permission denied")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := database.GetUserByID(instance.UserID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to get user info: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
return
|
||||
}
|
||||
|
||||
if modifyType == "configFile" {
|
||||
handleConfigFileModify(w, instance, modifiedData)
|
||||
} else {
|
||||
handleSystemConfigModify(w, r, instance, modifiedData, user)
|
||||
}
|
||||
}
|
||||
|
||||
func handleConfigFileModify(w http.ResponseWriter, instance database.FrpcInstance, modifiedData map[string]interface{}) {
|
||||
configPath := instance.ConfigPath
|
||||
|
||||
for key, value := range modifiedData {
|
||||
configKey := key
|
||||
if key == "auth_method" {
|
||||
configKey = "auth.method"
|
||||
}
|
||||
if key == "auth_token" {
|
||||
configKey = "auth.token"
|
||||
}
|
||||
|
||||
var configValue string
|
||||
if key == "serverPort" {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
configValue = strconv.Itoa(int(v))
|
||||
case int:
|
||||
configValue = strconv.Itoa(v)
|
||||
case string:
|
||||
var intVal int
|
||||
if _, err := fmt.Sscanf(v, "%d", &intVal); err == nil {
|
||||
configValue = strconv.Itoa(intVal)
|
||||
} else {
|
||||
configValue = v
|
||||
}
|
||||
default:
|
||||
configValue = fmt.Sprintf("%v", value)
|
||||
}
|
||||
} else {
|
||||
configValue = fmt.Sprintf("%v", value)
|
||||
}
|
||||
if err := config.SetKeyText(configPath, configKey, "", configValue, os.ReadFile, os.WriteFile); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to set key %s: %v", key, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{
|
||||
"instanceName": instance.Name,
|
||||
"instanceID": instance.ID,
|
||||
"configPath": configPath,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name))
|
||||
}
|
||||
|
||||
func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance database.FrpcInstance, modifiedData map[string]interface{}, user *database.User) {
|
||||
newName := instance.Name
|
||||
newRunUser := instance.RunUser
|
||||
newBootAtStart := instance.BootAtStart
|
||||
var bootServiceError string
|
||||
|
||||
if v, ok := modifiedData["name"].(string); ok && v != "" {
|
||||
newName = v
|
||||
}
|
||||
if v, ok := modifiedData["runUser"].(string); ok {
|
||||
newRunUser = v
|
||||
}
|
||||
if v, ok := modifiedData["bootAtStart"].(bool); ok {
|
||||
newBootAtStart = v
|
||||
}
|
||||
|
||||
oldConfigPath := instance.ConfigPath
|
||||
var newConfigPath string
|
||||
|
||||
if newName != instance.Name || newRunUser != instance.RunUser {
|
||||
configDir, err := service.GetConfigDir()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to get config directory: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory")
|
||||
return
|
||||
}
|
||||
|
||||
newConfigFileName := fmt.Sprintf("superfrpc_%s_%s.toml", user.Username, newName)
|
||||
newConfigPath = filepath.Join(configDir, newConfigFileName)
|
||||
|
||||
if oldConfigPath != newConfigPath {
|
||||
if _, err := os.Stat(oldConfigPath); err == nil {
|
||||
if err := os.Rename(oldConfigPath, newConfigPath); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to rename config file %s to %s: %v", oldConfigPath, newConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to rename config file")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newConfigPath = oldConfigPath
|
||||
}
|
||||
|
||||
instance.RunUser = newRunUser
|
||||
instance.BootAtStart = newBootAtStart
|
||||
instance.ConfigPath = newConfigPath
|
||||
|
||||
if err := database.DBUpdateFrpcInstance(instance); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to update instance in database: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database")
|
||||
return
|
||||
}
|
||||
|
||||
if newBootAtStart {
|
||||
if err := service.RemoveBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||
}
|
||||
instance.Name = newName
|
||||
if err := service.SetBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err))
|
||||
if bootServiceError != "" {
|
||||
bootServiceError += "; "
|
||||
}
|
||||
bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := service.RemoveBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||
}
|
||||
instance.Name = newName
|
||||
}
|
||||
instance.Name = newName
|
||||
|
||||
data := map[string]interface{}{
|
||||
"instanceName": newName,
|
||||
"instanceID": instance.ID,
|
||||
"configPath": newConfigPath,
|
||||
"bootAtStart": newBootAtStart,
|
||||
"runUser": newRunUser,
|
||||
}
|
||||
if bootServiceError != "" {
|
||||
data["bootServiceError"] = bootServiceError
|
||||
}
|
||||
utils.SendSuccessResponse(w, "System config modified successfully", data)
|
||||
}
|
||||
|
||||
func GetUserInstances(userID int) ([]database.FrpcInstance, error) {
|
||||
rows, err := database.DBQueryUserInstances(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var instances []database.FrpcInstance
|
||||
for rows.Next() {
|
||||
var instance database.FrpcInstance
|
||||
var createdAtStr string
|
||||
if err := rows.Scan(
|
||||
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
||||
instances = append(instances, instance)
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
func getStringFromMap(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getNumFromMap(m map[string]interface{}, key string) int {
|
||||
if v, ok := m[key].(int); ok {
|
||||
return v
|
||||
}
|
||||
if v, ok := m[key].(float64); ok {
|
||||
return int(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
instances, err := GetUserInstances(userID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get user instances: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances")
|
||||
return
|
||||
}
|
||||
|
||||
instanceList := make([]map[string]interface{}, len(instances))
|
||||
for i, inst := range instances {
|
||||
instanceData := map[string]interface{}{
|
||||
"instanceID": inst.ID,
|
||||
"name": inst.Name,
|
||||
"createdAt": inst.CreatedAt,
|
||||
"createdBy": inst.CreatedBy,
|
||||
"isRunning": false,
|
||||
}
|
||||
|
||||
err = service.IsInstanceRunning(inst.ID)
|
||||
if err != nil {
|
||||
instanceData["isRunning"] = false
|
||||
} else {
|
||||
instanceData["isRunning"] = true
|
||||
}
|
||||
|
||||
instanceList[i] = instanceData
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instances retrieved successfully", instanceList)
|
||||
postLog.Info(fmt.Sprintf("[ListInstancesHandler] Retrieved %d instances for user %d", len(instances), userID))
|
||||
}
|
||||
|
||||
func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceIDStr := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start a not existed instance: %d", userID, instanceID))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start instance %d that does not belong to them", userID, instanceID))
|
||||
return
|
||||
}
|
||||
|
||||
initType := service.GetInitSystem()
|
||||
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get service name: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := service.StartWindowsService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start Windows service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start Windows service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Windows service %s started successfully", serviceName))
|
||||
|
||||
case "systemd":
|
||||
if err := service.StartSystemdService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start systemd service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start systemd service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Systemd service %s started successfully", serviceName))
|
||||
|
||||
case "init.d":
|
||||
if err := service.StartInitDService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start init.d service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start init.d service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Init.d service %s started successfully", serviceName))
|
||||
|
||||
default:
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{
|
||||
"instanceID": instanceID,
|
||||
"serviceName": serviceName,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[StartInstanceHandler] Instance %d started successfully", instanceID))
|
||||
}
|
||||
|
||||
func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceIDStr := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop a not existed instance: %d", userID, instanceID))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop instance %d that does not belong to them", userID, instanceID))
|
||||
return
|
||||
}
|
||||
|
||||
initType := service.GetInitSystem()
|
||||
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get service name: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := service.StopWindowsService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop Windows service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop Windows service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Windows service %s stopped successfully", serviceName))
|
||||
|
||||
case "systemd":
|
||||
if err := service.StopSystemdService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop systemd service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop systemd service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Systemd service %s stopped successfully", serviceName))
|
||||
|
||||
case "init.d":
|
||||
if err := service.StopInitDService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop init.d service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop init.d service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Init.d service %s stopped successfully", serviceName))
|
||||
|
||||
default:
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{
|
||||
"instanceID": instanceID,
|
||||
"serviceName": serviceName,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[StopInstanceHandler] Instance %d stopped successfully", instanceID))
|
||||
}
|
||||
|
||||
func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[RestartInstanceHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceIDStr := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart a not existed instance: %d", userID, instanceID))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart instance %d that does not belong to them", userID, instanceID))
|
||||
return
|
||||
}
|
||||
|
||||
initType := service.GetInitSystem()
|
||||
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get service name: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := service.RestartWindowsService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart Windows service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart Windows service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Windows service %s restarted successfully", serviceName))
|
||||
|
||||
case "systemd":
|
||||
if err := service.RestartSystemdService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart systemd service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart systemd service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Systemd service %s restarted successfully", serviceName))
|
||||
|
||||
case "init.d":
|
||||
if err := service.RestartInitDService(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart init.d service %s: %v", serviceName, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart init.d service: %v", err))
|
||||
return
|
||||
}
|
||||
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Init.d service %s restarted successfully", serviceName))
|
||||
|
||||
default:
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{
|
||||
"instanceID": instanceID,
|
||||
"serviceName": serviceName,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Instance %d restarted successfully", instanceID))
|
||||
}
|
||||
|
||||
func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[GetInstanceStatusHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
queryParams := r.URL.Query()
|
||||
instanceIDStr := queryParams.Get("instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
err = service.IsInstanceRunning(instanceID)
|
||||
isRunning := err == nil
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance status retrieved successfully", map[string]interface{}{
|
||||
"instanceID": instanceID,
|
||||
"isRunning": isRunning,
|
||||
})
|
||||
}
|
||||
|
||||
func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[GetInstanceInfoHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
queryParams := r.URL.Query()
|
||||
instanceIDStr := queryParams.Get("instanceID")
|
||||
if instanceIDStr == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, err := strconv.Atoi(instanceIDStr)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
utils.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := database.GetUserByID(userID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get user info: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
return
|
||||
}
|
||||
|
||||
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get service name: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(instance.ConfigPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to read config file: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
frpcConfig, err := config.DecodeFrpcConfig(string(configContent))
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to decode frpc config: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to decode frpc config")
|
||||
return
|
||||
}
|
||||
|
||||
err = service.IsInstanceRunning(instanceID)
|
||||
isRunning := err == nil
|
||||
|
||||
response := map[string]interface{}{
|
||||
"name": instance.Name,
|
||||
"serviceName": serviceName,
|
||||
"createdAt": instance.CreatedAt.Format(time.RFC3339),
|
||||
"createdBy": instance.CreatedBy,
|
||||
"isRunning": isRunning,
|
||||
"auth_method": frpcConfig.Global["auth.method"],
|
||||
"bootAtStart": instance.BootAtStart,
|
||||
"runUser": instance.RunUser,
|
||||
"configPath": instance.ConfigPath,
|
||||
}
|
||||
|
||||
if user.Type == "admin" || user.Type == "superuser" {
|
||||
response["serverAddr"] = frpcConfig.Global["serverAddr"]
|
||||
response["serverPort"] = frpcConfig.Global["serverPort"]
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Instance info retrieved successfully", response)
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"super-frpc/config"
|
||||
"super-frpc/database"
|
||||
"super-frpc/utils"
|
||||
"super-frpc/postLog"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
func CreateProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[CreateProxyHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceID == "" {
|
||||
postLog.Error("[CreateProxyHandler] instanceID is required")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{})
|
||||
if !ok {
|
||||
postLog.Error("[CreateProxyHandler] Invalid proxyInfo format")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format")
|
||||
return
|
||||
}
|
||||
|
||||
proxyInfo := config.FrpcProxyInfo{
|
||||
Name: getStringFromMap(proxyInfoMap, "name"),
|
||||
Type: getStringFromMap(proxyInfoMap, "type"),
|
||||
LocalIP: getStringFromMap(proxyInfoMap, "localIP"),
|
||||
LocalPort: getNumFromMap(proxyInfoMap, "localPort"),
|
||||
RemotePort: getNumFromMap(proxyInfoMap, "remotePort"),
|
||||
}
|
||||
|
||||
if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" ||
|
||||
proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 {
|
||||
postLog.Error("[CreateProxyHandler] Missing required fields in proxyInfo")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo")
|
||||
return
|
||||
}
|
||||
|
||||
var instance database.FrpcInstance
|
||||
instanceIDInt, _ := strconv.Atoi(instanceID)
|
||||
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Instance not found for user %d", userID))
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(instance.ConfigPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
updatedContent, err := config.AddFrpcProxy(string(configContent), proxyInfo)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to add proxy: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to add proxy")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Proxy created successfully", map[string]interface{}{
|
||||
"instanceID": instance.ID,
|
||||
"configPath": instance.ConfigPath,
|
||||
"proxyName": proxyInfo.Name,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[CreateProxyHandler] Proxy %s created successfully for instance %d", proxyInfo.Name, instance.ID))
|
||||
}
|
||||
|
||||
func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ModifyProxyHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceID == "" {
|
||||
postLog.Error("[ModifyProxyHandler] instanceID is required")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{})
|
||||
if !ok {
|
||||
postLog.Error("[ModifyProxyHandler] Invalid proxyInfo format")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format")
|
||||
return
|
||||
}
|
||||
|
||||
proxyInfo := config.FrpcProxyInfo{
|
||||
Name: getStringFromMap(proxyInfoMap, "name"),
|
||||
Type: getStringFromMap(proxyInfoMap, "type"),
|
||||
LocalIP: getStringFromMap(proxyInfoMap, "localIP"),
|
||||
LocalPort: getNumFromMap(proxyInfoMap, "localPort"),
|
||||
RemotePort: getNumFromMap(proxyInfoMap, "remotePort"),
|
||||
}
|
||||
|
||||
if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" ||
|
||||
proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 {
|
||||
postLog.Error("[ModifyProxyHandler] Missing required fields in proxyInfo")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo")
|
||||
return
|
||||
}
|
||||
|
||||
var instance database.FrpcInstance
|
||||
instanceIDInt, _ := strconv.Atoi(instanceID)
|
||||
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Instance not found for user %d", userID))
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(instance.ConfigPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
updatedContent, err := config.ModifyFrpcProxy(string(configContent), proxyInfo)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to modify proxy: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to modify proxy")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Proxy modified successfully", map[string]interface{}{
|
||||
"instanceID": instance.ID,
|
||||
"configPath": instance.ConfigPath,
|
||||
"proxyName": proxyInfo.Name,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[ModifyProxyHandler] Proxy %s modified successfully for instance %d", proxyInfo.Name, instance.ID))
|
||||
}
|
||||
|
||||
func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[DeleteProxyHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var reqMap map[string]interface{}
|
||||
if err := json.Unmarshal(body, &reqMap); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to unmarshal request body: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
return
|
||||
}
|
||||
|
||||
instanceID := getStringFromMap(reqMap, "instanceID")
|
||||
if instanceID == "" {
|
||||
postLog.Error("[DeleteProxyHandler] instanceID is required")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
proxyName := getStringFromMap(reqMap, "proxyName")
|
||||
if proxyName == "" {
|
||||
postLog.Error("[DeleteProxyHandler] proxyName is required")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "proxyName is required")
|
||||
return
|
||||
}
|
||||
|
||||
var instance database.FrpcInstance
|
||||
instanceIDInt, _ := strconv.Atoi(instanceID)
|
||||
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Instance not found for user %d", userID))
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(instance.ConfigPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
updatedContent, err := config.RemoveFrpcProxy(string(configContent), proxyName)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to remove proxy: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove proxy")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Proxy deleted successfully", map[string]interface{}{
|
||||
"instanceID": instance.ID,
|
||||
"configPath": instance.ConfigPath,
|
||||
"proxyName": proxyName,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[DeleteProxyHandler] Proxy %s deleted successfully from instance %d", proxyName, instance.ID))
|
||||
}
|
||||
|
||||
func ListProxiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ListProxiesHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
queryParams := r.URL.Query()
|
||||
instanceID := queryParams.Get("instanceID")
|
||||
if instanceID == "" {
|
||||
postLog.Error("[ListProxiesHandler] instanceID is required")
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
|
||||
return
|
||||
}
|
||||
|
||||
var instance database.FrpcInstance
|
||||
instanceIDInt, _ := strconv.Atoi(instanceID)
|
||||
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to query instance: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
|
||||
return
|
||||
}
|
||||
|
||||
if instance.UserID != userID {
|
||||
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Instance not found for user %d", userID))
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
return
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(instance.ConfigPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
var cfg config.FrpcConfig
|
||||
if _, err := toml.Decode(string(configContent), &cfg); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to parse config: %v", err))
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to parse config file")
|
||||
return
|
||||
}
|
||||
|
||||
proxyList := make([]map[string]interface{}, len(cfg.Proxies))
|
||||
for i, proxy := range cfg.Proxies {
|
||||
proxyData := map[string]interface{}{
|
||||
"name": proxy["name"],
|
||||
"type": proxy["type"],
|
||||
"localIP": proxy["localIP"],
|
||||
"localPort": proxy["localPort"],
|
||||
"remotePort": proxy["remotePort"],
|
||||
}
|
||||
proxyList[i] = proxyData
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Proxies listed successfully", map[string]interface{}{
|
||||
"instanceID": instance.ID,
|
||||
"proxyCount": len(proxyList),
|
||||
"proxies": proxyList,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[ListProxiesHandler] Retrieved %d proxies for instance %d", len(proxyList), instance.ID))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"super-frpc/postLog"
|
||||
"super-frpc/utils"
|
||||
)
|
||||
|
||||
func GetSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodGet, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[GetSettingsHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,496 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"super-frpc/database"
|
||||
"super-frpc/global"
|
||||
"super-frpc/utils"
|
||||
"super-frpc/postLog"
|
||||
"super-frpc/session"
|
||||
)
|
||||
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username"`
|
||||
Passwd string `json:"passwd"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Passwd string `json:"passwd"`
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username"`
|
||||
Passwd string `json:"passwd"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type ModifyUserRequest struct {
|
||||
UserID int `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
Passwd string `json:"passwd"`
|
||||
}
|
||||
|
||||
type ModifyUserTypeRequest struct {
|
||||
UserID int `json:"userID"`
|
||||
Type string `json:"newType"`
|
||||
}
|
||||
|
||||
type RemoveUserRequest struct {
|
||||
TargetUserID int `json:"targetUserID"`
|
||||
}
|
||||
|
||||
type RemoveSessionRequest struct {
|
||||
SessionID string `json:"sessionID"`
|
||||
}
|
||||
|
||||
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
utils.SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
||||
postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid request method: %s", r.Method))
|
||||
return
|
||||
}
|
||||
|
||||
if !session.ValidateTimeStamp(r.Header, global.Is.Debug) {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header")
|
||||
postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid or missing X-Timestamp in header: %s", r.Header.Get("X-Timestamp")))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[RegisterHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req RegisterRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Passwd == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Username and password are required")
|
||||
postLog.Warning("[RegisterHandler] New user registration failed: username or password is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
||||
postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
||||
return
|
||||
}
|
||||
|
||||
if !session.IsValidPassword(req.Passwd) {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Password does not meet complexity requirements (must contain uppercase, lowercase, digit, and special character)")
|
||||
postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: password \"%s\" does not meet complexity requirements", req.Passwd))
|
||||
return
|
||||
}
|
||||
|
||||
userList, err := database.DBListUsers()
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to list users: %v", err))
|
||||
return
|
||||
}
|
||||
newUserType := ""
|
||||
if len(userList) == 0 {
|
||||
newUserType = "superuser"
|
||||
} else {
|
||||
newUserType = "visitor"
|
||||
}
|
||||
|
||||
userID, err := database.AddUser(req.Username, req.Passwd, newUserType, session.HashPassword, session.IsValidPassword)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := database.GetUserByID(userID)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after registration")
|
||||
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to retrieve user \"%s\" after registration: %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "user registered successfully", map[string]interface{}{
|
||||
"userID": user.UserID,
|
||||
"username": user.Username,
|
||||
"type": user.Type,
|
||||
})
|
||||
}
|
||||
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
utils.SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Invalid request method: %s", r.Method))
|
||||
return
|
||||
}
|
||||
|
||||
if !session.ValidateTimeStamp(r.Header, global.Is.Debug) {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header")
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req LoginRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Passwd == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Username and password are required")
|
||||
postLog.Warning("[LoginHandler] Login failed: username or password is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
||||
postLog.Debug(fmt.Sprintf("[LoginHandler] Login failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := database.GetUserByUsername(req.Username)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, "User not exist")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: User not exist \"%s\"", req.Username))
|
||||
return
|
||||
}
|
||||
|
||||
if !session.VerifyPassword(req.Passwd, user.Passwd) {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, "Invalid password")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: invalid password for user \"%s\"", req.Username))
|
||||
return
|
||||
}
|
||||
|
||||
existingTokenInfo, err := session.GetTokenInfo(user.UserID)
|
||||
if err == nil && existingTokenInfo != nil {
|
||||
utils.SendErrorResponse(w, http.StatusConflict, "User is already logged in")
|
||||
postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: user \"%s\" is already logged in", req.Username))
|
||||
return
|
||||
}
|
||||
|
||||
token, err := session.GenerateToken(user.UserID)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to generate token")
|
||||
postLog.Error(fmt.Sprintf("[LoginHandler] Failed to generate token for user \"%s\": %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := session.JoinSession(user.UserID, user.Username, token); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create session")
|
||||
postLog.Error(fmt.Sprintf("[LoginHandler] Failed to create session for user \"%s\": %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Login successful", map[string]interface{}{
|
||||
"token": token,
|
||||
"userID": user.UserID,
|
||||
"username": user.Username,
|
||||
"type": user.Type,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[LoginHandler] User \"%s\" Login successful", req.Username))
|
||||
}
|
||||
|
||||
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[LogoutHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
sessionTokenMux := session.GetSessionTokenMap()
|
||||
sessionTokenMux.RLock()
|
||||
sessionID := ""
|
||||
for sid, token := range session.GetSessionTokenMapSnapshot() {
|
||||
if token == r.Header.Get("X-Token") {
|
||||
sessionID = sid
|
||||
break
|
||||
}
|
||||
}
|
||||
sessionTokenMux.RUnlock()
|
||||
|
||||
if sessionID == "" {
|
||||
utils.SendErrorResponse(w, http.StatusNotFound, "Session not found for token")
|
||||
postLog.Warning(fmt.Sprintf("[LogoutHandler] Session not found for token from user [%d]%s", userID, session.GetUsernameByID(userID)))
|
||||
return
|
||||
}
|
||||
|
||||
if err := session.RemoveSession(sessionID); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to logout")
|
||||
postLog.Error(fmt.Sprintf("[LogoutHandler] Failed to logout user [%d]%s: %v", userID, session.GetUsernameByID(userID), err))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "Logout successful", nil)
|
||||
postLog.Info(fmt.Sprintf("[LogoutHandler] User [%d]%s Logout successful", userID, session.GetUsernameByID(userID)))
|
||||
}
|
||||
|
||||
func RemoveSessionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodPost, "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req RemoveSessionRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.SessionID == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "SessionID is required")
|
||||
postLog.Warning("[RemoveSessionHandler] SessionID is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if err := session.RemoveSession(req.SessionID); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to remove session: %v", err))
|
||||
postLog.Error(fmt.Sprintf("[RemoveSessionHandler] Failed to remove session %s: %v", req.SessionID, err))
|
||||
return
|
||||
}
|
||||
|
||||
postLog.Info(fmt.Sprintf("[RemoveSessionHandler] User [%d]%s removed session %s", userID, session.GetUsernameByID(userID), req.SessionID))
|
||||
utils.SendSuccessResponse(w, "Session removed successfully", nil)
|
||||
}
|
||||
|
||||
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodPost, "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[CreateUserHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[CreateUserHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req CreateUserRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[CreateUserHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Passwd == "" || req.Type == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Username, password, and type are required")
|
||||
postLog.Warning("[CreateUserHandler] CreateUser failed: username, password, or type is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Type != "admin" && req.Type != "user" && req.Type != "superuser" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'user' or 'superuser'")
|
||||
postLog.Warning(fmt.Sprintf("[CreateUserHandler] CreateUser failed: invalid type: %s", req.Type))
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := database.AddUser(req.Username, req.Passwd, req.Type, session.HashPassword, session.IsValidPassword)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := database.GetUserByID(userID)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after creation")
|
||||
postLog.Error(fmt.Sprintf("[CreateUserHandler] Failed to retrieve user \"%s\" after creation: %v", req.Username, err))
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendSuccessResponse(w, "User created successfully", map[string]interface{}{
|
||||
"userID": user.UserID,
|
||||
"username": user.Username,
|
||||
"type": user.Type,
|
||||
})
|
||||
postLog.Info(fmt.Sprintf("[CreateUserHandler] User \"%s\" created successfully with ID: %d", req.Username, userID))
|
||||
}
|
||||
|
||||
func ModifyUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodPost, "user", "admin", "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req ModifyUserRequest
|
||||
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserID == 0 {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "UserID is required")
|
||||
postLog.Warning("[ModifyUserHandler] ModifyUser failed: UserID is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Username == "" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Username is required")
|
||||
postLog.Warning("[ModifyUserHandler] ModifyUser failed: username is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DBUpdateUser(req.UserID, req.Username, req.Passwd); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[ModifyUserHandler] Failed to update user [%d]: %v", req.UserID, err))
|
||||
return
|
||||
}
|
||||
utils.SendSuccessResponse(w, "User updated successfully", nil)
|
||||
postLog.Info(fmt.Sprintf("[ModifyUserHandler] User [%d]%s updated successfully to: %s", req.UserID, session.GetUsernameByID(req.UserID), req.Username))
|
||||
}
|
||||
|
||||
func ModifyUserTypeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodPost, "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req ModifyUserTypeRequest
|
||||
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
if req.UserID == 0 {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "UserID is required")
|
||||
postLog.Warning("[ModifyUserTypeHandler] ModifyUserType failed: UserID is empty")
|
||||
return
|
||||
}
|
||||
if req.Type != "admin" && req.Type != "visitor" && req.Type != "superuser" {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'visitor' or 'superuser'")
|
||||
postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] ModifyUserType failed: invalid type: %s", req.Type))
|
||||
return
|
||||
}
|
||||
if err := database.DBUpdateUserType(req.UserID, req.Type); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[ModifyUserTypeHandler] Failed to update user type [%d]: %v", req.UserID, err))
|
||||
return
|
||||
}
|
||||
utils.SendSuccessResponse(w, "User type updated successfully", nil)
|
||||
postLog.Info(fmt.Sprintf("[ModifyUserTypeHandler] User [%d]%s type updated successfully to: %s", req.UserID, session.GetUsernameByID(req.UserID), req.Type))
|
||||
}
|
||||
|
||||
func RemoveUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodPost, "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||
postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Failed to read request body: %v", err))
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var req RemoveUserRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
||||
postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Invalid request format: %v", err))
|
||||
return
|
||||
}
|
||||
if req.TargetUserID == 0 {
|
||||
utils.SendErrorResponse(w, http.StatusBadRequest, "TargetUserID is required")
|
||||
postLog.Warning("[RemoveUserHandler] RemoveUser failed: TargetUserID is empty")
|
||||
return
|
||||
}
|
||||
if err := database.RemoveUser(req.TargetUserID); err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
postLog.Error(fmt.Sprintf("[RemoveUserHandler] Failed to remove user [%d]: %v", req.TargetUserID, err))
|
||||
return
|
||||
}
|
||||
utils.SendSuccessResponse(w, "User removed successfully", nil)
|
||||
}
|
||||
|
||||
func ListUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := utils.Auth(w, r, http.MethodGet, "superuser")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ListUserHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
userList, err := database.DBListUsers()
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to list users")
|
||||
postLog.Error(fmt.Sprintf("[ListUserHandler] Failed to list users: %v", err))
|
||||
return
|
||||
}
|
||||
utils.SendSuccessResponse(w, "User list retrieved successfully", userList)
|
||||
}
|
||||
|
||||
func ListActiveSessionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := utils.Auth(w, r, http.MethodGet, "superuser", "admin")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
postLog.Warning(fmt.Sprintf("[ListActiveSessionsHandler] Auth failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
sessions := session.ListActiveSessions()
|
||||
postLog.Debug(fmt.Sprintf("[ListActiveSessionsHandler] User [%d]%s listed %d active sessions", userID, session.GetUsernameByID(userID), len(sessions)))
|
||||
utils.SendSuccessResponse(w, "Active sessions listed", sessions)
|
||||
}
|
||||
|
||||
func isValidInput(input string) bool {
|
||||
return database.IsValidInput(input)
|
||||
}
|
||||
Reference in New Issue
Block a user