2022-12-03 03:20:49 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/gob"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-playground/validator/v10"
|
2022-12-05 07:45:51 +00:00
|
|
|
"github.com/go-redis/redis/v9"
|
2022-12-03 03:20:49 +00:00
|
|
|
"github.com/gorilla/sessions"
|
|
|
|
"github.com/labstack/echo-contrib/session"
|
|
|
|
"github.com/labstack/echo/v4"
|
2022-12-05 02:38:34 +00:00
|
|
|
lksdk "github.com/livekit/server-sdk-go"
|
2022-12-03 03:20:49 +00:00
|
|
|
"github.com/mattn/go-mastodon"
|
2022-12-05 07:45:51 +00:00
|
|
|
"github.com/rbcervilla/redisstore/v9"
|
2022-12-03 03:20:49 +00:00
|
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
Template struct {
|
|
|
|
templates *template.Template
|
|
|
|
}
|
|
|
|
|
|
|
|
CustomValidator struct {
|
|
|
|
validator *validator.Validate
|
|
|
|
}
|
|
|
|
|
|
|
|
M map[string]interface{}
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2022-12-05 02:38:34 +00:00
|
|
|
mastAppConfigBase *mastodon.AppConfig = nil
|
|
|
|
mainDB *mongo.Database = nil
|
|
|
|
mainValidator = validator.New()
|
|
|
|
mainConfig *AppConfig
|
|
|
|
lkRoomServiceClient *lksdk.RoomServiceClient
|
2022-12-03 03:20:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
gob.Register(&SessionData{})
|
|
|
|
gob.Register(&M{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2022-12-03 17:44:11 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
// Load config from environment variables and .env
|
|
|
|
mainConfig, err = loadConfig(os.Getenv("AUDON_ENV"))
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
2022-12-03 03:20:49 +00:00
|
|
|
}
|
2022-12-03 17:44:11 +00:00
|
|
|
|
2022-12-05 02:38:34 +00:00
|
|
|
// Setup Livekit RoomService Client
|
2022-12-05 07:45:51 +00:00
|
|
|
lkURL := &url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: mainConfig.Livekit.Host,
|
|
|
|
}
|
|
|
|
lkRoomServiceClient = lksdk.NewRoomServiceClient(lkURL.String(), mainConfig.Livekit.APIKey, mainConfig.Livekit.APISecret)
|
2022-12-05 02:38:34 +00:00
|
|
|
|
2022-12-04 17:52:44 +00:00
|
|
|
backContext, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
2022-12-03 03:20:49 +00:00
|
|
|
defer cancel()
|
2022-12-04 17:52:44 +00:00
|
|
|
|
|
|
|
// Setup database client
|
|
|
|
dbClient, err := mongo.Connect(backContext, options.Client().ApplyURI(mainConfig.MongoURL.String()))
|
2022-12-03 03:20:49 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
2022-12-04 05:19:41 +00:00
|
|
|
mainDB = dbClient.Database(mainConfig.Database.Name)
|
2022-12-04 17:52:44 +00:00
|
|
|
err = createIndexes(backContext)
|
2022-12-03 03:20:49 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
|
2022-12-05 07:45:51 +00:00
|
|
|
// Setup redis client
|
|
|
|
redisClient := redis.NewClient(&redis.Options{
|
|
|
|
Addr: mainConfig.Redis.Host,
|
|
|
|
Username: mainConfig.Redis.User,
|
|
|
|
Password: mainConfig.Redis.Password,
|
|
|
|
DB: 0,
|
|
|
|
})
|
|
|
|
|
2022-12-05 01:11:44 +00:00
|
|
|
// Setup echo server
|
2022-12-03 03:20:49 +00:00
|
|
|
e := echo.New()
|
|
|
|
defer e.Close()
|
|
|
|
|
|
|
|
t := &Template{
|
|
|
|
templates: template.Must(template.ParseFiles("audon-fe/index.html", "audon-fe/dist/index.html")),
|
|
|
|
}
|
|
|
|
e.Renderer = t
|
|
|
|
e.Validator = &CustomValidator{validator: mainValidator}
|
2022-12-05 01:11:44 +00:00
|
|
|
|
|
|
|
// Setup session middleware (currently Audon stores all client data in cookie)
|
2022-12-05 07:45:51 +00:00
|
|
|
redisStore, err := redisstore.NewRedisStore(backContext, redisClient)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
redisStore.KeyPrefix("session_")
|
|
|
|
sessionOptions := sessions.Options{
|
2022-12-04 17:52:44 +00:00
|
|
|
Path: "/",
|
|
|
|
Domain: mainConfig.LocalDomain,
|
|
|
|
MaxAge: 86400 * 30,
|
|
|
|
HttpOnly: true,
|
|
|
|
SameSite: http.SameSiteStrictMode,
|
|
|
|
Secure: true,
|
|
|
|
}
|
|
|
|
if mainConfig.Environment == "development" {
|
2022-12-05 07:45:51 +00:00
|
|
|
sessionOptions.Domain = ""
|
|
|
|
sessionOptions.SameSite = http.SameSiteNoneMode
|
|
|
|
sessionOptions.Secure = false
|
|
|
|
sessionOptions.MaxAge = 3600 * 24
|
|
|
|
sessionOptions.HttpOnly = false
|
2022-12-04 17:52:44 +00:00
|
|
|
}
|
2022-12-05 07:45:51 +00:00
|
|
|
redisStore.Options(sessionOptions)
|
|
|
|
e.Use(session.Middleware(redisStore))
|
2022-12-03 03:20:49 +00:00
|
|
|
|
2022-12-04 19:50:55 +00:00
|
|
|
e.POST("/app/login", loginHandler)
|
|
|
|
e.GET("/app/oauth", oauthHandler)
|
|
|
|
e.GET("/app/verify", verifyHandler)
|
2022-12-03 03:20:49 +00:00
|
|
|
|
2022-12-06 02:28:14 +00:00
|
|
|
e.POST("/app/webhook", livekitWebhookHandler)
|
|
|
|
|
2022-12-04 19:50:55 +00:00
|
|
|
api := e.Group("/api", authMiddleware)
|
|
|
|
api.POST("/room", createRoomHandler)
|
2022-12-05 01:11:44 +00:00
|
|
|
api.GET("/room/:id", joinRoomHandler)
|
|
|
|
api.DELETE("/room/:id", closeRoomHandler)
|
2022-12-05 07:45:51 +00:00
|
|
|
api.PATCH("/room/:room/:user", updatePermissionHandler)
|
2022-12-05 01:11:44 +00:00
|
|
|
|
2022-12-05 07:45:51 +00:00
|
|
|
// e.Static("/", "audon-fe/dist/assets")
|
|
|
|
|
2022-12-04 19:50:55 +00:00
|
|
|
e.Logger.Debug(e.Start(":1323"))
|
2022-12-03 03:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
|
|
|
return t.templates.ExecuteTemplate(w, name, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cv *CustomValidator) Validate(i interface{}) error {
|
|
|
|
if err := cv.validator.Struct(i); err != nil {
|
|
|
|
return wrapValidationError(err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getAppConfig(server string) (*mastodon.AppConfig, error) {
|
|
|
|
if mastAppConfigBase != nil {
|
|
|
|
return &mastodon.AppConfig{
|
|
|
|
Server: server,
|
|
|
|
ClientName: mastAppConfigBase.ClientName,
|
|
|
|
Scopes: mastAppConfigBase.Scopes,
|
|
|
|
Website: mastAppConfigBase.Website,
|
|
|
|
RedirectURIs: mastAppConfigBase.RedirectURIs,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
redirectURI := "urn:ietf:wg:oauth:2.0:oob"
|
2022-12-03 17:44:11 +00:00
|
|
|
u := &url.URL{
|
|
|
|
Host: mainConfig.LocalDomain,
|
2022-12-04 05:19:41 +00:00
|
|
|
Scheme: "https",
|
2022-12-03 17:44:11 +00:00
|
|
|
Path: "/",
|
2022-12-03 03:20:49 +00:00
|
|
|
}
|
2022-12-04 19:50:55 +00:00
|
|
|
u = u.JoinPath("app", "oauth")
|
2022-12-03 17:44:11 +00:00
|
|
|
redirectURI = u.String()
|
2022-12-03 03:20:49 +00:00
|
|
|
|
|
|
|
conf := &mastodon.AppConfig{
|
|
|
|
ClientName: "Audon",
|
|
|
|
Scopes: "read:accounts read:follows",
|
|
|
|
Website: "https://github.com/nmkj-io/audon",
|
|
|
|
RedirectURIs: redirectURI,
|
|
|
|
}
|
|
|
|
|
|
|
|
mastAppConfigBase = conf
|
|
|
|
|
|
|
|
return &mastodon.AppConfig{
|
|
|
|
Server: server,
|
|
|
|
ClientName: conf.ClientName,
|
|
|
|
Scopes: conf.Scopes,
|
|
|
|
Website: conf.Website,
|
|
|
|
RedirectURIs: conf.RedirectURIs,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-12-04 19:50:55 +00:00
|
|
|
func getSession(c echo.Context, sessionID string) (sess *sessions.Session, err error) {
|
|
|
|
sess, err = session.Get(sessionID, c)
|
2022-12-03 03:20:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return sess, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// retrieve user's session, returns invalid cookie error if failed
|
2022-12-04 19:50:55 +00:00
|
|
|
func getSessionData(c echo.Context) (data *SessionData, err error) {
|
|
|
|
sess, err := getSession(c, SESSION_NAME)
|
|
|
|
if err != nil {
|
|
|
|
c.Logger().Error(err)
|
|
|
|
return nil, ErrSessionNotAvailable
|
|
|
|
}
|
|
|
|
|
2022-12-03 03:20:49 +00:00
|
|
|
val := sess.Values[SESSION_DATASTORE_NAME]
|
|
|
|
data, ok := val.(*SessionData)
|
|
|
|
|
|
|
|
if !ok {
|
2022-12-04 19:50:55 +00:00
|
|
|
return nil, ErrInvalidSession
|
2022-12-03 03:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// write user's session, returns error if failed
|
|
|
|
func writeSessionData(c echo.Context, data *SessionData) error {
|
2022-12-04 19:50:55 +00:00
|
|
|
sess, err := getSession(c, SESSION_NAME)
|
2022-12-03 03:20:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sess.Values[SESSION_DATASTORE_NAME] = data
|
|
|
|
|
2022-12-04 05:19:41 +00:00
|
|
|
return sess.Save(c.Request(), c.Response())
|
2022-12-03 03:20:49 +00:00
|
|
|
}
|
2022-12-04 19:50:55 +00:00
|
|
|
|
|
|
|
// handler for GET to /app/verify
|
|
|
|
func verifyHandler(c echo.Context) (err error) {
|
2022-12-05 07:45:51 +00:00
|
|
|
valid, acc, _ := verifyTokenInSession(c)
|
2022-12-04 19:50:55 +00:00
|
|
|
if !valid {
|
|
|
|
return c.NoContent(http.StatusUnauthorized)
|
|
|
|
}
|
|
|
|
|
2022-12-05 07:45:51 +00:00
|
|
|
return c.JSON(http.StatusOK, acc)
|
2022-12-04 19:50:55 +00:00
|
|
|
}
|