package api import ( "encoding/json" "fmt" "log" "net/http" "strconv" 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" "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 } // 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()) } 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())) } func AuthLoginRedirect(context *gin.Context) { context.Redirect(http.StatusTemporaryRedirect, GlobalOAuth.AuthCodeURL(context.GetString("state"))) } func AuthLogoutRedirect(context *gin.Context) { oauthTokenJSON, err := context.Cookie("discord-oauthtoken") if err == nil { user.Logout(GlobalDatabase, oauthTokenJSON) context.SetCookie("discord-oauthtoken", "", -1, "", GlobalConfig.API.Domain, false, true) } else { log.Println(err) } log.Println(GlobalConfig.GetFrontendRootDomain()) context.Redirect(http.StatusTemporaryRedirect, GlobalConfig.GetFrontendRootDomain()) } // Create Endpoints (POST) 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) } } 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) } else { context.AbortWithStatus(http.StatusBadRequest) } } func CreateFunctionTag(context *gin.Context) { checkAuthentication(context) name := context.Query("name") if name != "" { functiontag.Create(GlobalDatabase, name) context.Status(http.StatusOK) } else { context.AbortWithStatus(http.StatusBadRequest) } } // Update Endpoints (PUT) 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) 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) } else { context.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%slogout", GlobalConfig.GetFrontendRootDomain())) } } } } func GetUserLoggedIn(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "message": (checkAuthentication(context) != nil), }) } 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 { 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), }) } } // Delete Endpoints (DELETE) func DeleteFunction(context *gin.Context) { checkAuthentication(context) functionNames, ok := context.GetQueryArray("name") if ok { function.Delete(GlobalDatabase, functionNames) context.Status(http.StatusOK) } else { context.AbortWithStatus(http.StatusBadRequest) } }