177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
![]() |
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"io"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"github.com/pelletier/go-toml/v2"
|
||
|
"github.com/ravener/discord-oauth2"
|
||
|
"github.com/xyproto/randomstring"
|
||
|
"golang.org/x/oauth2"
|
||
|
)
|
||
|
|
||
|
type appConfig struct {
|
||
|
OAuth struct {
|
||
|
ClientID string
|
||
|
ClientSecret string
|
||
|
RedirectUrl string
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type discordUser struct {
|
||
|
LoggedIn bool
|
||
|
Id string
|
||
|
Username string
|
||
|
Avatar string
|
||
|
Discriminator string
|
||
|
Public_Flags float64
|
||
|
Flags float64
|
||
|
Banner string
|
||
|
Accent_Color float64
|
||
|
Global_Name string
|
||
|
Avatar_Decoration_Data struct {
|
||
|
Asset string
|
||
|
Sku_Id string
|
||
|
Expires_At string
|
||
|
}
|
||
|
Collectibles string
|
||
|
Banner_Color string
|
||
|
Clan string
|
||
|
Primary_Guild string
|
||
|
Mfa_Eneabled bool
|
||
|
Locale string
|
||
|
Premium_Type float64
|
||
|
}
|
||
|
|
||
|
var config appConfig
|
||
|
var oauthConfig *oauth2.Config
|
||
|
var oauthToken *oauth2.Token
|
||
|
var userInfo discordUser
|
||
|
|
||
|
func parseConfig(configPath string) {
|
||
|
configFile, err := os.Open(configPath)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
configFileContent, err := io.ReadAll(configFile)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
err = toml.Unmarshal(configFileContent, &config)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func createDiscordOAuthConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||
|
config := &oauth2.Config{
|
||
|
ClientID: clientId,
|
||
|
ClientSecret: clientSecret,
|
||
|
RedirectURL: redirectUrl,
|
||
|
Scopes: []string{discord.ScopeIdentify},
|
||
|
Endpoint: discord.Endpoint,
|
||
|
}
|
||
|
return config
|
||
|
}
|
||
|
|
||
|
func loginDisplay(context *gin.Context) {
|
||
|
if userInfo.LoggedIn {
|
||
|
context.Redirect(http.StatusTemporaryRedirect, "/dashboard")
|
||
|
} else {
|
||
|
context.HTML(http.StatusOK, "root.html", gin.H{
|
||
|
"message": "hello, world!",
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func loginRedirect(context *gin.Context) {
|
||
|
context.Redirect(302, oauthConfig.AuthCodeURL(context.GetString("state")))
|
||
|
}
|
||
|
|
||
|
func authCallback(context *gin.Context) {
|
||
|
oauthState := randomstring.CookieFriendlyString(32)
|
||
|
context.Set("state", oauthState)
|
||
|
var err error
|
||
|
oauthToken, err = oauthConfig.Exchange(context.Request.Context(), context.Query("code"))
|
||
|
if err != nil {
|
||
|
|
||
|
context.HTML(http.StatusInternalServerError, "internalservererror.html", gin.H{
|
||
|
"message": "internalservererror",
|
||
|
})
|
||
|
}
|
||
|
response, err := oauthConfig.Client(context.Request.Context(), oauthToken).Get("https://discord.com/api/users/@me")
|
||
|
if err != nil || response.StatusCode != 200 {
|
||
|
responseMessage := ""
|
||
|
if err != nil {
|
||
|
responseMessage = err.Error()
|
||
|
} else {
|
||
|
responseMessage = response.Status
|
||
|
}
|
||
|
context.HTML(http.StatusInternalServerError, "internalservererror.html", gin.H{
|
||
|
"message": responseMessage,
|
||
|
})
|
||
|
}
|
||
|
defer response.Body.Close()
|
||
|
body, err := io.ReadAll(response.Body)
|
||
|
if err != nil {
|
||
|
context.HTML(http.StatusInternalServerError, "internalservererror.html", gin.H{
|
||
|
"message": err.Error(),
|
||
|
})
|
||
|
}
|
||
|
var discordUserInfo discordUser
|
||
|
json.Unmarshal(body, &discordUserInfo)
|
||
|
discordUserInfo.LoggedIn = true
|
||
|
userInfo = discordUserInfo
|
||
|
context.Redirect(http.StatusTemporaryRedirect, "/dashboard")
|
||
|
}
|
||
|
|
||
|
func dashboardDisplay(context *gin.Context) {
|
||
|
if userInfo.LoggedIn {
|
||
|
context.HTML(http.StatusOK, "dashboard.html", gin.H{
|
||
|
"discordUser": gin.H{
|
||
|
"id": userInfo.Id,
|
||
|
"username": userInfo.Username,
|
||
|
"avatar": userInfo.Avatar,
|
||
|
"discriminator": userInfo.Discriminator,
|
||
|
"public_flags": userInfo.Public_Flags,
|
||
|
"flags": userInfo.Flags,
|
||
|
"banner": userInfo.Banner,
|
||
|
"accent_color": strconv.FormatFloat(userInfo.Accent_Color, 'f', 0, 64),
|
||
|
"global_name": userInfo.Global_Name,
|
||
|
"avatar_decoration_data": gin.H{
|
||
|
"asset": userInfo.Avatar_Decoration_Data.Asset,
|
||
|
"sku_id": userInfo.Avatar_Decoration_Data.Sku_Id,
|
||
|
"expires_at": userInfo.Avatar_Decoration_Data.Expires_At,
|
||
|
},
|
||
|
"collectibles": userInfo.Collectibles,
|
||
|
"banner_color": userInfo.Banner_Color,
|
||
|
"clan": userInfo.Clan,
|
||
|
"primary_guild": userInfo.Primary_Guild,
|
||
|
"mfa_enabled": userInfo.Mfa_Eneabled,
|
||
|
"locale": userInfo.Locale,
|
||
|
"premium_type": userInfo.Premium_Type,
|
||
|
},
|
||
|
})
|
||
|
} else {
|
||
|
context.Redirect(http.StatusTemporaryRedirect, "/")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
parseConfig("config.toml")
|
||
|
oauthConfig = createDiscordOAuthConfig(config.OAuth.ClientID, config.OAuth.ClientSecret, config.OAuth.RedirectUrl)
|
||
|
app := gin.Default()
|
||
|
app.LoadHTMLGlob("src/templates/*.html")
|
||
|
app.Static("/public", "./public")
|
||
|
app.GET("/", loginDisplay)
|
||
|
app.GET("/login", loginRedirect)
|
||
|
app.GET("/auth/callback", authCallback)
|
||
|
app.GET("/dashboard", dashboardDisplay)
|
||
|
app.Run()
|
||
|
}
|