From a66b676481d273508927e64a22e388dc302890ba Mon Sep 17 00:00:00 2001
From: FChannel <>
Date: Sun, 22 May 2022 14:08:36 -0700
Subject: route organization
---
main.go | 24 +-
route/routes/actor.go | 680 ++++++++++++++++++++++++++++++++++++++++++++++
route/routes/admin.go | 291 ++++++++++++++++++++
route/routes/api.go | 55 ++++
route/routes/boardmgmt.go | 47 ++++
route/routes/main.go | 100 +++++++
route/routes/news.go | 73 +++++
route/routes/webfinger.go | 58 ++++
route/structs.go | 56 ++++
route/util.go | 377 +++++++++++++++++++++++++
routes/404.go | 9 -
routes/actor.go | 377 -------------------------
routes/admin.go | 290 --------------------
routes/api.go | 55 ----
routes/archive.go | 53 ----
routes/boardmgmt.go | 47 ----
routes/follow.go | 17 --
routes/inbox.go | 9 -
routes/index.go | 69 -----
routes/media.go | 7 -
routes/news.go | 72 -----
routes/outbox.go | 106 --------
routes/post.go | 189 -------------
routes/structs.go | 56 ----
routes/util.go | 377 -------------------------
routes/webfinger.go | 58 ----
26 files changed, 1751 insertions(+), 1801 deletions(-)
create mode 100644 route/routes/actor.go
create mode 100644 route/routes/admin.go
create mode 100644 route/routes/api.go
create mode 100644 route/routes/boardmgmt.go
create mode 100644 route/routes/main.go
create mode 100644 route/routes/news.go
create mode 100644 route/routes/webfinger.go
create mode 100644 route/structs.go
create mode 100644 route/util.go
delete mode 100644 routes/404.go
delete mode 100644 routes/actor.go
delete mode 100644 routes/admin.go
delete mode 100644 routes/api.go
delete mode 100644 routes/archive.go
delete mode 100644 routes/boardmgmt.go
delete mode 100644 routes/follow.go
delete mode 100644 routes/inbox.go
delete mode 100644 routes/index.go
delete mode 100644 routes/media.go
delete mode 100644 routes/news.go
delete mode 100644 routes/outbox.go
delete mode 100644 routes/post.go
delete mode 100644 routes/structs.go
delete mode 100644 routes/util.go
delete mode 100644 routes/webfinger.go
diff --git a/main.go b/main.go
index cc167ed..0caa138 100644
--- a/main.go
+++ b/main.go
@@ -7,7 +7,8 @@ import (
"github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/config"
"github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/routes"
+ "github.com/FChannel0/FChannel-Server/route"
+ "github.com/FChannel0/FChannel-Server/route/routes"
"github.com/FChannel0/FChannel-Server/util"
"github.com/FChannel0/FChannel-Server/webfinger"
"github.com/gofiber/fiber/v2"
@@ -28,7 +29,7 @@ func main() {
template := html.New("./views", ".html")
template.Debug(true)
- routes.TemplateFunctions(template)
+ route.TemplateFunctions(template)
app := fiber.New(fiber.Config{
AppName: "FChannel",
@@ -68,6 +69,8 @@ func main() {
app.Get("/"+config.Key+"/newsdelete", routes.AdminNewsDelete)
app.All("/"+config.Key+"/:actor/follow", routes.AdminFollow)
app.Get("/"+config.Key+"/:actor", routes.AdminActorIndex)
+
+ // News routes
app.Get("/news", routes.NewsGet)
// Board managment
@@ -81,24 +84,25 @@ func main() {
app.Get("/poparchive", routes.BoardPopArchive)
app.Get("/autosubscribe", routes.BoardAutoSubscribe)
app.Get("/blacklist", routes.BoardBlacklist)
- app.Get("/report", routes.BoardBlacklist)
+ app.Get("/report", routes.BoardReport)
+
+ // Webfinger routes
app.Get("/.well-known/webfinger", routes.Webfinger)
+
+ // API routes
app.Get("/api/media", routes.Media)
- // Board actor
- app.Get("/:actor/catalog", routes.CatalogGet)
+ // Board actor routes
+ app.Get("/:actor/catalog", routes.ActorCatalogGet)
app.Post("/:actor/inbox", routes.ActorInbox)
app.All("/:actor/outbox", routes.ActorOutbox)
app.Get("/:actor/following", routes.ActorFollowing)
app.All("/:actor/followers", routes.ActorFollowers)
app.Get("/:actor/reported", routes.ActorReported)
app.Get("/:actor/archive", routes.ActorArchive)
- app.Get("/:actor", routes.OutboxGet)
+ app.Get("/:actor", routes.ActorOutboxGet)
app.Post("/:actor", routes.ActorPost)
- app.Get("/:actor/:post", routes.PostGet)
-
- //404 handler
- app.Use(routes.NotFound)
+ app.Get("/:actor/:post", routes.ActorPostGet)
db.PrintAdminAuth()
diff --git a/route/routes/actor.go b/route/routes/actor.go
new file mode 100644
index 0000000..e27133b
--- /dev/null
+++ b/route/routes/actor.go
@@ -0,0 +1,680 @@
+package routes
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "regexp"
+ "strconv"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/post"
+ "github.com/FChannel0/FChannel-Server/route"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
+)
+
+func ActorInbox(ctx *fiber.Ctx) error {
+ activity, err := activitypub.GetActivityFromJson(ctx)
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if activity.Actor.PublicKey.Id == "" {
+ nActor, err := activitypub.FingerActor(activity.Actor.Id)
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ activity.Actor = &nActor
+ }
+
+ if !activity.Actor.VerifyHeaderSignature(ctx) {
+ response := activity.Reject()
+ return response.MakeRequestInbox()
+ }
+
+ switch activity.Type {
+ case "Create":
+ for _, e := range activity.To {
+ actor := activitypub.Actor{Id: e}
+ if res, err := actor.IsLocal(); err == nil && res {
+ if res, err := activity.Actor.IsLocal(); err == nil && res {
+ reqActivity := activitypub.Activity{Id: activity.Object.Id}
+ col, err := reqActivity.GetCollection()
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if len(col.OrderedItems) < 1 {
+ break
+ }
+
+ if err := activity.Object.WriteCache(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ actor, err := activitypub.GetActorFromDB(e)
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if err := actor.ArchivePosts(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ //SendToFollowers(e, activity)
+ } else if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ } else if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ }
+
+ break
+
+ case "Delete":
+ for _, e := range activity.To {
+ actor, err := activitypub.GetActorFromDB(e)
+ if err != nil {
+ return util.MakeError(err, "")
+ }
+
+ if actor.Id != "" && actor.Id != config.Domain {
+ if activity.Object.Replies.OrderedItems != nil {
+ for _, k := range activity.Object.Replies.OrderedItems {
+ if err := k.Tombstone(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ }
+ }
+
+ if err := activity.Object.Tombstone(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ if err := actor.UnArchiveLast(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ break
+ }
+ }
+ break
+
+ case "Follow":
+ for _, e := range activity.To {
+ if _, err := activitypub.GetActorFromDB(e); err == nil {
+ response := activity.AcceptFollow()
+ response, err := response.SetActorFollower()
+
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if err := response.MakeRequestInbox(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ alreadyFollowing, err := response.Actor.IsAlreadyFollowing(response.Object.Id)
+
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ objActor, err := activitypub.FingerActor(response.Object.Actor)
+
+ if err != nil || objActor.Id == "" {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ reqActivity := activitypub.Activity{Id: objActor.Following}
+ remoteActorFollowingCol, err := reqActivity.GetCollection()
+
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ alreadyFollow := false
+
+ for _, e := range remoteActorFollowingCol.Items {
+ if e.Id == response.Actor.Id {
+ alreadyFollowing = true
+ }
+ }
+
+ autoSub, err := response.Actor.GetAutoSubscribe()
+
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if autoSub && !alreadyFollow && alreadyFollowing {
+ followActivity, err := response.Actor.MakeFollowActivity(response.Object.Actor)
+
+ if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+
+ if err := followActivity.MakeRequestOutbox(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ }
+ } else if err != nil {
+ return util.MakeError(err, "ActorInbox")
+ } else {
+ config.Log.Println("follow request for rejected")
+ response := activity.Reject()
+ return response.MakeRequestInbox()
+ }
+ }
+ break
+
+ case "Reject":
+ if activity.Object.Object.Type == "Follow" {
+ config.Log.Println("follow rejected")
+ if _, err := activity.SetActorFollowing(); err != nil {
+ return util.MakeError(err, "ActorInbox")
+ }
+ }
+ break
+ }
+
+ return nil
+}
+
+func ActorOutbox(ctx *fiber.Ctx) error {
+ //var activity activitypub.Activity
+ actor, err := webfinger.GetActorFromPath(ctx.Path(), "/")
+ if err != nil {
+ return util.MakeError(err, "ActorOutbox")
+ }
+
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ actor.GetOutbox(ctx)
+ return nil
+ }
+
+ return route.ParseOutboxRequest(ctx, actor)
+}
+
+func ActorFollowing(ctx *fiber.Ctx) error {
+ actor, _ := activitypub.GetActorFromDB(config.Domain + "/" + ctx.Params("actor"))
+ return actor.GetFollowingResp(ctx)
+}
+
+func ActorFollowers(ctx *fiber.Ctx) error {
+ actor, _ := activitypub.GetActorFromDB(config.Domain + "/" + ctx.Params("actor"))
+ return actor.GetFollowersResp(ctx)
+}
+
+func ActorReported(c *fiber.Ctx) error {
+ // STUB
+
+ return c.SendString("actor reported")
+}
+
+func ActorArchive(c *fiber.Ctx) error {
+ // STUB
+
+ return c.SendString("actor archive")
+}
+
+func ActorPost(ctx *fiber.Ctx) error {
+ header, _ := ctx.FormFile("file")
+
+ if ctx.FormValue("inReplyTo") == "" && header == nil {
+ return ctx.Render("403", fiber.Map{
+ "message": "Media is required for new posts",
+ })
+ }
+
+ var file multipart.File
+
+ if header != nil {
+ file, _ = header.Open()
+ }
+
+ if file != nil && header.Size > (7<<20) {
+ return ctx.Render("403", fiber.Map{
+ "message": "7MB max file size",
+ })
+ }
+
+ if is, _ := util.IsPostBlacklist(ctx.FormValue("comment")); is {
+ errors.New("\n\nBlacklist post blocked\n\n")
+ return ctx.Redirect("/", 301)
+ }
+
+ if ctx.FormValue("inReplyTo") == "" || file == nil {
+ if ctx.FormValue("comment") == "" && ctx.FormValue("subject") == "" {
+ return ctx.Render("403", fiber.Map{
+ "message": "Comment or Subject required",
+ })
+ }
+ }
+
+ if len(ctx.FormValue("comment")) > 2000 {
+ return ctx.Render("403", fiber.Map{
+ "message": "Comment limit 2000 characters",
+ })
+ }
+
+ if len(ctx.FormValue("subject")) > 100 || len(ctx.FormValue("name")) > 100 || len(ctx.FormValue("options")) > 100 {
+ return ctx.Render("403", fiber.Map{
+ "message": "Name, Subject or Options limit 100 characters",
+ })
+ }
+
+ if ctx.FormValue("captcha") == "" {
+ return ctx.Render("403", fiber.Map{
+ "message": "Incorrect Captcha",
+ })
+ }
+
+ b := bytes.Buffer{}
+ we := multipart.NewWriter(&b)
+
+ if file != nil {
+ var fw io.Writer
+
+ fw, err := we.CreateFormFile("file", header.Filename)
+
+ if err != nil {
+ errors.New("error with form file create")
+ }
+ _, err = io.Copy(fw, file)
+
+ if err != nil {
+ errors.New("error with form file copy")
+ }
+ }
+
+ reply, _ := post.ParseCommentForReply(ctx.FormValue("comment"))
+
+ form, _ := ctx.MultipartForm()
+
+ for key, r0 := range form.Value {
+ if key == "captcha" {
+ err := we.WriteField(key, ctx.FormValue("captchaCode")+":"+ctx.FormValue("captcha"))
+ if err != nil {
+ errors.New("error with writing captcha field")
+ }
+ } else if key == "name" {
+ name, tripcode, _ := post.CreateNameTripCode(ctx)
+
+ err := we.WriteField(key, name)
+ if err != nil {
+ errors.New("error with writing name field")
+ }
+
+ err = we.WriteField("tripcode", tripcode)
+ if err != nil {
+ errors.New("error with writing tripcode field")
+ }
+ } else {
+ err := we.WriteField(key, r0[0])
+ if err != nil {
+ errors.New("error with writing field")
+ }
+ }
+ }
+
+ if ctx.FormValue("inReplyTo") == "" && reply != "" {
+ err := we.WriteField("inReplyTo", reply)
+ if err != nil {
+ errors.New("error with writing inReplyTo field")
+ }
+ }
+
+ we.Close()
+
+ sendTo := ctx.FormValue("sendTo")
+
+ req, err := http.NewRequest("POST", sendTo, &b)
+
+ if err != nil {
+ errors.New("error with post form req")
+ }
+
+ req.Header.Set("Content-Type", we.FormDataContentType())
+
+ resp, err := util.RouteProxy(req)
+
+ if err != nil {
+ errors.New("error with post form resp")
+ }
+
+ defer resp.Body.Close()
+ body, _ := ioutil.ReadAll(resp.Body)
+
+ if resp.StatusCode == 200 {
+
+ var obj activitypub.ObjectBase
+
+ obj = post.ParseOptions(ctx, obj)
+ for _, e := range obj.Option {
+ if e == "noko" || e == "nokosage" {
+ return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/"+util.ShortURL(ctx.FormValue("sendTo"), string(body)), 301)
+ }
+ }
+
+ if ctx.FormValue("returnTo") == "catalog" {
+ return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/catalog", 301)
+ } else {
+ return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301)
+ }
+ }
+
+ if resp.StatusCode == 403 {
+ return ctx.Render("403", fiber.Map{
+ "message": string(body),
+ })
+ }
+
+ return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301)
+}
+
+func ActorPostGet(ctx *fiber.Ctx) error {
+
+ actor, err := activitypub.GetActorByNameFromDB(ctx.Params("actor"))
+ if err != nil {
+ return nil
+ }
+
+ // this is a activitpub json request return json instead of html page
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ route.GetActorPost(ctx, ctx.Path())
+ return nil
+ }
+
+ re := regexp.MustCompile("\\w+$")
+ postId := re.FindString(ctx.Path())
+
+ inReplyTo := actor.Id + "/" + postId
+
+ var data route.PageData
+
+ re = regexp.MustCompile("f(\\w|[!@#$%^&*<>])+-(\\w|[!@#$%^&*<>])+")
+
+ if re.MatchString(ctx.Path()) { // if non local actor post
+ name := activitypub.GetActorFollowNameFromPath(ctx.Path())
+
+ followActors, err := actor.GetFollowFromName(name)
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+
+ followCollection, err := activitypub.GetActorsFollowPostFromId(followActors, postId)
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+
+ if len(followCollection.OrderedItems) > 0 {
+ data.Board.InReplyTo = followCollection.OrderedItems[0].Id
+ data.Posts = append(data.Posts, followCollection.OrderedItems[0])
+
+ actor, err := activitypub.FingerActor(data.Board.InReplyTo)
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+
+ data.Board.Post.Actor = actor.Id
+ }
+ } else {
+ obj := activitypub.ObjectBase{Id: inReplyTo}
+ collection, err := obj.GetCollectionFromPath()
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+
+ if collection.Actor.Id != "" {
+ data.Board.Post.Actor = collection.Actor.Id
+ data.Board.InReplyTo = inReplyTo
+ }
+
+ if len(collection.OrderedItems) > 0 {
+ data.Posts = append(data.Posts, collection.OrderedItems[0])
+ }
+ }
+
+ if len(data.Posts) > 0 {
+ data.PostId = util.ShortURL(data.Board.To, data.Posts[0].Id)
+ }
+
+ data.Board.Name = actor.Name
+ data.Board.PrefName = actor.PreferredUsername
+ data.Board.To = actor.Outbox
+ data.Board.Actor = actor
+ data.Board.Summary = actor.Summary
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Domain = config.Domain
+ data.Board.Restricted = actor.Restricted
+ data.ReturnTo = "feed"
+
+ capt, err := util.GetRandomCaptcha()
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+ data.Board.Captcha = config.Domain + "/" + capt
+ data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
+
+ data.Instance, err = activitypub.GetActorFromDB(config.Domain)
+ if err != nil {
+ return util.MakeError(err, "PostGet")
+ }
+
+ data.Key = config.Key
+ data.Boards = webfinger.Boards
+
+ data.Title = "/" + data.Board.Name + "/ - " + data.PostId
+
+ if len(data.Posts) > 0 {
+ data.Meta.Description = data.Posts[0].Content
+ data.Meta.Url = data.Posts[0].Id
+ data.Meta.Title = data.Posts[0].Name
+ data.Meta.Preview = data.Posts[0].Preview.Href
+ }
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("npost", fiber.Map{
+ "page": data,
+ }, "layouts/main")
+}
+
+func ActorCatalogGet(ctx *fiber.Ctx) error {
+ actorName := ctx.Params("actor")
+ actor, err := activitypub.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return util.MakeError(err, "CatalogGet")
+ }
+
+ collection, err := actor.GetCatalogCollection()
+
+ // TODO: implement this in template functions
+ // "showArchive": func() bool {
+ // col, err := db.GetActorCollectionDBTypeLimit(collection.Actor.Id, "Archive", 1)
+ // if err != nil {
+ // // TODO: figure out what to do here
+ // panic(err)
+ // }
+ //
+ // if len(col.OrderedItems) > 0 {
+ // return true
+ // }
+ // return false
+ //},
+
+ var data route.PageData
+ data.Board.Name = actor.Name
+ data.Board.PrefName = actor.PreferredUsername
+ data.Board.InReplyTo = ""
+ data.Board.To = actor.Outbox
+ data.Board.Actor = actor
+ data.Board.Summary = actor.Summary
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Domain = config.Domain
+ data.Board.Restricted = actor.Restricted
+ data.Key = config.Key
+ data.ReturnTo = "catalog"
+
+ data.Board.Post.Actor = actor.Id
+
+ data.Instance, err = activitypub.GetActorFromDB(config.Domain)
+ if err != nil {
+ return util.MakeError(err, "CatalogGet")
+ }
+
+ capt, err := util.GetRandomCaptcha()
+ if err != nil {
+ return util.MakeError(err, "CatalogGet")
+ }
+
+ data.Board.Captcha = config.Domain + "/" + capt
+ data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
+
+ data.Title = "/" + data.Board.Name + "/ - catalog"
+
+ data.Boards = webfinger.Boards
+ data.Posts = collection.OrderedItems
+
+ data.Meta.Description = data.Board.Summary
+ data.Meta.Url = data.Board.Actor.Id
+ data.Meta.Title = data.Title
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("catalog", fiber.Map{
+ "page": data,
+ }, "layouts/main")
+}
+
+func ActorOutboxGet(ctx *fiber.Ctx) error {
+ actor, err := activitypub.GetActorByNameFromDB(ctx.Params("actor"))
+
+ if err != nil {
+ return nil
+ }
+
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ actor.GetInfoResp(ctx)
+ return nil
+ }
+
+ var page int
+ if postNum := ctx.Query("page"); postNum != "" {
+ if page, err = strconv.Atoi(postNum); err != nil {
+ return util.MakeError(err, "OutboxGet")
+ }
+ }
+
+ collection, err := actor.WantToServePage(page)
+ if err != nil {
+ return util.MakeError(err, "OutboxGet")
+ }
+
+ var offset = 15
+ var pages []int
+ pageLimit := (float64(collection.TotalItems) / float64(offset))
+
+ if pageLimit > 11 {
+ pageLimit = 11
+ }
+
+ for i := 0.0; i < pageLimit; i++ {
+ pages = append(pages, int(i))
+ }
+
+ var data route.PageData
+ data.Board.Name = actor.Name
+ data.Board.PrefName = actor.PreferredUsername
+ data.Board.Summary = actor.Summary
+ data.Board.InReplyTo = ""
+ data.Board.To = actor.Outbox
+ data.Board.Actor = actor
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Domain = config.Domain
+ data.Board.Restricted = actor.Restricted
+ data.CurrentPage = page
+ data.ReturnTo = "feed"
+
+ data.Board.Post.Actor = actor.Id
+
+ capt, err := util.GetRandomCaptcha()
+ if err != nil {
+ return util.MakeError(err, "OutboxGet")
+ }
+ data.Board.Captcha = config.Domain + "/" + capt
+ data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
+
+ data.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
+
+ data.Key = config.Key
+
+ data.Boards = webfinger.Boards
+ data.Posts = collection.OrderedItems
+
+ data.Pages = pages
+ data.TotalPage = len(data.Pages) - 1
+
+ data.Meta.Description = data.Board.Summary
+ data.Meta.Url = data.Board.Actor.Id
+ data.Meta.Title = data.Title
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("nposts", fiber.Map{
+ "page": data,
+ }, "layouts/main")
+}
+
+func ActorArchiveGet(ctx *fiber.Ctx) error {
+ collection := ctx.Locals("collection").(activitypub.Collection)
+ actor := collection.Actor
+
+ var returnData route.PageData
+ returnData.Board.Name = actor.Name
+ returnData.Board.PrefName = actor.PreferredUsername
+ returnData.Board.InReplyTo = ""
+ returnData.Board.To = actor.Outbox
+ returnData.Board.Actor = actor
+ returnData.Board.Summary = actor.Summary
+ returnData.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ returnData.Board.Domain = config.Domain
+ returnData.Board.Restricted = actor.Restricted
+ returnData.Key = config.Key
+ returnData.ReturnTo = "archive"
+
+ returnData.Board.Post.Actor = actor.Id
+
+ var err error
+ returnData.Instance, err = activitypub.GetActorFromDB(config.Domain)
+
+ capt, err := util.GetRandomCaptcha()
+ if err != nil {
+ return util.MakeError(err, "ArchiveGet")
+ }
+ returnData.Board.Captcha = config.Domain + "/" + capt
+ returnData.Board.CaptchaCode = post.GetCaptchaCode(returnData.Board.Captcha)
+
+ returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
+
+ returnData.Boards = webfinger.Boards
+
+ returnData.Posts = collection.OrderedItems
+
+ returnData.Themes = &config.Themes
+ returnData.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("archive", fiber.Map{
+ "page": returnData,
+ }, "layouts/main")
+}
diff --git a/route/routes/admin.go b/route/routes/admin.go
new file mode 100644
index 0000000..86e12c6
--- /dev/null
+++ b/route/routes/admin.go
@@ -0,0 +1,291 @@
+package routes
+
+import (
+ "bytes"
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+ "time"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/route"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
+)
+
+func AdminVerify(ctx *fiber.Ctx) error {
+ identifier := ctx.FormValue("id")
+ code := ctx.FormValue("code")
+
+ var verify util.Verify
+ verify.Identifier = identifier
+ verify.Code = code
+
+ j, _ := json.Marshal(&verify)
+
+ req, err := http.NewRequest("POST", config.Domain+"/auth", bytes.NewBuffer(j))
+
+ if err != nil {
+ return util.MakeError(err, "AdminVerify")
+ }
+
+ req.Header.Set("Content-Type", config.ActivityStreams)
+
+ resp, err := http.DefaultClient.Do(req)
+
+ if err != nil {
+ return util.MakeError(err, "AdminVerify")
+ }
+
+ defer resp.Body.Close()
+
+ rBody, _ := ioutil.ReadAll(resp.Body)
+
+ body := string(rBody)
+
+ if resp.StatusCode != 200 {
+ return ctx.Redirect("/"+config.Key, http.StatusPermanentRedirect)
+ }
+
+ ctx.Cookie(&fiber.Cookie{
+ Name: "session_token",
+ Value: body + "|" + verify.Code,
+ Expires: time.Now().UTC().Add(60 * 60 * 48 * time.Second),
+ })
+
+ return ctx.Redirect("/", http.StatusSeeOther)
+}
+
+// TODO remove this route it is mostly unneeded
+func AdminAuth(ctx *fiber.Ctx) error {
+ var verify util.Verify
+
+ err := json.Unmarshal(ctx.Body(), &verify)
+
+ if err != nil {
+ return util.MakeError(err, "AdminAuth")
+ }
+
+ v, _ := util.GetVerificationByCode(verify.Code)
+
+ if v.Identifier == verify.Identifier {
+ _, err := ctx.Write([]byte(v.Board))
+ return util.MakeError(err, "AdminAuth")
+ }
+
+ ctx.Response().Header.SetStatusCode(http.StatusBadRequest)
+ _, err = ctx.Write([]byte(""))
+
+ return util.MakeError(err, "AdminAuth")
+}
+
+func AdminIndex(ctx *fiber.Ctx) error {
+ id, _ := util.GetPasswordFromSession(ctx)
+ actor, _ := webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
+
+ if actor.Id == "" {
+ actor, _ = activitypub.GetActorByNameFromDB(config.Domain)
+ }
+
+ if id == "" || (id != actor.Id && id != config.Domain) {
+ return ctx.Render("verify", fiber.Map{})
+ }
+
+ actor, err := activitypub.GetActor(config.Domain)
+
+ if err != nil {
+ return util.MakeError(err, "AdminIndex")
+ }
+
+ reqActivity := activitypub.Activity{Id: actor.Following}
+ follow, _ := reqActivity.GetCollection()
+ follower, _ := reqActivity.GetCollection()
+
+ var following []string
+ var followers []string
+
+ for _, e := range follow.Items {
+ following = append(following, e.Id)
+ }
+
+ for _, e := range follower.Items {
+ followers = append(followers, e.Id)
+ }
+
+ var adminData route.AdminPage
+ adminData.Following = following
+ adminData.Followers = followers
+ adminData.Actor = actor.Id
+ adminData.Key = config.Key
+ adminData.Domain = config.Domain
+ adminData.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ adminData.Title = actor.Name + " Admin page"
+
+ adminData.Boards = webfinger.Boards
+
+ adminData.Board.Post.Actor = actor.Id
+
+ adminData.PostBlacklist, _ = util.GetRegexBlacklist()
+
+ adminData.Themes = &config.Themes
+
+ return ctx.Render("admin", fiber.Map{
+ "page": adminData,
+ })
+}
+
+func AdminFollow(ctx *fiber.Ctx) error {
+ follow := ctx.FormValue("follow")
+ actorId := ctx.FormValue("actor")
+
+ actor := activitypub.Actor{Id: actorId}
+ followActivity, _ := actor.MakeFollowActivity(follow)
+
+ objActor := activitypub.Actor{Id: followActivity.Object.Actor}
+
+ if isLocal, _ := objActor.IsLocal(); !isLocal && followActivity.Actor.Id == config.Domain {
+ _, err := ctx.Write([]byte("main board can only follow local boards. Create a new board and then follow outside boards from it."))
+ return util.MakeError(err, "AdminIndex")
+ }
+
+ if actor, _ := activitypub.FingerActor(follow); actor.Id != "" {
+ if err := followActivity.MakeRequestOutbox(); err != nil {
+ return util.MakeError(err, "AdminFollow")
+ }
+ }
+
+ var redirect string
+ actor, _ = webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
+
+ if actor.Name != "main" {
+ redirect = actor.Name
+ }
+
+ return ctx.Redirect("/"+config.Key+"/"+redirect, http.StatusSeeOther)
+}
+
+func AdminAddBoard(ctx *fiber.Ctx) error {
+ actor, _ := activitypub.GetActorFromDB(config.Domain)
+
+ if hasValidation := actor.HasValidation(ctx); !hasValidation {
+ return nil
+ }
+
+ var newActorActivity activitypub.Activity
+ var board activitypub.Actor
+
+ var restrict bool
+ if ctx.FormValue("restricted") == "True" {
+ restrict = true
+ } else {
+ restrict = false
+ }
+
+ board.Name = ctx.FormValue("name")
+ board.PreferredUsername = ctx.FormValue("prefname")
+ board.Summary = ctx.FormValue("summary")
+ board.Restricted = restrict
+
+ newActorActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
+ newActorActivity.Type = "New"
+
+ var nobj activitypub.ObjectBase
+ newActorActivity.Actor = &actor
+ newActorActivity.Object = &nobj
+
+ newActorActivity.Object.Alias = board.Name
+ newActorActivity.Object.Name = board.PreferredUsername
+ newActorActivity.Object.Summary = board.Summary
+ newActorActivity.Object.Sensitive = board.Restricted
+
+ newActorActivity.MakeRequestOutbox()
+ return ctx.Redirect("/"+config.Key, http.StatusSeeOther)
+}
+
+func AdminPostNews(c *fiber.Ctx) error {
+ // STUB
+
+ return c.SendString("admin post news")
+}
+
+func AdminNewsDelete(c *fiber.Ctx) error {
+ // STUB
+
+ return c.SendString("admin news delete")
+}
+
+func AdminActorIndex(ctx *fiber.Ctx) error {
+ actor, _ := webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
+
+ reqActivity := activitypub.Activity{Id: actor.Following}
+ follow, _ := reqActivity.GetCollection()
+
+ reqActivity.Id = actor.Followers
+ follower, _ := reqActivity.GetCollection()
+
+ reqActivity.Id = actor.Id + "/reported"
+ reported, _ := activitypub.GetActorCollectionReq(reqActivity.Id)
+
+ var following []string
+ var followers []string
+ var reports []db.Report
+
+ for _, e := range follow.Items {
+ following = append(following, e.Id)
+ }
+
+ for _, e := range follower.Items {
+ followers = append(followers, e.Id)
+ }
+
+ for _, e := range reported.Items {
+ var r db.Report
+ r.Count = int(e.Size)
+ r.ID = e.Id
+ r.Reason = e.Content
+ reports = append(reports, r)
+ }
+
+ localReports, _ := db.GetLocalReport(actor.Name)
+
+ for _, e := range localReports {
+ var r db.Report
+ r.Count = e.Count
+ r.ID = e.ID
+ r.Reason = e.Reason
+ reports = append(reports, r)
+ }
+
+ var data route.AdminPage
+ data.Following = following
+ data.Followers = followers
+ data.Reported = reports
+ data.Domain = config.Domain
+ data.IsLocal, _ = actor.IsLocal()
+
+ data.Title = "Manage /" + actor.Name + "/"
+ data.Boards = webfinger.Boards
+ data.Board.Name = actor.Name
+ data.Board.Actor = actor
+ data.Key = config.Key
+ data.Board.TP = config.TP
+
+ data.Board.Post.Actor = actor.Id
+
+ data.AutoSubscribe, _ = actor.GetAutoSubscribe()
+
+ data.Themes = &config.Themes
+
+ data.RecentPosts, _ = actor.GetRecentPosts()
+
+ if cookie := ctx.Cookies("theme"); cookie != "" {
+ data.ThemeCookie = cookie
+ }
+
+ return ctx.Render("manage", fiber.Map{
+ "page": data,
+ })
+}
diff --git a/route/routes/api.go b/route/routes/api.go
new file mode 100644
index 0000000..080d88d
--- /dev/null
+++ b/route/routes/api.go
@@ -0,0 +1,55 @@
+package routes
+
+import (
+ "io/ioutil"
+ "net/http"
+ "time"
+
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Media(c *fiber.Ctx) error {
+ if c.Query("hash") != "" {
+ return RouteImages(c, c.Query("hash"))
+ }
+
+ return c.SendStatus(404)
+}
+
+func RouteImages(ctx *fiber.Ctx, media string) error {
+ req, err := http.NewRequest("GET", config.MediaHashs[media], nil)
+ if err != nil {
+ return util.MakeError(err, "RouteImages")
+ }
+
+ client := http.Client{
+ Timeout: 5 * time.Second,
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return util.MakeError(err, "RouteImages")
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ fileBytes, err := ioutil.ReadFile("./static/notfound.png")
+ if err != nil {
+ return util.MakeError(err, "RouteImages")
+ }
+
+ _, err = ctx.Write(fileBytes)
+ return util.MakeError(err, "RouteImages")
+ }
+
+ body, _ := ioutil.ReadAll(resp.Body)
+ for name, values := range resp.Header {
+ for _, value := range values {
+ ctx.Append(name, value)
+ }
+ }
+
+ return ctx.Send(body)
+}
diff --git a/route/routes/boardmgmt.go b/route/routes/boardmgmt.go
new file mode 100644
index 0000000..15b2686
--- /dev/null
+++ b/route/routes/boardmgmt.go
@@ -0,0 +1,47 @@
+package routes
+
+import "github.com/gofiber/fiber/v2"
+
+func BoardBanMedia(ctx *fiber.Ctx) error {
+ return ctx.SendString("board ban media")
+}
+
+func BoardDelete(ctx *fiber.Ctx) error {
+ return ctx.SendString("board delete")
+}
+
+func BoardDeleteAttach(ctx *fiber.Ctx) error {
+ return ctx.SendString("board delete attach")
+}
+
+func BoardMarkSensitive(ctx *fiber.Ctx) error {
+ return ctx.SendString("board mark sensitive")
+}
+
+func BoardRemove(ctx *fiber.Ctx) error {
+ return ctx.SendString("board remove")
+}
+
+func BoardRemoveAttach(ctx *fiber.Ctx) error {
+ return ctx.SendString("board remove attach")
+}
+
+func BoardAddToIndex(ctx *fiber.Ctx) error {
+ return ctx.SendString("board add to index")
+}
+
+func BoardPopArchive(ctx *fiber.Ctx) error {
+ return ctx.SendString("board pop archive")
+}
+
+func BoardAutoSubscribe(ctx *fiber.Ctx) error {
+ return ctx.SendString("board auto subscribe")
+}
+
+func BoardBlacklist(ctx *fiber.Ctx) error {
+ return ctx.SendString("board blacklist")
+}
+
+func BoardReport(ctx *fiber.Ctx) error {
+ return ctx.SendString("board report")
+}
diff --git a/route/routes/main.go b/route/routes/main.go
new file mode 100644
index 0000000..99dad31
--- /dev/null
+++ b/route/routes/main.go
@@ -0,0 +1,100 @@
+package routes
+
+import (
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/route"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Index(ctx *fiber.Ctx) error {
+ actor, err := activitypub.GetActorFromDB(config.Domain)
+ if err != nil {
+ return util.MakeError(err, "Index")
+ }
+
+ // this is a activitpub json request return json instead of html page
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ actor.GetInfoResp(ctx)
+ return nil
+ }
+
+ var data route.PageData
+
+ reqActivity := activitypub.Activity{Id: "https://fchan.xyz/followers"}
+ col, err := reqActivity.GetCollection()
+ if err != nil {
+ return util.MakeError(err, "Index")
+ }
+
+ if len(col.Items) > 0 {
+ data.InstanceIndex = col.Items
+ }
+
+ data.NewsItems, err = db.GetNews(3)
+ if err != nil {
+ return util.MakeError(err, "Index")
+ }
+
+ data.Title = "Welcome to " + actor.PreferredUsername
+ data.PreferredUsername = actor.PreferredUsername
+ data.Boards = webfinger.Boards
+ data.Board.Name = ""
+ data.Key = config.Key
+ data.Board.Domain = config.Domain
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Actor = actor
+ data.Board.Post.Actor = actor.Id
+ data.Board.Restricted = actor.Restricted
+ //almost certainly there is a better algorithm for this but the old one was wrong
+ //and I suck at math. This works at least.
+ data.BoardRemainer = make([]int, 3-(len(data.Boards)%3))
+
+ if len(data.BoardRemainer) == 3 {
+ data.BoardRemainer = make([]int, 0)
+ }
+
+ data.Meta.Description = data.PreferredUsername + " a federated image board based on ActivityPub. The current version of the code running on the server is still a work-in-progress product, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."
+ data.Meta.Url = data.Board.Domain
+ data.Meta.Title = data.Title
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("index", fiber.Map{
+ "page": data,
+ }, "layouts/main")
+}
+
+func Inbox(ctx *fiber.Ctx) error {
+ // TODO main actor Inbox route
+ return ctx.SendString("main inbox")
+}
+
+func Outbox(ctx *fiber.Ctx) error {
+ actor, err := webfinger.GetActorFromPath(ctx.Path(), "/")
+
+ if err != nil {
+ return util.MakeError(err, "Outbox")
+ }
+
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ actor.GetOutbox(ctx)
+ return nil
+ }
+
+ return route.ParseOutboxRequest(ctx, actor)
+}
+
+func Following(ctx *fiber.Ctx) error {
+ actor, _ := activitypub.GetActorFromDB(config.Domain)
+ return actor.GetFollowingResp(ctx)
+}
+
+func Followers(ctx *fiber.Ctx) error {
+ actor, _ := activitypub.GetActorFromDB(config.Domain)
+ return actor.GetFollowersResp(ctx)
+}
diff --git a/route/routes/news.go b/route/routes/news.go
new file mode 100644
index 0000000..0d226a5
--- /dev/null
+++ b/route/routes/news.go
@@ -0,0 +1,73 @@
+package routes
+
+import (
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/route"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
+)
+
+func NewsGet(ctx *fiber.Ctx) error {
+ timestamp := 0
+
+ actor, err := activitypub.GetActorFromDB(config.Domain)
+ if err != nil {
+ return util.MakeError(err, "NewsGet")
+ }
+
+ var data route.PageData
+ data.PreferredUsername = actor.PreferredUsername
+ data.Boards = webfinger.Boards
+ data.Board.Name = ""
+ data.Key = config.Key
+ data.Board.Domain = config.Domain
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Actor = actor
+ data.Board.Post.Actor = actor.Id
+ data.Board.Restricted = actor.Restricted
+ data.NewsItems = make([]db.NewsItem, 1)
+
+ data.NewsItems[0], err = db.GetNewsItem(timestamp)
+ if err != nil {
+ return util.MakeError(err, "NewsGet")
+ }
+
+ data.Title = actor.PreferredUsername + ": " + data.NewsItems[0].Title
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("news", fiber.Map{"page": data}, "layouts/main")
+}
+
+func AllNewsGet(ctx *fiber.Ctx) error {
+ actor, err := activitypub.GetActorFromDB(config.Domain)
+ if err != nil {
+ return util.MakeError(err, "AllNewsGet")
+ }
+
+ var data route.PageData
+ data.PreferredUsername = actor.PreferredUsername
+ data.Title = actor.PreferredUsername + " News"
+ data.Boards = webfinger.Boards
+ data.Board.Name = ""
+ data.Key = config.Key
+ data.Board.Domain = config.Domain
+ data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
+ data.Board.Actor = actor
+ data.Board.Post.Actor = actor.Id
+ data.Board.Restricted = actor.Restricted
+
+ data.NewsItems, err = db.GetNews(0)
+ if err != nil {
+ return util.MakeError(err, "AllNewsGet")
+ }
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = route.GetThemeCookie(ctx)
+
+ return ctx.Render("anews", fiber.Map{"page": data}, "layouts/main")
+}
diff --git a/route/routes/webfinger.go b/route/routes/webfinger.go
new file mode 100644
index 0000000..3d5fa63
--- /dev/null
+++ b/route/routes/webfinger.go
@@ -0,0 +1,58 @@
+package routes
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Webfinger(c *fiber.Ctx) error {
+ acct := c.Query("resource")
+
+ if len(acct) < 1 {
+ c.Status(fiber.StatusBadRequest)
+ return c.Send([]byte("resource needs a value"))
+ }
+
+ acct = strings.Replace(acct, "acct:", "", -1)
+
+ actorDomain := strings.Split(acct, "@")
+
+ if len(actorDomain) < 2 {
+ c.Status(fiber.StatusBadRequest)
+ return c.Send([]byte("accepts only subject form of acct:board@instance"))
+ }
+
+ if actorDomain[0] == "main" {
+ actorDomain[0] = ""
+ } else {
+ actorDomain[0] = "/" + actorDomain[0]
+ }
+
+ actor := activitypub.Actor{Id: config.TP + "" + actorDomain[1] + "" + actorDomain[0]}
+ if res, err := actor.IsLocal(); err == nil && !res {
+ c.Status(fiber.StatusBadRequest)
+ return c.Send([]byte("actor not local"))
+ } else if err != nil {
+ return util.MakeError(err, "Webfinger")
+ }
+
+ var finger activitypub.Webfinger
+ var link activitypub.WebfingerLink
+
+ finger.Subject = "acct:" + actorDomain[0] + "@" + actorDomain[1]
+ link.Rel = "self"
+ link.Type = "application/activity+json"
+ link.Href = config.TP + "" + actorDomain[1] + "" + actorDomain[0]
+
+ finger.Links = append(finger.Links, link)
+
+ enc, _ := json.Marshal(finger)
+
+ c.Set("Content-Type", config.ActivityStreams)
+ return c.Send(enc)
+}
diff --git a/route/structs.go b/route/structs.go
new file mode 100644
index 0000000..7b1c2b8
--- /dev/null
+++ b/route/structs.go
@@ -0,0 +1,56 @@
+package route
+
+import (
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+)
+
+type PageData struct {
+ Title string
+ PreferredUsername string
+ Board webfinger.Board
+ Pages []int
+ CurrentPage int
+ TotalPage int
+ Boards []webfinger.Board
+ Posts []activitypub.ObjectBase
+ Key string
+ PostId string
+ Instance activitypub.Actor
+ InstanceIndex []activitypub.ObjectBase
+ ReturnTo string
+ NewsItems []db.NewsItem
+ BoardRemainer []int
+ Meta Meta
+
+ Themes *[]string
+ ThemeCookie string
+}
+
+type AdminPage struct {
+ Title string
+ Board webfinger.Board
+ Key string
+ Actor string
+ Boards []webfinger.Board
+ Following []string
+ Followers []string
+ Reported []db.Report
+ Domain string
+ IsLocal bool
+ PostBlacklist []util.PostBlacklist
+ AutoSubscribe bool
+ RecentPosts []activitypub.ObjectBase
+
+ Themes *[]string
+ ThemeCookie string
+}
+
+type Meta struct {
+ Title string
+ Description string
+ Url string
+ Preview string
+}
diff --git a/route/util.go b/route/util.go
new file mode 100644
index 0000000..5bd15d6
--- /dev/null
+++ b/route/util.go
@@ -0,0 +1,377 @@
+package route
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/post"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/template/html"
+)
+
+func GetThemeCookie(c *fiber.Ctx) string {
+ cookie := c.Cookies("theme")
+ if cookie != "" {
+ cookies := strings.SplitN(cookie, "=", 2)
+ return cookies[0]
+ }
+
+ return "default"
+}
+
+func WantToServeCatalog(actorName string) (activitypub.Collection, bool, error) {
+ var collection activitypub.Collection
+ serve := false
+
+ actor, err := activitypub.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return collection, false, util.MakeError(err, "WantToServeCatalog")
+ }
+
+ if actor.Id != "" {
+ collection, err = actor.GetCatalogCollection()
+ if err != nil {
+ return collection, false, util.MakeError(err, "WantToServeCatalog")
+ }
+
+ collection.Actor = actor
+ return collection, true, nil
+ }
+
+ return collection, serve, nil
+}
+
+func WantToServeArchive(actorName string) (activitypub.Collection, bool, error) {
+ var collection activitypub.Collection
+ serve := false
+
+ actor, err := activitypub.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return collection, false, util.MakeError(err, "WantToServeArchive")
+ }
+
+ if actor.Id != "" {
+ collection, err = actor.GetCollectionType("Archive")
+ if err != nil {
+ return collection, false, util.MakeError(err, "WantToServeArchive")
+ }
+
+ collection.Actor = actor
+ return collection, true, nil
+ }
+
+ return collection, serve, nil
+}
+
+func GetActorPost(ctx *fiber.Ctx, path string) error {
+ obj := activitypub.ObjectBase{Id: config.Domain + "" + path}
+ collection, err := obj.GetCollectionFromPath()
+
+ if err != nil {
+ return util.MakeError(err, "GetActorPost")
+ }
+
+ if len(collection.OrderedItems) > 0 {
+ enc, err := json.MarshalIndent(collection, "", "\t")
+ if err != nil {
+ return util.MakeError(err, "GetActorPost")
+ }
+
+ ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
+ _, err = ctx.Write(enc)
+ return util.MakeError(err, "GetActorPost")
+ }
+
+ return nil
+}
+
+func ParseOutboxRequest(ctx *fiber.Ctx, actor activitypub.Actor) error {
+ contentType := util.GetContentType(ctx.Get("content-type"))
+
+ if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" {
+ hasCaptcha, err := util.BoardHasAuthType(actor.Name, "captcha")
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ valid, err := post.CheckCaptcha(ctx.FormValue("captcha"))
+ if err == nil && hasCaptcha && valid {
+ header, _ := ctx.FormFile("file")
+ if header != nil {
+ f, _ := header.Open()
+ defer f.Close()
+ if header.Size > (7 << 20) {
+ ctx.Response().Header.SetStatusCode(403)
+ _, err := ctx.Write([]byte("7MB max file size"))
+ return util.MakeError(err, "ParseOutboxRequest")
+ } else if isBanned, err := post.IsMediaBanned(f); err == nil && isBanned {
+ //Todo add logging
+ config.Log.Println("media banned")
+ ctx.Response().Header.SetStatusCode(403)
+ _, err := ctx.Write([]byte("media banned"))
+ return util.MakeError(err, "ParseOutboxRequest")
+ } else if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ contentType, _ := util.GetFileContentType(f)
+
+ if !post.SupportedMIMEType(contentType) {
+ ctx.Response().Header.SetStatusCode(403)
+ _, err := ctx.Write([]byte("file type not supported"))
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+ }
+
+ var nObj = activitypub.CreateObject("Note")
+ nObj, err := post.ObjectFromForm(ctx, nObj)
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ nObj.Actor = config.Domain + "/" + actor.Name
+
+ nObj, err = nObj.Write()
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ if len(nObj.To) == 0 {
+ if err := actor.ArchivePosts(); err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+ }
+
+ activity, err := nObj.CreateActivity("Create")
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ activity, err = activity.AddFollowersTo()
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ go activity.MakeRequestInbox()
+
+ var id string
+ op := len(nObj.InReplyTo) - 1
+ if op >= 0 {
+ if nObj.InReplyTo[op].Id == "" {
+ id = nObj.Id
+ } else {
+ id = nObj.InReplyTo[0].Id + "|" + nObj.Id
+ }
+ }
+
+ ctx.Response().Header.Set("Status", "200")
+ _, err = ctx.Write([]byte(id))
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte("captcha could not auth"))
+ return util.MakeError(err, "")
+ } else { // json request
+ activity, err := activitypub.GetActivityFromJson(ctx)
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ if res, err := activity.IsLocal(); err == nil && res {
+ if res := activity.Actor.VerifyHeaderSignature(ctx); err == nil && !res {
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte(""))
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ switch activity.Type {
+ case "Create":
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte(""))
+ break
+
+ case "Follow":
+ validActor := (activity.Object.Actor != "")
+ validLocalActor := (activity.Actor.Id == actor.Id)
+
+ var rActivity activitypub.Activity
+
+ if validActor && validLocalActor {
+ rActivity = activity.AcceptFollow()
+ rActivity, err = rActivity.SetActorFollowing()
+
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ if err := activity.MakeRequestInbox(); err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+ }
+
+ actor, _ := activitypub.GetActorFromDB(config.Domain)
+ webfinger.FollowingBoards, err = actor.GetFollowing()
+
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ webfinger.Boards, err = webfinger.GetBoardCollection()
+
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+ break
+
+ case "Delete":
+ config.Log.Println("This is a delete")
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte("could not process activity"))
+ break
+
+ case "Note":
+ ctx.Response().Header.Set("Satus", "403")
+ _, err = ctx.Write([]byte("could not process activity"))
+ break
+
+ case "New":
+ name := activity.Object.Alias
+ prefname := activity.Object.Name
+ summary := activity.Object.Summary
+ restricted := activity.Object.Sensitive
+
+ actor, err := db.CreateNewBoard(*activitypub.CreateNewActor(name, prefname, summary, config.AuthReq, restricted))
+ if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ if actor.Id != "" {
+ var board []activitypub.ObjectBase
+ var item activitypub.ObjectBase
+ var removed bool = false
+
+ item.Id = actor.Id
+ for _, e := range webfinger.FollowingBoards {
+ if e.Id != item.Id {
+ board = append(board, e)
+ } else {
+ removed = true
+ }
+ }
+
+ if !removed {
+ board = append(board, item)
+ }
+
+ webfinger.FollowingBoards = board
+ webfinger.Boards, err = webfinger.GetBoardCollection()
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte(""))
+ break
+
+ default:
+ ctx.Response().Header.Set("status", "403")
+ _, err = ctx.Write([]byte("could not process activity"))
+ }
+ } else if err != nil {
+ return util.MakeError(err, "ParseOutboxRequest")
+ } else {
+ config.Log.Println("is NOT activity")
+ ctx.Response().Header.Set("Status", "403")
+ _, err = ctx.Write([]byte("could not process activity"))
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+ }
+
+ return nil
+}
+
+func TemplateFunctions(engine *html.Engine) {
+ engine.AddFunc("mod", func(i, j int) bool {
+ return i%j == 0
+ })
+
+ engine.AddFunc("sub", func(i, j int) int {
+ return i - j
+ })
+
+ engine.AddFunc("add", func(i, j int) int {
+ return i + j
+ })
+
+ engine.AddFunc("unixtoreadable", func(u int) string {
+ return time.Unix(int64(u), 0).Format("Jan 02, 2006")
+ })
+
+ engine.AddFunc("timeToReadableLong", func(t time.Time) string {
+ return t.Format("01/02/06(Mon)15:04:05")
+ })
+
+ engine.AddFunc("timeToUnix", func(t time.Time) string {
+ return fmt.Sprint(t.Unix())
+ })
+
+ engine.AddFunc("proxy", util.MediaProxy)
+
+ // previously short
+ engine.AddFunc("shortURL", util.ShortURL)
+
+ engine.AddFunc("parseAttachment", post.ParseAttachment)
+
+ engine.AddFunc("parseContent", post.ParseContent)
+
+ engine.AddFunc("shortImg", util.ShortImg)
+
+ engine.AddFunc("convertSize", util.ConvertSize)
+
+ engine.AddFunc("isOnion", util.IsOnion)
+
+ engine.AddFunc("parseReplyLink", func(actorId string, op string, id string, content string) template.HTML {
+ actor, _ := activitypub.FingerActor(actorId)
+ title := strings.ReplaceAll(post.ParseLinkTitle(actor.Id+"/", op, content), `/\<`, ">")
+ link := ">>" + util.ShortURL(actor.Outbox, id) + ""
+ return template.HTML(link)
+ })
+
+ engine.AddFunc("shortExcerpt", func(post activitypub.ObjectBase) string {
+ var returnString string
+
+ if post.Name != "" {
+ returnString = post.Name + "| " + post.Content
+ } else {
+ returnString = post.Content
+ }
+
+ re := regexp.MustCompile(`(^(.|\r\n|\n){100})`)
+
+ match := re.FindStringSubmatch(returnString)
+
+ if len(match) > 0 {
+ returnString = match[0] + "..."
+ }
+
+ re = regexp.MustCompile(`(^.+\|)`)
+
+ match = re.FindStringSubmatch(returnString)
+
+ if len(match) > 0 {
+ returnString = strings.Replace(returnString, match[0], ""+match[0]+"", 1)
+ returnString = strings.Replace(returnString, "|", ":", 1)
+ }
+
+ return returnString
+ })
+}
diff --git a/routes/404.go b/routes/404.go
deleted file mode 100644
index 94b96e8..0000000
--- a/routes/404.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package routes
-
-import (
- "github.com/gofiber/fiber/v2"
-)
-
-func NotFound(c *fiber.Ctx) error {
- return c.Status(404).Render("404", fiber.Map{}, "layouts/main")
-}
diff --git a/routes/actor.go b/routes/actor.go
deleted file mode 100644
index 4f848f1..0000000
--- a/routes/actor.go
+++ /dev/null
@@ -1,377 +0,0 @@
-package routes
-
-import (
- "bytes"
- "errors"
- "io"
- "io/ioutil"
- "mime/multipart"
- "net/http"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func ActorInbox(ctx *fiber.Ctx) error {
- activity, err := activitypub.GetActivityFromJson(ctx)
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if activity.Actor.PublicKey.Id == "" {
- nActor, err := activitypub.FingerActor(activity.Actor.Id)
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- activity.Actor = &nActor
- }
-
- if !activity.Actor.VerifyHeaderSignature(ctx) {
- response := activity.Reject()
- return response.MakeRequestInbox()
- }
-
- switch activity.Type {
- case "Create":
- for _, e := range activity.To {
- actor := activitypub.Actor{Id: e}
- if res, err := actor.IsLocal(); err == nil && res {
- if res, err := activity.Actor.IsLocal(); err == nil && res {
- reqActivity := activitypub.Activity{Id: activity.Object.Id}
- col, err := reqActivity.GetCollection()
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if len(col.OrderedItems) < 1 {
- break
- }
-
- if err := activity.Object.WriteCache(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- actor, err := activitypub.GetActorFromDB(e)
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if err := actor.ArchivePosts(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- //SendToFollowers(e, activity)
- } else if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- } else if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- }
-
- break
-
- case "Delete":
- for _, e := range activity.To {
- actor, err := activitypub.GetActorFromDB(e)
- if err != nil {
- return util.MakeError(err, "")
- }
-
- if actor.Id != "" && actor.Id != config.Domain {
- if activity.Object.Replies.OrderedItems != nil {
- for _, k := range activity.Object.Replies.OrderedItems {
- if err := k.Tombstone(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- }
- }
-
- if err := activity.Object.Tombstone(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- if err := actor.UnArchiveLast(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- break
- }
- }
- break
-
- case "Follow":
- for _, e := range activity.To {
- if _, err := activitypub.GetActorFromDB(e); err == nil {
- response := activity.AcceptFollow()
- response, err := response.SetActorFollower()
-
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if err := response.MakeRequestInbox(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- alreadyFollowing, err := response.Actor.IsAlreadyFollowing(response.Object.Id)
-
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- objActor, err := activitypub.FingerActor(response.Object.Actor)
-
- if err != nil || objActor.Id == "" {
- return util.MakeError(err, "ActorInbox")
- }
-
- reqActivity := activitypub.Activity{Id: objActor.Following}
- remoteActorFollowingCol, err := reqActivity.GetCollection()
-
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- alreadyFollow := false
-
- for _, e := range remoteActorFollowingCol.Items {
- if e.Id == response.Actor.Id {
- alreadyFollowing = true
- }
- }
-
- autoSub, err := response.Actor.GetAutoSubscribe()
-
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if autoSub && !alreadyFollow && alreadyFollowing {
- followActivity, err := response.Actor.MakeFollowActivity(response.Object.Actor)
-
- if err != nil {
- return util.MakeError(err, "ActorInbox")
- }
-
- if err := followActivity.MakeRequestOutbox(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- }
- } else if err != nil {
- return util.MakeError(err, "ActorInbox")
- } else {
- config.Log.Println("follow request for rejected")
- response := activity.Reject()
- return response.MakeRequestInbox()
- }
- }
- break
-
- case "Reject":
- if activity.Object.Object.Type == "Follow" {
- config.Log.Println("follow rejected")
- if _, err := activity.SetActorFollowing(); err != nil {
- return util.MakeError(err, "ActorInbox")
- }
- }
- break
- }
-
- return nil
-}
-
-func ActorOutbox(ctx *fiber.Ctx) error {
- //var activity activitypub.Activity
- actor, err := webfinger.GetActorFromPath(ctx.Path(), "/")
- if err != nil {
- return util.MakeError(err, "ActorOutbox")
- }
-
- if activitypub.AcceptActivity(ctx.Get("Accept")) {
- actor.GetOutbox(ctx)
- return nil
- }
-
- return ParseOutboxRequest(ctx, actor)
-}
-
-func ActorFollowing(ctx *fiber.Ctx) error {
- actor, _ := activitypub.GetActorFromDB(config.Domain + "/" + ctx.Params("actor"))
- return actor.GetFollowingResp(ctx)
-}
-
-func ActorFollowers(ctx *fiber.Ctx) error {
- actor, _ := activitypub.GetActorFromDB(config.Domain + "/" + ctx.Params("actor"))
- return actor.GetFollowersResp(ctx)
-}
-
-func ActorReported(c *fiber.Ctx) error {
- // STUB
-
- return c.SendString("actor reported")
-}
-
-func ActorArchive(c *fiber.Ctx) error {
- // STUB
-
- return c.SendString("actor archive")
-}
-
-func ActorPost(ctx *fiber.Ctx) error {
- header, _ := ctx.FormFile("file")
-
- if ctx.FormValue("inReplyTo") == "" && header == nil {
- return ctx.Render("403", fiber.Map{
- "message": "Media is required for new posts",
- })
- }
-
- var file multipart.File
-
- if header != nil {
- file, _ = header.Open()
- }
-
- if file != nil && header.Size > (7<<20) {
- return ctx.Render("403", fiber.Map{
- "message": "7MB max file size",
- })
- }
-
- if is, _ := util.IsPostBlacklist(ctx.FormValue("comment")); is {
- errors.New("\n\nBlacklist post blocked\n\n")
- return ctx.Redirect("/", 301)
- }
-
- if ctx.FormValue("inReplyTo") == "" || file == nil {
- if ctx.FormValue("comment") == "" && ctx.FormValue("subject") == "" {
- return ctx.Render("403", fiber.Map{
- "message": "Comment or Subject required",
- })
- }
- }
-
- if len(ctx.FormValue("comment")) > 2000 {
- return ctx.Render("403", fiber.Map{
- "message": "Comment limit 2000 characters",
- })
- }
-
- if len(ctx.FormValue("subject")) > 100 || len(ctx.FormValue("name")) > 100 || len(ctx.FormValue("options")) > 100 {
- return ctx.Render("403", fiber.Map{
- "message": "Name, Subject or Options limit 100 characters",
- })
- }
-
- if ctx.FormValue("captcha") == "" {
- return ctx.Render("403", fiber.Map{
- "message": "Incorrect Captcha",
- })
- }
-
- b := bytes.Buffer{}
- we := multipart.NewWriter(&b)
-
- if file != nil {
- var fw io.Writer
-
- fw, err := we.CreateFormFile("file", header.Filename)
-
- if err != nil {
- errors.New("error with form file create")
- }
- _, err = io.Copy(fw, file)
-
- if err != nil {
- errors.New("error with form file copy")
- }
- }
-
- reply, _ := post.ParseCommentForReply(ctx.FormValue("comment"))
-
- form, _ := ctx.MultipartForm()
-
- for key, r0 := range form.Value {
- if key == "captcha" {
- err := we.WriteField(key, ctx.FormValue("captchaCode")+":"+ctx.FormValue("captcha"))
- if err != nil {
- errors.New("error with writing captcha field")
- }
- } else if key == "name" {
- name, tripcode, _ := post.CreateNameTripCode(ctx)
-
- err := we.WriteField(key, name)
- if err != nil {
- errors.New("error with writing name field")
- }
-
- err = we.WriteField("tripcode", tripcode)
- if err != nil {
- errors.New("error with writing tripcode field")
- }
- } else {
- err := we.WriteField(key, r0[0])
- if err != nil {
- errors.New("error with writing field")
- }
- }
- }
-
- if ctx.FormValue("inReplyTo") == "" && reply != "" {
- err := we.WriteField("inReplyTo", reply)
- if err != nil {
- errors.New("error with writing inReplyTo field")
- }
- }
-
- we.Close()
-
- sendTo := ctx.FormValue("sendTo")
-
- req, err := http.NewRequest("POST", sendTo, &b)
-
- if err != nil {
- errors.New("error with post form req")
- }
-
- req.Header.Set("Content-Type", we.FormDataContentType())
-
- resp, err := util.RouteProxy(req)
-
- if err != nil {
- errors.New("error with post form resp")
- }
-
- defer resp.Body.Close()
- body, _ := ioutil.ReadAll(resp.Body)
-
- if resp.StatusCode == 200 {
-
- var obj activitypub.ObjectBase
-
- obj = post.ParseOptions(ctx, obj)
- for _, e := range obj.Option {
- if e == "noko" || e == "nokosage" {
- return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/"+util.ShortURL(ctx.FormValue("sendTo"), string(body)), 301)
- }
- }
-
- if ctx.FormValue("returnTo") == "catalog" {
- return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/catalog", 301)
- } else {
- return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301)
- }
- }
-
- if resp.StatusCode == 403 {
- return ctx.Render("403", fiber.Map{
- "message": string(body),
- })
- }
-
- return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301)
-}
diff --git a/routes/admin.go b/routes/admin.go
deleted file mode 100644
index d837a2f..0000000
--- a/routes/admin.go
+++ /dev/null
@@ -1,290 +0,0 @@
-package routes
-
-import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "net/http"
- "time"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func AdminVerify(ctx *fiber.Ctx) error {
- identifier := ctx.FormValue("id")
- code := ctx.FormValue("code")
-
- var verify util.Verify
- verify.Identifier = identifier
- verify.Code = code
-
- j, _ := json.Marshal(&verify)
-
- req, err := http.NewRequest("POST", config.Domain+"/auth", bytes.NewBuffer(j))
-
- if err != nil {
- return util.MakeError(err, "AdminVerify")
- }
-
- req.Header.Set("Content-Type", config.ActivityStreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- if err != nil {
- return util.MakeError(err, "AdminVerify")
- }
-
- defer resp.Body.Close()
-
- rBody, _ := ioutil.ReadAll(resp.Body)
-
- body := string(rBody)
-
- if resp.StatusCode != 200 {
- return ctx.Redirect("/"+config.Key, http.StatusPermanentRedirect)
- }
-
- ctx.Cookie(&fiber.Cookie{
- Name: "session_token",
- Value: body + "|" + verify.Code,
- Expires: time.Now().UTC().Add(60 * 60 * 48 * time.Second),
- })
-
- return ctx.Redirect("/", http.StatusSeeOther)
-}
-
-// TODO remove this route it is mostly unneeded
-func AdminAuth(ctx *fiber.Ctx) error {
- var verify util.Verify
-
- err := json.Unmarshal(ctx.Body(), &verify)
-
- if err != nil {
- return util.MakeError(err, "AdminAuth")
- }
-
- v, _ := util.GetVerificationByCode(verify.Code)
-
- if v.Identifier == verify.Identifier {
- _, err := ctx.Write([]byte(v.Board))
- return util.MakeError(err, "AdminAuth")
- }
-
- ctx.Response().Header.SetStatusCode(http.StatusBadRequest)
- _, err = ctx.Write([]byte(""))
-
- return util.MakeError(err, "AdminAuth")
-}
-
-func AdminIndex(ctx *fiber.Ctx) error {
- id, _ := util.GetPasswordFromSession(ctx)
- actor, _ := webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
-
- if actor.Id == "" {
- actor, _ = activitypub.GetActorByNameFromDB(config.Domain)
- }
-
- if id == "" || (id != actor.Id && id != config.Domain) {
- return ctx.Render("verify", fiber.Map{})
- }
-
- actor, err := activitypub.GetActor(config.Domain)
-
- if err != nil {
- return util.MakeError(err, "AdminIndex")
- }
-
- reqActivity := activitypub.Activity{Id: actor.Following}
- follow, _ := reqActivity.GetCollection()
- follower, _ := reqActivity.GetCollection()
-
- var following []string
- var followers []string
-
- for _, e := range follow.Items {
- following = append(following, e.Id)
- }
-
- for _, e := range follower.Items {
- followers = append(followers, e.Id)
- }
-
- var adminData AdminPage
- adminData.Following = following
- adminData.Followers = followers
- adminData.Actor = actor.Id
- adminData.Key = config.Key
- adminData.Domain = config.Domain
- adminData.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- adminData.Title = actor.Name + " Admin page"
-
- adminData.Boards = webfinger.Boards
-
- adminData.Board.Post.Actor = actor.Id
-
- adminData.PostBlacklist, _ = util.GetRegexBlacklist()
-
- adminData.Themes = &config.Themes
-
- return ctx.Render("admin", fiber.Map{
- "page": adminData,
- })
-}
-
-func AdminFollow(ctx *fiber.Ctx) error {
- follow := ctx.FormValue("follow")
- actorId := ctx.FormValue("actor")
-
- actor := activitypub.Actor{Id: actorId}
- followActivity, _ := actor.MakeFollowActivity(follow)
-
- objActor := activitypub.Actor{Id: followActivity.Object.Actor}
-
- if isLocal, _ := objActor.IsLocal(); !isLocal && followActivity.Actor.Id == config.Domain {
- _, err := ctx.Write([]byte("main board can only follow local boards. Create a new board and then follow outside boards from it."))
- return util.MakeError(err, "AdminIndex")
- }
-
- if actor, _ := activitypub.FingerActor(follow); actor.Id != "" {
- if err := followActivity.MakeRequestOutbox(); err != nil {
- return util.MakeError(err, "AdminFollow")
- }
- }
-
- var redirect string
- actor, _ = webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
-
- if actor.Name != "main" {
- redirect = actor.Name
- }
-
- return ctx.Redirect("/"+config.Key+"/"+redirect, http.StatusSeeOther)
-}
-
-func AdminAddBoard(ctx *fiber.Ctx) error {
- actor, _ := activitypub.GetActorFromDB(config.Domain)
-
- if hasValidation := actor.HasValidation(ctx); !hasValidation {
- return nil
- }
-
- var newActorActivity activitypub.Activity
- var board activitypub.Actor
-
- var restrict bool
- if ctx.FormValue("restricted") == "True" {
- restrict = true
- } else {
- restrict = false
- }
-
- board.Name = ctx.FormValue("name")
- board.PreferredUsername = ctx.FormValue("prefname")
- board.Summary = ctx.FormValue("summary")
- board.Restricted = restrict
-
- newActorActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- newActorActivity.Type = "New"
-
- var nobj activitypub.ObjectBase
- newActorActivity.Actor = &actor
- newActorActivity.Object = &nobj
-
- newActorActivity.Object.Alias = board.Name
- newActorActivity.Object.Name = board.PreferredUsername
- newActorActivity.Object.Summary = board.Summary
- newActorActivity.Object.Sensitive = board.Restricted
-
- newActorActivity.MakeRequestOutbox()
- return ctx.Redirect("/"+config.Key, http.StatusSeeOther)
-}
-
-func AdminPostNews(c *fiber.Ctx) error {
- // STUB
-
- return c.SendString("admin post news")
-}
-
-func AdminNewsDelete(c *fiber.Ctx) error {
- // STUB
-
- return c.SendString("admin news delete")
-}
-
-func AdminActorIndex(ctx *fiber.Ctx) error {
- actor, _ := webfinger.GetActorFromPath(ctx.Path(), "/"+config.Key+"/")
-
- reqActivity := activitypub.Activity{Id: actor.Following}
- follow, _ := reqActivity.GetCollection()
-
- reqActivity.Id = actor.Followers
- follower, _ := reqActivity.GetCollection()
-
- reqActivity.Id = actor.Id + "/reported"
- reported, _ := activitypub.GetActorCollectionReq(reqActivity.Id)
-
- var following []string
- var followers []string
- var reports []db.Report
-
- for _, e := range follow.Items {
- following = append(following, e.Id)
- }
-
- for _, e := range follower.Items {
- followers = append(followers, e.Id)
- }
-
- for _, e := range reported.Items {
- var r db.Report
- r.Count = int(e.Size)
- r.ID = e.Id
- r.Reason = e.Content
- reports = append(reports, r)
- }
-
- localReports, _ := db.GetLocalReport(actor.Name)
-
- for _, e := range localReports {
- var r db.Report
- r.Count = e.Count
- r.ID = e.ID
- r.Reason = e.Reason
- reports = append(reports, r)
- }
-
- var data AdminPage
- data.Following = following
- data.Followers = followers
- data.Reported = reports
- data.Domain = config.Domain
- data.IsLocal, _ = actor.IsLocal()
-
- data.Title = "Manage /" + actor.Name + "/"
- data.Boards = webfinger.Boards
- data.Board.Name = actor.Name
- data.Board.Actor = actor
- data.Key = config.Key
- data.Board.TP = config.TP
-
- data.Board.Post.Actor = actor.Id
-
- data.AutoSubscribe, _ = actor.GetAutoSubscribe()
-
- data.Themes = &config.Themes
-
- data.RecentPosts, _ = actor.GetRecentPosts()
-
- if cookie := ctx.Cookies("theme"); cookie != "" {
- data.ThemeCookie = cookie
- }
-
- return ctx.Render("manage", fiber.Map{
- "page": data,
- })
-}
diff --git a/routes/api.go b/routes/api.go
deleted file mode 100644
index 080d88d..0000000
--- a/routes/api.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package routes
-
-import (
- "io/ioutil"
- "net/http"
- "time"
-
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/gofiber/fiber/v2"
-)
-
-func Media(c *fiber.Ctx) error {
- if c.Query("hash") != "" {
- return RouteImages(c, c.Query("hash"))
- }
-
- return c.SendStatus(404)
-}
-
-func RouteImages(ctx *fiber.Ctx, media string) error {
- req, err := http.NewRequest("GET", config.MediaHashs[media], nil)
- if err != nil {
- return util.MakeError(err, "RouteImages")
- }
-
- client := http.Client{
- Timeout: 5 * time.Second,
- }
-
- resp, err := client.Do(req)
- if err != nil {
- return util.MakeError(err, "RouteImages")
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- fileBytes, err := ioutil.ReadFile("./static/notfound.png")
- if err != nil {
- return util.MakeError(err, "RouteImages")
- }
-
- _, err = ctx.Write(fileBytes)
- return util.MakeError(err, "RouteImages")
- }
-
- body, _ := ioutil.ReadAll(resp.Body)
- for name, values := range resp.Header {
- for _, value := range values {
- ctx.Append(name, value)
- }
- }
-
- return ctx.Send(body)
-}
diff --git a/routes/archive.go b/routes/archive.go
deleted file mode 100644
index c4950b4..0000000
--- a/routes/archive.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package routes
-
-import (
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func ArchiveGet(ctx *fiber.Ctx) error {
- collection := ctx.Locals("collection").(activitypub.Collection)
- actor := collection.Actor
-
- var returnData PageData
- returnData.Board.Name = actor.Name
- returnData.Board.PrefName = actor.PreferredUsername
- returnData.Board.InReplyTo = ""
- returnData.Board.To = actor.Outbox
- returnData.Board.Actor = actor
- returnData.Board.Summary = actor.Summary
- returnData.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- returnData.Board.Domain = config.Domain
- returnData.Board.Restricted = actor.Restricted
- returnData.Key = config.Key
- returnData.ReturnTo = "archive"
-
- returnData.Board.Post.Actor = actor.Id
-
- var err error
- returnData.Instance, err = activitypub.GetActorFromDB(config.Domain)
-
- capt, err := util.GetRandomCaptcha()
- if err != nil {
- return util.MakeError(err, "ArchiveGet")
- }
- returnData.Board.Captcha = config.Domain + "/" + capt
- returnData.Board.CaptchaCode = post.GetCaptchaCode(returnData.Board.Captcha)
-
- returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
-
- returnData.Boards = webfinger.Boards
-
- returnData.Posts = collection.OrderedItems
-
- returnData.Themes = &config.Themes
- returnData.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("archive", fiber.Map{
- "page": returnData,
- }, "layouts/main")
-}
diff --git a/routes/boardmgmt.go b/routes/boardmgmt.go
deleted file mode 100644
index 12e133d..0000000
--- a/routes/boardmgmt.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package routes
-
-import "github.com/gofiber/fiber/v2"
-
-func BoardBanMedia(c *fiber.Ctx) error {
- return c.SendString("board ban media")
-}
-
-func BoardDelete(c *fiber.Ctx) error {
- return c.SendString("board delete")
-}
-
-func BoardDeleteAttach(c *fiber.Ctx) error {
- return c.SendString("board delete attach")
-}
-
-func BoardMarkSensitive(c *fiber.Ctx) error {
- return c.SendString("board mark sensitive")
-}
-
-func BoardRemove(c *fiber.Ctx) error {
- return c.SendString("board remove")
-}
-
-func BoardRemoveAttach(c *fiber.Ctx) error {
- return c.SendString("board remove attach")
-}
-
-func BoardAddToIndex(c *fiber.Ctx) error {
- return c.SendString("board add to index")
-}
-
-func BoardPopArchive(c *fiber.Ctx) error {
- return c.SendString("board pop archive")
-}
-
-func BoardAutoSubscribe(c *fiber.Ctx) error {
- return c.SendString("board auto subscribe")
-}
-
-func BoardBlacklist(c *fiber.Ctx) error {
- return c.SendString("board blacklist")
-}
-
-func BoardReport(c *fiber.Ctx) error {
- return c.SendString("board report")
-}
diff --git a/routes/follow.go b/routes/follow.go
deleted file mode 100644
index f61e405..0000000
--- a/routes/follow.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package routes
-
-import (
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/gofiber/fiber/v2"
-)
-
-func Following(ctx *fiber.Ctx) error {
- actor, _ := activitypub.GetActorFromDB(config.Domain)
- return actor.GetFollowingResp(ctx)
-}
-
-func Followers(ctx *fiber.Ctx) error {
- actor, _ := activitypub.GetActorFromDB(config.Domain)
- return actor.GetFollowersResp(ctx)
-}
diff --git a/routes/inbox.go b/routes/inbox.go
deleted file mode 100644
index 2f88329..0000000
--- a/routes/inbox.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package routes
-
-import "github.com/gofiber/fiber/v2"
-
-func Inbox(c *fiber.Ctx) error {
- // STUB
-
- return c.SendString("main inbox")
-}
diff --git a/routes/index.go b/routes/index.go
deleted file mode 100644
index 68fcfd0..0000000
--- a/routes/index.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package routes
-
-import (
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func Index(ctx *fiber.Ctx) error {
- actor, err := activitypub.GetActorFromDB(config.Domain)
- if err != nil {
- return util.MakeError(err, "Index")
- }
-
- // this is a activitpub json request return json instead of html page
- if activitypub.AcceptActivity(ctx.Get("Accept")) {
- actor.GetInfoResp(ctx)
- return nil
- }
-
- var data PageData
-
- reqActivity := activitypub.Activity{Id: "https://fchan.xyz/followers"}
- col, err := reqActivity.GetCollection()
- if err != nil {
- return util.MakeError(err, "Index")
- }
-
- if len(col.Items) > 0 {
- data.InstanceIndex = col.Items
- }
-
- data.NewsItems, err = db.GetNews(3)
- if err != nil {
- return util.MakeError(err, "Index")
- }
-
- data.Title = "Welcome to " + actor.PreferredUsername
- data.PreferredUsername = actor.PreferredUsername
- data.Boards = webfinger.Boards
- data.Board.Name = ""
- data.Key = config.Key
- data.Board.Domain = config.Domain
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Actor = actor
- data.Board.Post.Actor = actor.Id
- data.Board.Restricted = actor.Restricted
- //almost certainly there is a better algorithm for this but the old one was wrong
- //and I suck at math. This works at least.
- data.BoardRemainer = make([]int, 3-(len(data.Boards)%3))
-
- if len(data.BoardRemainer) == 3 {
- data.BoardRemainer = make([]int, 0)
- }
-
- data.Meta.Description = data.PreferredUsername + " a federated image board based on ActivityPub. The current version of the code running on the server is still a work-in-progress product, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."
- data.Meta.Url = data.Board.Domain
- data.Meta.Title = data.Title
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("index", fiber.Map{
- "page": data,
- }, "layouts/main")
-}
diff --git a/routes/media.go b/routes/media.go
deleted file mode 100644
index a4543c1..0000000
--- a/routes/media.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package routes
-
-import "github.com/gofiber/fiber/v2"
-
-func ApiMedia(c *fiber.Ctx) error {
- return c.SendString("api media")
-}
diff --git a/routes/news.go b/routes/news.go
deleted file mode 100644
index c091605..0000000
--- a/routes/news.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package routes
-
-import (
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func NewsGet(ctx *fiber.Ctx) error {
- timestamp := 0
-
- actor, err := activitypub.GetActorFromDB(config.Domain)
- if err != nil {
- return util.MakeError(err, "NewsGet")
- }
-
- var data PageData
- data.PreferredUsername = actor.PreferredUsername
- data.Boards = webfinger.Boards
- data.Board.Name = ""
- data.Key = config.Key
- data.Board.Domain = config.Domain
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Actor = actor
- data.Board.Post.Actor = actor.Id
- data.Board.Restricted = actor.Restricted
- data.NewsItems = make([]db.NewsItem, 1)
-
- data.NewsItems[0], err = db.GetNewsItem(timestamp)
- if err != nil {
- return util.MakeError(err, "NewsGet")
- }
-
- data.Title = actor.PreferredUsername + ": " + data.NewsItems[0].Title
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("news", fiber.Map{"page": data}, "layouts/main")
-}
-
-func AllNewsGet(ctx *fiber.Ctx) error {
- actor, err := activitypub.GetActorFromDB(config.Domain)
- if err != nil {
- return util.MakeError(err, "AllNewsGet")
- }
-
- var data PageData
- data.PreferredUsername = actor.PreferredUsername
- data.Title = actor.PreferredUsername + " News"
- data.Boards = webfinger.Boards
- data.Board.Name = ""
- data.Key = config.Key
- data.Board.Domain = config.Domain
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Actor = actor
- data.Board.Post.Actor = actor.Id
- data.Board.Restricted = actor.Restricted
-
- data.NewsItems, err = db.GetNews(0)
- if err != nil {
- return util.MakeError(err, "AllNewsGet")
- }
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("anews", fiber.Map{"page": data}, "layouts/main")
-}
diff --git a/routes/outbox.go b/routes/outbox.go
deleted file mode 100644
index 1d0d52e..0000000
--- a/routes/outbox.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package routes
-
-import (
- "strconv"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func Outbox(ctx *fiber.Ctx) error {
- actor, err := webfinger.GetActorFromPath(ctx.Path(), "/")
- if err != nil {
- return util.MakeError(err, "Outbox")
- }
-
- if activitypub.AcceptActivity(ctx.Get("Accept")) {
- actor.GetOutbox(ctx)
- return nil
- }
-
- return ParseOutboxRequest(ctx, actor)
-}
-
-func OutboxGet(ctx *fiber.Ctx) error {
- actor, err := activitypub.GetActorByNameFromDB(ctx.Params("actor"))
-
- if err != nil {
- return nil
- }
-
- if activitypub.AcceptActivity(ctx.Get("Accept")) {
- actor.GetInfoResp(ctx)
- return nil
- }
-
- var page int
- if postNum := ctx.Query("page"); postNum != "" {
- if page, err = strconv.Atoi(postNum); err != nil {
- return util.MakeError(err, "OutboxGet")
- }
- }
-
- collection, err := actor.WantToServePage(page)
- if err != nil {
- return util.MakeError(err, "OutboxGet")
- }
-
- var offset = 15
- var pages []int
- pageLimit := (float64(collection.TotalItems) / float64(offset))
-
- if pageLimit > 11 {
- pageLimit = 11
- }
-
- for i := 0.0; i < pageLimit; i++ {
- pages = append(pages, int(i))
- }
-
- var data PageData
- data.Board.Name = actor.Name
- data.Board.PrefName = actor.PreferredUsername
- data.Board.Summary = actor.Summary
- data.Board.InReplyTo = ""
- data.Board.To = actor.Outbox
- data.Board.Actor = actor
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Domain = config.Domain
- data.Board.Restricted = actor.Restricted
- data.CurrentPage = page
- data.ReturnTo = "feed"
-
- data.Board.Post.Actor = actor.Id
-
- capt, err := util.GetRandomCaptcha()
- if err != nil {
- return util.MakeError(err, "OutboxGet")
- }
- data.Board.Captcha = config.Domain + "/" + capt
- data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
-
- data.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
-
- data.Key = config.Key
-
- data.Boards = webfinger.Boards
- data.Posts = collection.OrderedItems
-
- data.Pages = pages
- data.TotalPage = len(data.Pages) - 1
-
- data.Meta.Description = data.Board.Summary
- data.Meta.Url = data.Board.Actor.Id
- data.Meta.Title = data.Title
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("nposts", fiber.Map{
- "page": data,
- }, "layouts/main")
-}
diff --git a/routes/post.go b/routes/post.go
deleted file mode 100644
index 4002be7..0000000
--- a/routes/post.go
+++ /dev/null
@@ -1,189 +0,0 @@
-package routes
-
-import (
- "regexp"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
-)
-
-func PostGet(ctx *fiber.Ctx) error {
-
- actor, err := activitypub.GetActorByNameFromDB(ctx.Params("actor"))
- if err != nil {
- return nil
- }
-
- // this is a activitpub json request return json instead of html page
- if activitypub.AcceptActivity(ctx.Get("Accept")) {
- GetActorPost(ctx, ctx.Path())
- return nil
- }
-
- re := regexp.MustCompile("\\w+$")
- postId := re.FindString(ctx.Path())
-
- inReplyTo := actor.Id + "/" + postId
-
- var data PageData
-
- re = regexp.MustCompile("f(\\w|[!@#$%^&*<>])+-(\\w|[!@#$%^&*<>])+")
-
- if re.MatchString(ctx.Path()) { // if non local actor post
- name := activitypub.GetActorFollowNameFromPath(ctx.Path())
-
- followActors, err := actor.GetFollowFromName(name)
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
-
- followCollection, err := activitypub.GetActorsFollowPostFromId(followActors, postId)
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
-
- if len(followCollection.OrderedItems) > 0 {
- data.Board.InReplyTo = followCollection.OrderedItems[0].Id
- data.Posts = append(data.Posts, followCollection.OrderedItems[0])
-
- actor, err := activitypub.FingerActor(data.Board.InReplyTo)
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
-
- data.Board.Post.Actor = actor.Id
- }
- } else {
- obj := activitypub.ObjectBase{Id: inReplyTo}
- collection, err := obj.GetCollectionFromPath()
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
-
- if collection.Actor.Id != "" {
- data.Board.Post.Actor = collection.Actor.Id
- data.Board.InReplyTo = inReplyTo
- }
-
- if len(collection.OrderedItems) > 0 {
- data.Posts = append(data.Posts, collection.OrderedItems[0])
- }
- }
-
- if len(data.Posts) > 0 {
- data.PostId = util.ShortURL(data.Board.To, data.Posts[0].Id)
- }
-
- data.Board.Name = actor.Name
- data.Board.PrefName = actor.PreferredUsername
- data.Board.To = actor.Outbox
- data.Board.Actor = actor
- data.Board.Summary = actor.Summary
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Domain = config.Domain
- data.Board.Restricted = actor.Restricted
- data.ReturnTo = "feed"
-
- capt, err := util.GetRandomCaptcha()
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
- data.Board.Captcha = config.Domain + "/" + capt
- data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
-
- data.Instance, err = activitypub.GetActorFromDB(config.Domain)
- if err != nil {
- return util.MakeError(err, "PostGet")
- }
-
- data.Key = config.Key
- data.Boards = webfinger.Boards
-
- data.Title = "/" + data.Board.Name + "/ - " + data.PostId
-
- if len(data.Posts) > 0 {
- data.Meta.Description = data.Posts[0].Content
- data.Meta.Url = data.Posts[0].Id
- data.Meta.Title = data.Posts[0].Name
- data.Meta.Preview = data.Posts[0].Preview.Href
- }
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("npost", fiber.Map{
- "page": data,
- }, "layouts/main")
-}
-
-func CatalogGet(ctx *fiber.Ctx) error {
- actorName := ctx.Params("actor")
- actor, err := activitypub.GetActorByNameFromDB(actorName)
- if err != nil {
- return util.MakeError(err, "CatalogGet")
- }
-
- collection, err := actor.GetCatalogCollection()
-
- // TODO: implement this in template functions
- // "showArchive": func() bool {
- // col, err := db.GetActorCollectionDBTypeLimit(collection.Actor.Id, "Archive", 1)
- // if err != nil {
- // // TODO: figure out what to do here
- // panic(err)
- // }
- //
- // if len(col.OrderedItems) > 0 {
- // return true
- // }
- // return false
- //},
-
- var data PageData
- data.Board.Name = actor.Name
- data.Board.PrefName = actor.PreferredUsername
- data.Board.InReplyTo = ""
- data.Board.To = actor.Outbox
- data.Board.Actor = actor
- data.Board.Summary = actor.Summary
- data.Board.ModCred, _ = util.GetPasswordFromSession(ctx)
- data.Board.Domain = config.Domain
- data.Board.Restricted = actor.Restricted
- data.Key = config.Key
- data.ReturnTo = "catalog"
-
- data.Board.Post.Actor = actor.Id
-
- data.Instance, err = activitypub.GetActorFromDB(config.Domain)
- if err != nil {
- return util.MakeError(err, "CatalogGet")
- }
-
- capt, err := util.GetRandomCaptcha()
- if err != nil {
- return util.MakeError(err, "CatalogGet")
- }
-
- data.Board.Captcha = config.Domain + "/" + capt
- data.Board.CaptchaCode = post.GetCaptchaCode(data.Board.Captcha)
-
- data.Title = "/" + data.Board.Name + "/ - catalog"
-
- data.Boards = webfinger.Boards
- data.Posts = collection.OrderedItems
-
- data.Meta.Description = data.Board.Summary
- data.Meta.Url = data.Board.Actor.Id
- data.Meta.Title = data.Title
-
- data.Themes = &config.Themes
- data.ThemeCookie = getThemeCookie(ctx)
-
- return ctx.Render("catalog", fiber.Map{
- "page": data,
- }, "layouts/main")
-}
diff --git a/routes/structs.go b/routes/structs.go
deleted file mode 100644
index 1c85676..0000000
--- a/routes/structs.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package routes
-
-import (
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
-)
-
-type PageData struct {
- Title string
- PreferredUsername string
- Board webfinger.Board
- Pages []int
- CurrentPage int
- TotalPage int
- Boards []webfinger.Board
- Posts []activitypub.ObjectBase
- Key string
- PostId string
- Instance activitypub.Actor
- InstanceIndex []activitypub.ObjectBase
- ReturnTo string
- NewsItems []db.NewsItem
- BoardRemainer []int
- Meta Meta
-
- Themes *[]string
- ThemeCookie string
-}
-
-type AdminPage struct {
- Title string
- Board webfinger.Board
- Key string
- Actor string
- Boards []webfinger.Board
- Following []string
- Followers []string
- Reported []db.Report
- Domain string
- IsLocal bool
- PostBlacklist []util.PostBlacklist
- AutoSubscribe bool
- RecentPosts []activitypub.ObjectBase
-
- Themes *[]string
- ThemeCookie string
-}
-
-type Meta struct {
- Title string
- Description string
- Url string
- Preview string
-}
diff --git a/routes/util.go b/routes/util.go
deleted file mode 100644
index ed5dfb7..0000000
--- a/routes/util.go
+++ /dev/null
@@ -1,377 +0,0 @@
-package routes
-
-import (
- "encoding/json"
- "fmt"
- "html/template"
- "regexp"
- "strings"
- "time"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/post"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/FChannel0/FChannel-Server/webfinger"
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/template/html"
-)
-
-func getThemeCookie(c *fiber.Ctx) string {
- cookie := c.Cookies("theme")
- if cookie != "" {
- cookies := strings.SplitN(cookie, "=", 2)
- return cookies[0]
- }
-
- return "default"
-}
-
-func WantToServeCatalog(actorName string) (activitypub.Collection, bool, error) {
- var collection activitypub.Collection
- serve := false
-
- actor, err := activitypub.GetActorByNameFromDB(actorName)
- if err != nil {
- return collection, false, util.MakeError(err, "WantToServeCatalog")
- }
-
- if actor.Id != "" {
- collection, err = actor.GetCatalogCollection()
- if err != nil {
- return collection, false, util.MakeError(err, "WantToServeCatalog")
- }
-
- collection.Actor = actor
- return collection, true, nil
- }
-
- return collection, serve, nil
-}
-
-func WantToServeArchive(actorName string) (activitypub.Collection, bool, error) {
- var collection activitypub.Collection
- serve := false
-
- actor, err := activitypub.GetActorByNameFromDB(actorName)
- if err != nil {
- return collection, false, util.MakeError(err, "WantToServeArchive")
- }
-
- if actor.Id != "" {
- collection, err = actor.GetCollectionType("Archive")
- if err != nil {
- return collection, false, util.MakeError(err, "WantToServeArchive")
- }
-
- collection.Actor = actor
- return collection, true, nil
- }
-
- return collection, serve, nil
-}
-
-func GetActorPost(ctx *fiber.Ctx, path string) error {
- obj := activitypub.ObjectBase{Id: config.Domain + "" + path}
- collection, err := obj.GetCollectionFromPath()
-
- if err != nil {
- return util.MakeError(err, "GetActorPost")
- }
-
- if len(collection.OrderedItems) > 0 {
- enc, err := json.MarshalIndent(collection, "", "\t")
- if err != nil {
- return util.MakeError(err, "GetActorPost")
- }
-
- ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
- _, err = ctx.Write(enc)
- return util.MakeError(err, "GetActorPost")
- }
-
- return nil
-}
-
-func ParseOutboxRequest(ctx *fiber.Ctx, actor activitypub.Actor) error {
- contentType := util.GetContentType(ctx.Get("content-type"))
-
- if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" {
- hasCaptcha, err := util.BoardHasAuthType(actor.Name, "captcha")
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- valid, err := post.CheckCaptcha(ctx.FormValue("captcha"))
- if err == nil && hasCaptcha && valid {
- header, _ := ctx.FormFile("file")
- if header != nil {
- f, _ := header.Open()
- defer f.Close()
- if header.Size > (7 << 20) {
- ctx.Response().Header.SetStatusCode(403)
- _, err := ctx.Write([]byte("7MB max file size"))
- return util.MakeError(err, "ParseOutboxRequest")
- } else if isBanned, err := post.IsMediaBanned(f); err == nil && isBanned {
- //Todo add logging
- config.Log.Println("media banned")
- ctx.Response().Header.SetStatusCode(403)
- _, err := ctx.Write([]byte("media banned"))
- return util.MakeError(err, "ParseOutboxRequest")
- } else if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- contentType, _ := util.GetFileContentType(f)
-
- if !post.SupportedMIMEType(contentType) {
- ctx.Response().Header.SetStatusCode(403)
- _, err := ctx.Write([]byte("file type not supported"))
- return util.MakeError(err, "ParseOutboxRequest")
- }
- }
-
- var nObj = activitypub.CreateObject("Note")
- nObj, err := post.ObjectFromForm(ctx, nObj)
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- nObj.Actor = config.Domain + "/" + actor.Name
-
- nObj, err = nObj.Write()
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- if len(nObj.To) == 0 {
- if err := actor.ArchivePosts(); err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
- }
-
- activity, err := nObj.CreateActivity("Create")
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- activity, err = activity.AddFollowersTo()
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- go activity.MakeRequestInbox()
-
- var id string
- op := len(nObj.InReplyTo) - 1
- if op >= 0 {
- if nObj.InReplyTo[op].Id == "" {
- id = nObj.Id
- } else {
- id = nObj.InReplyTo[0].Id + "|" + nObj.Id
- }
- }
-
- ctx.Response().Header.Set("Status", "200")
- _, err = ctx.Write([]byte(id))
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte("captcha could not auth"))
- return util.MakeError(err, "")
- } else { // json request
- activity, err := activitypub.GetActivityFromJson(ctx)
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- if res, err := activity.IsLocal(); err == nil && res {
- if res := activity.Actor.VerifyHeaderSignature(ctx); err == nil && !res {
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte(""))
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- switch activity.Type {
- case "Create":
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte(""))
- break
-
- case "Follow":
- validActor := (activity.Object.Actor != "")
- validLocalActor := (activity.Actor.Id == actor.Id)
-
- var rActivity activitypub.Activity
-
- if validActor && validLocalActor {
- rActivity = activity.AcceptFollow()
- rActivity, err = rActivity.SetActorFollowing()
-
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- if err := activity.MakeRequestInbox(); err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
- }
-
- actor, _ := activitypub.GetActorFromDB(config.Domain)
- webfinger.FollowingBoards, err = actor.GetFollowing()
-
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- webfinger.Boards, err = webfinger.GetBoardCollection()
-
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
- break
-
- case "Delete":
- config.Log.Println("This is a delete")
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- break
-
- case "Note":
- ctx.Response().Header.Set("Satus", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- break
-
- case "New":
- name := activity.Object.Alias
- prefname := activity.Object.Name
- summary := activity.Object.Summary
- restricted := activity.Object.Sensitive
-
- actor, err := db.CreateNewBoard(*activitypub.CreateNewActor(name, prefname, summary, config.AuthReq, restricted))
- if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- if actor.Id != "" {
- var board []activitypub.ObjectBase
- var item activitypub.ObjectBase
- var removed bool = false
-
- item.Id = actor.Id
- for _, e := range webfinger.FollowingBoards {
- if e.Id != item.Id {
- board = append(board, e)
- } else {
- removed = true
- }
- }
-
- if !removed {
- board = append(board, item)
- }
-
- webfinger.FollowingBoards = board
- webfinger.Boards, err = webfinger.GetBoardCollection()
- return util.MakeError(err, "ParseOutboxRequest")
- }
-
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte(""))
- break
-
- default:
- ctx.Response().Header.Set("status", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- }
- } else if err != nil {
- return util.MakeError(err, "ParseOutboxRequest")
- } else {
- config.Log.Println("is NOT activity")
- ctx.Response().Header.Set("Status", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- return util.MakeError(err, "ParseOutboxRequest")
- }
- }
-
- return nil
-}
-
-func TemplateFunctions(engine *html.Engine) {
- engine.AddFunc("mod", func(i, j int) bool {
- return i%j == 0
- })
-
- engine.AddFunc("sub", func(i, j int) int {
- return i - j
- })
-
- engine.AddFunc("add", func(i, j int) int {
- return i + j
- })
-
- engine.AddFunc("unixtoreadable", func(u int) string {
- return time.Unix(int64(u), 0).Format("Jan 02, 2006")
- })
-
- engine.AddFunc("timeToReadableLong", func(t time.Time) string {
- return t.Format("01/02/06(Mon)15:04:05")
- })
-
- engine.AddFunc("timeToUnix", func(t time.Time) string {
- return fmt.Sprint(t.Unix())
- })
-
- engine.AddFunc("proxy", util.MediaProxy)
-
- // previously short
- engine.AddFunc("shortURL", util.ShortURL)
-
- engine.AddFunc("parseAttachment", post.ParseAttachment)
-
- engine.AddFunc("parseContent", post.ParseContent)
-
- engine.AddFunc("shortImg", util.ShortImg)
-
- engine.AddFunc("convertSize", util.ConvertSize)
-
- engine.AddFunc("isOnion", util.IsOnion)
-
- engine.AddFunc("parseReplyLink", func(actorId string, op string, id string, content string) template.HTML {
- actor, _ := activitypub.FingerActor(actorId)
- title := strings.ReplaceAll(post.ParseLinkTitle(actor.Id+"/", op, content), `/\<`, ">")
- link := ">>" + util.ShortURL(actor.Outbox, id) + ""
- return template.HTML(link)
- })
-
- engine.AddFunc("shortExcerpt", func(post activitypub.ObjectBase) string {
- var returnString string
-
- if post.Name != "" {
- returnString = post.Name + "| " + post.Content
- } else {
- returnString = post.Content
- }
-
- re := regexp.MustCompile(`(^(.|\r\n|\n){100})`)
-
- match := re.FindStringSubmatch(returnString)
-
- if len(match) > 0 {
- returnString = match[0] + "..."
- }
-
- re = regexp.MustCompile(`(^.+\|)`)
-
- match = re.FindStringSubmatch(returnString)
-
- if len(match) > 0 {
- returnString = strings.Replace(returnString, match[0], ""+match[0]+"", 1)
- returnString = strings.Replace(returnString, "|", ":", 1)
- }
-
- return returnString
- })
-}
diff --git a/routes/webfinger.go b/routes/webfinger.go
deleted file mode 100644
index 3d5fa63..0000000
--- a/routes/webfinger.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package routes
-
-import (
- "encoding/json"
- "strings"
-
- "github.com/FChannel0/FChannel-Server/activitypub"
- "github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/util"
- "github.com/gofiber/fiber/v2"
-)
-
-func Webfinger(c *fiber.Ctx) error {
- acct := c.Query("resource")
-
- if len(acct) < 1 {
- c.Status(fiber.StatusBadRequest)
- return c.Send([]byte("resource needs a value"))
- }
-
- acct = strings.Replace(acct, "acct:", "", -1)
-
- actorDomain := strings.Split(acct, "@")
-
- if len(actorDomain) < 2 {
- c.Status(fiber.StatusBadRequest)
- return c.Send([]byte("accepts only subject form of acct:board@instance"))
- }
-
- if actorDomain[0] == "main" {
- actorDomain[0] = ""
- } else {
- actorDomain[0] = "/" + actorDomain[0]
- }
-
- actor := activitypub.Actor{Id: config.TP + "" + actorDomain[1] + "" + actorDomain[0]}
- if res, err := actor.IsLocal(); err == nil && !res {
- c.Status(fiber.StatusBadRequest)
- return c.Send([]byte("actor not local"))
- } else if err != nil {
- return util.MakeError(err, "Webfinger")
- }
-
- var finger activitypub.Webfinger
- var link activitypub.WebfingerLink
-
- finger.Subject = "acct:" + actorDomain[0] + "@" + actorDomain[1]
- link.Rel = "self"
- link.Type = "application/activity+json"
- link.Href = config.TP + "" + actorDomain[1] + "" + actorDomain[0]
-
- finger.Links = append(finger.Links, link)
-
- enc, _ := json.Marshal(finger)
-
- c.Set("Content-Type", config.ActivityStreams)
- return c.Send(enc)
-}
--
cgit v1.2.3