Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor to use interfaces #35

Merged
merged 14 commits into from
Jan 24, 2024
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@

# Go workspace file
go.work

# jetbrains editor
.idea
13 changes: 10 additions & 3 deletions cmd/federation.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@ import (
)

func main() {
// bootstrap logger
logger := log.NewLogger("main")

// Load connection string from .env file
err := godotenv.Load()
if err != nil {
log.Warn(fmt.Sprintf("failed to load env, %v", err))
logger.Warn(fmt.Sprintf("failed to load env, %v", err))
}

conn, err := db.Connect()
if err != nil {
log.Fatal("failed connecting to db", err)
logger.Fatal("failed connecting to db", err)
}
defer conn.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep this one and remove the one on line 29. Better to call defer early

lazyguru marked this conversation as resolved.
Show resolved Hide resolved
db.RunMigrations(conn)
http.RunServer()

s := http.NewServer(logger)
s.RunServer()

os.Exit(0)
}
11 changes: 6 additions & 5 deletions internal/db/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ import (
var migrations embed.FS

func RunMigrations(db *sql.DB) {
log.Debug("Running migrations...")
logger := log.NewLogger("db migrations")
logger.Debug("Running migrations...")
driver, err := mysql.WithInstance(db, &mysql.Config{})
if err != nil {
log.Fatal("Error getting MySQL driver", err)
logger.Fatal("Error getting MySQL driver", err)
}
source, _ := iofs.New(migrations, "migrations")
m, err := migrate.NewWithInstance("iofs", source, "mysql", driver)
if err != nil {
log.Fatal("Error connecting to database", err)
logger.Fatal("Error connecting to database", err)
}
if err := m.Up(); err != nil && fmt.Sprintf("%s", err) != "no change" {
log.Fatal("Error running migrations", err)
logger.Fatal("Error running migrations", err)
}
log.Debug("Done!")
logger.Debug("Done!")
}
20 changes: 9 additions & 11 deletions internal/http/routes/activity.go → internal/http/activity.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
package routes
package http

import (
"context"
"encoding/json"
"fmt"
"net/http"
"sublinks/sublinks-federation/internal/activitypub"
"sublinks/sublinks-federation/internal/lemmy"
"sublinks/sublinks-federation/internal/log"

"fmt"

"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/gorilla/mux"
)

func SetupActivityRoutes(r *mux.Router) {
r.HandleFunc("/activities/{action}/{id}", getActivityHandler).Methods("GET")
func (server *Server) SetupActivityRoutes() {
server.Router.HandleFunc("/activities/{action}/{id}", server.getActivityHandler).Methods("GET")
}

func getActivityHandler(w http.ResponseWriter, r *http.Request) {
func (server *Server) getActivityHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var content []byte
switch vars["action"] {
case "create":
obj, err := GetPostActivityObject(vars["id"])
obj, err := server.GetPostActivityObject(vars["id"])
if err != nil {
log.Error("Error reading object", err)
server.Logger.Error("Error reading object", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
Expand All @@ -54,12 +52,12 @@ func getActivityHandler(w http.ResponseWriter, r *http.Request) {
w.Write(content)
}

func GetPostActivityObject(id string) (*activitypub.Post, error) {
func (server *Server) GetPostActivityObject(id string) (*activitypub.Post, error) {
ctx := context.Background()
c := lemmy.GetLemmyClient(ctx)
post, err := c.GetPost(ctx, id)
if err != nil {
log.Error("Error reading post", err)
server.Logger.Error("Error reading post", err)
return nil, err
}
return activitypub.ConvertPostToApub(post), nil
Expand Down
32 changes: 32 additions & 0 deletions internal/http/apub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package http

import (
"net/http"
)

func (server *Server) SetupApubRoutes() {
server.Router.HandleFunc("/users/{user}/inbox", server.getInboxHandler).Methods("GET")
server.Router.HandleFunc("/users/{user}/inbox", server.postInboxHandler).Methods("POST")
server.Router.HandleFunc("/users/{user}/outbox", server.getOutboxHandler).Methods("GET")
server.Router.HandleFunc("/users/{user}/outbox", server.postOutboxHandler).Methods("POST")
}

func (server *Server) getInboxHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Add("content-type", "application/activity+json")
}

func (server *Server) postInboxHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Add("content-type", "application/activity+json")
}

func (server *Server) getOutboxHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Add("content-type", "application/activity+json")
}

func (server *Server) postOutboxHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Add("content-type", "application/activity+json")
}
33 changes: 33 additions & 0 deletions internal/http/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package http

import (
"encoding/json"
"net/http"
)

type RequestError struct {
Msg string `json:"message"`
}

func (server *Server) logMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server.Logger.Request("", r)
next.ServeHTTP(w, r)
})
}

func (server *Server) notFound(w http.ResponseWriter, r *http.Request) {
server.Logger.Request("404 Not Found", r)
w.WriteHeader(http.StatusNotFound)
w.Header().Add("content-type", "application/activity+json")
content, _ := json.Marshal(RequestError{Msg: "not found"})
w.Write(content)
}

func (server *Server) notAllowedMethod(w http.ResponseWriter, r *http.Request) {
server.Logger.Request("405 Method Not Allowed", r)
w.WriteHeader(http.StatusNotFound)
w.Header().Add("content-type", "application/activity+json")
content, _ := json.Marshal(RequestError{Msg: "method not allowed"})
w.Write(content)
}
14 changes: 6 additions & 8 deletions internal/http/routes/post.go → internal/http/post.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package routes
package http

import (
"context"
"encoding/json"
"github.com/gorilla/mux"
"net/http"
"sublinks/sublinks-federation/internal/activitypub"
"sublinks/sublinks-federation/internal/lemmy"
"sublinks/sublinks-federation/internal/log"

"github.com/gorilla/mux"
)

func SetupPostRoutes(r *mux.Router) {
r.HandleFunc("/post/{postId}", getPostHandler).Methods("GET")
func (server *Server) SetupPostRoutes() {
server.Router.HandleFunc("/post/{postId}", server.getPostHandler).Methods("GET")
}

func getPostHandler(w http.ResponseWriter, r *http.Request) {
func (server *Server) getPostHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
ctx := context.Background()
c := lemmy.GetLemmyClient(ctx)
post, err := c.GetPost(ctx, vars["postId"])
if err != nil {
log.Error("Error reading post", err)
server.Logger.Error("Error reading post", err)
return
}
postLd := activitypub.ConvertPostToApub(post)
Expand Down
34 changes: 0 additions & 34 deletions internal/http/routes/apub.go

This file was deleted.

48 changes: 0 additions & 48 deletions internal/http/routes/routes.go

This file was deleted.

35 changes: 28 additions & 7 deletions internal/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,56 @@ package http
import (
"context"
"flag"
"github.com/gorilla/mux"
"net/http"
"os"
"os/signal"
"sublinks/sublinks-federation/internal/http/routes"
"sublinks/sublinks-federation/internal/log"
"time"
)

func RunServer() {
type Server struct {
*mux.Router
log.Logger
}

func NewServer(logger log.Logger) *Server {
r := mux.NewRouter()

return &Server{
Router: r,
Logger: logger,
}
}

func (server *Server) RunServer() {
var wait time.Duration
flag.DurationVar(&wait, "graceful-timeout", time.Second*15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
flag.Parse()

r := routes.SetupRoutes()
server.SetupUserRoutes()
server.SetupPostRoutes()
server.SetupApubRoutes()
server.SetupActivityRoutes()
server.NotFoundHandler = http.HandlerFunc(server.notFound)
server.MethodNotAllowedHandler = http.HandlerFunc(server.notAllowedMethod)
server.Use(server.logMiddleware)

srv := &http.Server{
Addr: "0.0.0.0:8080",
// Good practice to set timeouts to avoid Slowloris attacks.
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
Handler: r, // Pass our instance of gorilla/mux in.
// pass embed of Server for *mux
Handler: server,
}

// Run our server in a goroutine so that it doesn't block.
go func() {
log.Info("Starting server")
server.Logger.Info("Starting server")
if err := srv.ListenAndServe(); err != nil {
log.Error("Error starting server", err)
server.Logger.Error("Error starting server", err)
}
}()

Expand All @@ -52,5 +73,5 @@ func RunServer() {
// Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation.
log.Info("shutting down")
server.Logger.Info("shutting down")
}
Loading
Loading