203 lines
6.2 KiB
Go
203 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/FrankenBotDev/FrankenAPI/ent/authorizables"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/actions"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/logger"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/punishments"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/security"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/settings"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/stats"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/routes/support"
|
|
"github.com/FrankenBotDev/FrankenAPI/pkg/sys"
|
|
"github.com/joho/godotenv"
|
|
"gopkg.in/olahol/melody.v1"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
"github.com/go-chi/jwtauth/v5"
|
|
_ "github.com/jackc/pgx/v4/stdlib"
|
|
)
|
|
|
|
type LoginRequest struct {
|
|
Username string `json:username`
|
|
Password string `json:password`
|
|
}
|
|
|
|
type GopherInfo struct {
|
|
ID, USERID, CH string
|
|
}
|
|
|
|
type Message struct {
|
|
Command string
|
|
Payload map[string]string
|
|
}
|
|
|
|
func main() {
|
|
mrouter := melody.New()
|
|
gophers := make(map[*melody.Session]*GopherInfo)
|
|
counter := 0
|
|
err := godotenv.Load(".env")
|
|
if err != nil {
|
|
fmt.Println(".end could not be loaded")
|
|
log.Fatal(err)
|
|
}
|
|
client, cContext := sys.InitDB()
|
|
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
|
|
defer client.Close()
|
|
|
|
r := chi.NewRouter()
|
|
r.Use(middleware.Logger)
|
|
|
|
// Basic CORS
|
|
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
|
|
r.Use(cors.Handler(cors.Options{
|
|
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
|
|
AllowedOrigins: []string{"https://*", "http://*"},
|
|
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
|
ExposedHeaders: []string{"Link"},
|
|
AllowCredentials: false,
|
|
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
|
}))
|
|
|
|
r.Use(func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := context.WithValue(r.Context(), sys.EntClientKey, client)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
})
|
|
|
|
r.Use(func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := context.WithValue(r.Context(), sys.ContextClientKey, cContext)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
})
|
|
|
|
// Witout JWT
|
|
r.Group(func(r chi.Router) {
|
|
// AUTHENTIFICATION ROUTER
|
|
// using loging and password from a user to authenticate
|
|
// Returns bearer if login is succefful and error if not
|
|
r.Post("/server/auth", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var loginRequest LoginRequest
|
|
err := json.NewDecoder(r.Body).Decode(&loginRequest)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Queries requested data
|
|
user, err := client.Authorizables.Query().
|
|
Where(authorizables.Username(loginRequest.Username), authorizables.Password(security.NewSHA256asString([]byte(loginRequest.Password)))).
|
|
All(cContext)
|
|
if err != nil {
|
|
http.Error(w, "database error", http.StatusInternalServerError)
|
|
}
|
|
|
|
// Validates user entry if one is present login can proceed
|
|
if len(user) == 1 {
|
|
_, tokenString, err := tokenAuth.Encode(map[string]interface{}{"user_id": 1})
|
|
if err != nil {
|
|
sys.ErrorJsonResponse(w, "encoding_error", err)
|
|
}
|
|
|
|
raw, _ := json.Marshal(`{"type":"success", "token":"` + tokenString + `"}`)
|
|
|
|
w.Write(raw)
|
|
return
|
|
} else {
|
|
|
|
http.Error(w, `{"type":"error", "code":"invalid credentials"}`, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
// With JWT
|
|
r.Group(func(r chi.Router) {
|
|
|
|
r.Use(jwtauth.Verifier(tokenAuth))
|
|
r.Use(jwtauth.Authenticator)
|
|
|
|
r.Mount("/logger", logger.LogRouter())
|
|
r.Mount("/server/punishments", punishments.PunRouter())
|
|
r.Mount("/server/settings", settings.SettingsRouter())
|
|
r.Mount("/server/actions", actions.ActionRouter())
|
|
r.Mount("/server/support", support.SupportRouter())
|
|
r.Mount("/stats", stats.StatsRouter())
|
|
})
|
|
|
|
/* ==================================================
|
|
WEBSOCKET CODE
|
|
================================================== */
|
|
// Websocket Endpoint
|
|
r.Get("/ws", func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Println("websocket connection")
|
|
mrouter.HandleRequest(w, r)
|
|
})
|
|
|
|
// Connect Socket
|
|
mrouter.HandleConnect(func(s *melody.Session) {
|
|
fmt.Println("websocket connecting")
|
|
|
|
// Receives data and assignes it to the gophers map
|
|
gophers[s] = &GopherInfo{strconv.Itoa(counter), s.Request.URL.Query().Get("userid"), s.Request.URL.Query().Get("channel")}
|
|
|
|
// Message user client about Successfull Connection and tell the User
|
|
//s.Write([]byte(`{"type":"success", "id":` + gophers[s].ID + `}`))
|
|
s.Write([]byte(`{"comamnd":"msg", "payload":{"name":"SYSTEM", "userid":"system", "type":"INFO", "msg":"You are connected to:` + s.Request.URL.Query().Get("channel") + `"} }`))
|
|
counter += 1
|
|
})
|
|
|
|
// handle message and send to correct channel after session as created for user
|
|
mrouter.HandleMessage(func(s *melody.Session, msg []byte) {
|
|
info := gophers[s]
|
|
commandJson := Message{}
|
|
json.Unmarshal(msg, &commandJson)
|
|
|
|
// Command Handler
|
|
switch commandJson.Command {
|
|
case "msg":
|
|
raw, err := json.Marshal(`{"command":"msg", "payload":{"name":"` + commandJson.Payload["name"] + `", "userid":"` + commandJson.Payload["userid"] + `", "type":"default", "msg":"` + commandJson.Payload["msg"] + `"} }`)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
mrouter.BroadcastFilter(raw, func(q *melody.Session) bool {
|
|
// log messages here
|
|
return info.CH == q.Request.URL.Query().Get("channel")
|
|
})
|
|
}
|
|
})
|
|
|
|
// Deletes socket out of known Map
|
|
mrouter.HandleDisconnect(func(s *melody.Session) {
|
|
tCH := gophers[s].CH
|
|
|
|
// Sends a message to the channel that a user left
|
|
mrouter.BroadcastFilter([]byte(gophers[s].USERID+"left the channel"), func(q *melody.Session) bool {
|
|
// log messages here
|
|
return gophers[s].CH == tCH
|
|
})
|
|
|
|
// Deletes a user from the internal client map
|
|
delete(gophers, s)
|
|
})
|
|
|
|
fmt.Println("API Online on 0.0.0.0:3000")
|
|
http.ListenAndServe("0.0.0.0:3000", r)
|
|
}
|