cpularp-manager-api/src/lib/api/api.go

394 lines
12 KiB
Go
Raw Normal View History

2025-04-24 13:58:05 -05:00
package api
import (
"encoding/json"
"fmt"
2025-04-24 13:58:05 -05:00
"log"
"net/http"
"strconv"
2025-04-24 13:58:05 -05:00
authdiscord "example.com/auth/discord"
configserver "example.com/config/server"
character "example.com/database/character"
customization "example.com/database/customization"
function "example.com/database/function"
functionset "example.com/database/functionset"
functiontag "example.com/database/functiontag"
group "example.com/database/group"
inventoryslot "example.com/database/inventoryslot"
item "example.com/database/item"
itemtag "example.com/database/itemtag"
person "example.com/database/person"
role "example.com/database/role"
schematic "example.com/database/schematic"
tier "example.com/database/tier"
user "example.com/database/user"
2025-04-24 13:58:05 -05:00
"github.com/gin-gonic/gin"
"github.com/xyproto/randomstring"
"golang.org/x/oauth2"
"gorm.io/gorm"
)
var GlobalDatabase *gorm.DB
var GlobalConfig configserver.AppConfig
var GlobalOAuth *oauth2.Config
// Private Functions
func createOrUpdateUser(context *gin.Context) {
oauthTokenJSON := context.GetString("discord-oauthtoken")
err := error(nil)
if oauthTokenJSON == "" {
oauthTokenJSON, err = context.Cookie("discord-oauthtoken")
if err != nil {
log.Printf("This really shouldn't happen. %s", err)
context.AbortWithStatus(http.StatusInternalServerError)
}
}
var oauthToken *oauth2.Token
err = json.Unmarshal([]byte(oauthTokenJSON), &oauthToken)
if err != nil {
log.Println(err)
context.AbortWithStatus(http.StatusBadRequest)
}
var currentDiscordUser authdiscord.DiscordUser
result := currentDiscordUser.Get(context, oauthToken, GlobalOAuth)
if result == "" {
if user.Exists(GlobalDatabase, currentDiscordUser.Id) {
currentUser := (*user.Get(GlobalDatabase, []string{currentDiscordUser.Id}))[0]
err := error(nil)
if currentUser.LoggedIn {
context.SetCookie("discord-oauthtoken", currentUser.LoginToken, 0, "", GlobalConfig.API.Domain, false, false)
err = user.Update(
GlobalDatabase,
currentDiscordUser.Id,
currentDiscordUser.Global_Name,
currentDiscordUser.Username,
currentDiscordUser.Avatar,
currentDiscordUser.Avatar_Decoration_Data.Asset,
currentUser.LoginToken,
true,
)
} else {
// context.SetCookie("discord-oauthtoken", oauthTokenJSON, 0, "", GlobalConfig.API.Domain, false, false)
err = user.Update(
GlobalDatabase,
currentDiscordUser.Id,
currentDiscordUser.Global_Name,
currentDiscordUser.Username,
currentDiscordUser.Avatar,
currentDiscordUser.Avatar_Decoration_Data.Asset,
oauthTokenJSON,
true,
)
}
if err != nil {
log.Println(err)
context.AbortWithStatus(http.StatusInternalServerError)
}
} else {
// context.SetCookie("discord-oauthtoken", oauthTokenJSON, 0, "", GlobalConfig.API.Domain, false, false)
err := user.Create(
GlobalDatabase,
currentDiscordUser.Id,
currentDiscordUser.Global_Name,
currentDiscordUser.Username,
currentDiscordUser.Avatar,
currentDiscordUser.Avatar_Decoration_Data.Asset,
oauthTokenJSON,
true,
)
if err != nil {
log.Println(err)
context.AbortWithStatus(http.StatusInternalServerError)
}
}
} else {
log.Printf("This also really shouldn't happen. %s", result)
context.AbortWithStatus(http.StatusInternalServerError)
}
}
func checkAuthentication(context *gin.Context) *oauth2.Token {
oauthTokenJSON, err := context.Cookie("discord-oauthtoken")
if err == nil {
var oauthToken *oauth2.Token
err := json.Unmarshal([]byte(oauthTokenJSON), &oauthToken)
if err == nil {
if oauthToken.Valid() {
if (*user.Get(GlobalDatabase, []string{oauthTokenJSON}))[0].LoggedIn {
return oauthToken
}
}
}
}
context.Redirect(http.StatusTemporaryRedirect, GlobalConfig.GetFrontendRootDomain())
return nil
}
func objectIDStringsToInts(context *gin.Context, objectIDs []string) *[]int {
var objectIDInts []int
for _, objectID := range objectIDs {
objectIDInt, err := strconv.Atoi(objectID)
if err != nil {
objectIDInts = append(objectIDInts, objectIDInt)
} else {
context.AbortWithStatus(http.StatusBadRequest)
}
}
return &objectIDInts
}
2025-04-24 17:08:09 -05:00
// Authentication Workflow
func AuthCallback(context *gin.Context) {
oauthState := randomstring.CookieFriendlyString(32)
context.Set("state", oauthState)
var err error
oauthToken, err := GlobalOAuth.Exchange(context.Request.Context(), context.Query("code"))
if err != nil {
context.Redirect(http.StatusInternalServerError, GlobalConfig.GetFrontendRootDomain())
2025-04-24 17:08:09 -05:00
}
oauthTokenJSON, _ := json.Marshal(oauthToken)
context.SetCookie("discord-oauthtoken", string(oauthTokenJSON), 0, "", GlobalConfig.API.Domain, false, false)
context.Set("discord-oauthtoken", string(oauthTokenJSON))
createOrUpdateUser(context)
log.Println(GlobalConfig.GetFrontendRootDomain())
context.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%sdashboard", GlobalConfig.GetFrontendRootDomain()))
2025-04-24 17:08:09 -05:00
}
func AuthLoginRedirect(context *gin.Context) {
context.Redirect(http.StatusTemporaryRedirect, GlobalOAuth.AuthCodeURL(context.GetString("state")))
2025-04-24 13:58:05 -05:00
}
2025-04-24 17:08:09 -05:00
func AuthLogoutRedirect(context *gin.Context) {
2025-04-24 13:58:05 -05:00
oauthTokenJSON, err := context.Cookie("discord-oauthtoken")
if err == nil {
2025-04-29 00:42:57 -05:00
user.Logout(GlobalDatabase, oauthTokenJSON)
2025-04-24 13:58:05 -05:00
context.SetCookie("discord-oauthtoken", "", -1, "", GlobalConfig.API.Domain, false, true)
} else {
log.Println(err)
}
log.Println(GlobalConfig.GetFrontendRootDomain())
context.Redirect(http.StatusTemporaryRedirect, GlobalConfig.GetFrontendRootDomain())
2025-04-24 13:58:05 -05:00
}
// Create Endpoints (POST)
2025-04-29 00:42:57 -05:00
func CreateGroup(context *gin.Context) {
checkAuthentication(context)
name, nameOk := context.GetQuery("name")
if nameOk {
group.Create(GlobalDatabase, name)
context.Status(http.StatusOK)
} else {
context.AbortWithStatus(http.StatusBadRequest)
}
2025-04-24 13:58:05 -05:00
}
2025-04-29 00:42:57 -05:00
func CreateFunction(context *gin.Context) {
checkAuthentication(context)
name, nameOk := context.GetQuery("name")
tags := context.QueryArray("tags")
requirements := context.QueryArray("requirements")
if nameOk {
function.Create(GlobalDatabase, name, tags, requirements)
context.Status(http.StatusOK)
2025-04-25 17:39:40 -05:00
} else {
context.AbortWithStatus(http.StatusBadRequest)
2025-04-25 17:39:40 -05:00
}
}
2025-04-29 00:42:57 -05:00
func CreateFunctionTag(context *gin.Context) {
checkAuthentication(context)
name := context.Query("name")
if name != "" {
functiontag.Create(GlobalDatabase, name)
context.Status(http.StatusOK)
2025-04-25 17:39:40 -05:00
} else {
context.AbortWithStatus(http.StatusBadRequest)
2025-04-25 17:39:40 -05:00
}
}
// Update Endpoints (PUT)
2025-04-29 00:42:57 -05:00
func UpdateFunction(context *gin.Context) {
checkAuthentication(context)
name, nameOk := context.GetQuery("name")
tags := context.QueryArray("tags")
requirements := context.QueryArray("requirements")
if nameOk {
function.Update(GlobalDatabase, name, tags, requirements)
context.Status(http.StatusOK)
} else {
context.AbortWithStatus(http.StatusBadRequest)
}
}
// Read Endpoints (GET)
2025-04-29 00:42:57 -05:00
func GetDiscordUser(context *gin.Context) {
oauthToken := checkAuthentication(context)
if oauthToken != nil {
var discordUser authdiscord.DiscordUser
result := discordUser.Get(context, oauthToken, GlobalOAuth)
if result != "" {
log.Println(result)
log.Println("Assuming the Discord OAuth Key has expired.")
context.Redirect(http.StatusUnauthorized, fmt.Sprintf("%slogout", GlobalConfig.GetFrontendRootDomain()))
} else {
if (*user.Get(GlobalDatabase, []string{discordUser.Id}))[0].LoggedIn {
context.JSON(http.StatusOK, discordUser)
2025-04-24 17:08:09 -05:00
} else {
context.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%slogout", GlobalConfig.GetFrontendRootDomain()))
2025-04-24 13:58:05 -05:00
}
}
}
}
2025-04-29 00:42:57 -05:00
func GetUserLoggedIn(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"message": (checkAuthentication(context) != nil),
2025-04-24 13:58:05 -05:00
})
}
2025-04-24 17:08:09 -05:00
func GetObjects(context *gin.Context) {
objectIDs, idOk := context.GetQueryArray("id")
if idOk {
switch objectType := context.Param("object"); objectType {
case "user":
context.JSON(http.StatusOK, gin.H{
"user": user.Get(GlobalDatabase, objectIDs)})
case "persons":
context.JSON(http.StatusOK, gin.H{
"persons": person.Get(GlobalDatabase, objectIDs),
})
case "groups":
context.JSON(http.StatusOK, gin.H{
"groups": group.Get(GlobalDatabase, objectIDs),
})
case "characters":
context.JSON(http.StatusOK, gin.H{
"characters": character.Get(GlobalDatabase, objectIDs),
})
case "roles":
context.JSON(http.StatusOK, gin.H{
"roles": role.Get(GlobalDatabase, objectIDs),
})
case "tiers":
objectIDInts := objectIDStringsToInts(context, objectIDs)
context.JSON(http.StatusOK, gin.H{
"tiers": tier.Get(GlobalDatabase, *objectIDInts),
})
case "function-sets":
objectIDInts := objectIDStringsToInts(context, objectIDs)
context.JSON(http.StatusOK, gin.H{
"function_sets": functionset.Get(GlobalDatabase, *objectIDInts),
})
case "functions":
context.JSON(http.StatusOK, gin.H{
"functions": function.Get(GlobalDatabase, objectIDs),
})
case "function-tags":
context.JSON(http.StatusOK, gin.H{
"function_tags": functiontag.Get(GlobalDatabase, objectIDs),
})
case "inventory-slot":
objectIDInts := objectIDStringsToInts(context, objectIDs)
context.JSON(http.StatusOK, gin.H{
"inventory_slot": inventoryslot.Get(GlobalDatabase, *objectIDInts),
})
case "items":
context.JSON(http.StatusOK, gin.H{
"items": item.Get(GlobalDatabase, objectIDs),
})
case "item-tags":
context.JSON(http.StatusOK, gin.H{
"item_tags": itemtag.Get(GlobalDatabase, objectIDs),
})
case "customizations":
context.JSON(http.StatusOK, gin.H{
"customizations": customization.Get(GlobalDatabase, objectIDs),
})
case "schematics":
objectIDInts := objectIDStringsToInts(context, objectIDs)
context.JSON(http.StatusOK, gin.H{
"schematics": schematic.Get(GlobalDatabase, *objectIDInts),
})
}
} else {
2025-04-29 00:42:57 -05:00
context.Status(http.StatusBadRequest)
}
}
func GetAllObjects(context *gin.Context) {
switch objectType := context.Param("object"); objectType {
case "persons":
context.JSON(http.StatusOK, gin.H{
"persons": person.GetAll(GlobalDatabase),
})
case "groups":
context.JSON(http.StatusOK, gin.H{
"groups": group.GetAll(GlobalDatabase),
})
case "characters":
context.JSON(http.StatusOK, gin.H{
"characters": character.GetAll(GlobalDatabase),
})
case "roles":
context.JSON(http.StatusOK, gin.H{
"roles": role.GetAll(GlobalDatabase),
})
case "tiers":
context.JSON(http.StatusOK, gin.H{
"tiers": tier.GetAll(GlobalDatabase),
})
case "function-sets":
context.JSON(http.StatusOK, gin.H{
"function_sets": functionset.GetAll(GlobalDatabase),
})
case "functions":
context.JSON(http.StatusOK, gin.H{
"functions": function.GetAll(GlobalDatabase),
})
case "function-tags":
context.JSON(http.StatusOK, gin.H{
"function_tags": functiontag.GetAll(GlobalDatabase),
})
case "inventory-slot":
context.JSON(http.StatusOK, gin.H{
"inventory_slot": inventoryslot.GetAll(GlobalDatabase),
})
case "items":
context.JSON(http.StatusOK, gin.H{
"items": item.GetAll(GlobalDatabase),
})
case "item-tags":
context.JSON(http.StatusOK, gin.H{
"item_tags": itemtag.GetAll(GlobalDatabase),
})
case "customizations":
context.JSON(http.StatusOK, gin.H{
"customizations": customization.GetAll(GlobalDatabase),
})
case "schematics":
context.JSON(http.StatusOK, gin.H{
"schematics": schematic.GetAll(GlobalDatabase),
})
2025-04-25 17:39:40 -05:00
}
}
// Delete Endpoints (DELETE)
2025-04-25 17:39:40 -05:00
func DeleteFunction(context *gin.Context) {
checkAuthentication(context)
functionNames, ok := context.GetQueryArray("name")
if ok {
function.Delete(GlobalDatabase, functionNames)
context.Status(http.StatusOK)
2025-04-25 17:39:40 -05:00
} else {
context.AbortWithStatus(http.StatusBadRequest)
2025-04-25 17:39:40 -05:00
}
}