aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.go2
-rw-r--r--config/config.go1
-rw-r--r--db/database.go169
-rw-r--r--main.go527
-rw-r--r--post/util.go59
-rw-r--r--routes/api.go54
-rw-r--r--routes/webfinger.go57
-rw-r--r--util/util.go10
-rw-r--r--webfinger/webfinger.go44
9 files changed, 430 insertions, 493 deletions
diff --git a/client.go b/client.go
index e7cf792..f4094ba 100644
--- a/client.go
+++ b/client.go
@@ -27,7 +27,7 @@ func MediaProxy(url string) string {
return url
}
- MediaHashs[util.HashMedia(url)] = url
+ config.MediaHashs[util.HashMedia(url)] = url
return "/api/media?hash=" + util.HashMedia(url)
}
diff --git a/config/config.go b/config/config.go
index eb8cbdf..0fba1c1 100644
--- a/config/config.go
+++ b/config/config.go
@@ -30,6 +30,7 @@ var Redis = GetConfigValue("redis", "redis://localhost")
var ActivityStreams = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
var AuthReq = []string{"captcha", "email", "passphrase"}
var SupportedFiles = []string{"image/gif", "image/jpeg", "image/png", "image/webp", "image/apng", "video/mp4", "video/ogg", "video/webm", "audio/mpeg", "audio/ogg", "audio/wav", "audio/wave", "audio/x-wav"}
+var MediaHashs = make(map[string]string)
var Key string
var Themes []string
var DB *sql.DB
diff --git a/db/database.go b/db/database.go
index fcdb5f9..eb0bb98 100644
--- a/db/database.go
+++ b/db/database.go
@@ -2,9 +2,11 @@ package db
import (
"database/sql"
+ "encoding/json"
"fmt"
"html/template"
"io/ioutil"
+ "net/http"
"os"
"regexp"
"strings"
@@ -572,18 +574,13 @@ func CheckInactiveInstances() (map[string]string, error) {
func GetAdminAuth() (string, string, error) {
query := fmt.Sprintf("select identifier, code from boardaccess where board='%s' and type='admin'", config.Domain)
- rows, err := config.DB.Query(query)
- if err != nil {
- return "", "", err
- }
-
var code string
var identifier string
+ if err := config.DB.QueryRow(query).Scan(&identifier, &code); err != nil {
+ return "", "", err
+ }
- rows.Next()
- err = rows.Scan(&identifier, &code)
-
- return code, identifier, err
+ return code, identifier, nil
}
func IsHashBanned(hash string) (bool, error) {
@@ -595,3 +592,157 @@ func IsHashBanned(hash string) (bool, error) {
return h == hash, nil
}
+
+func MakeCaptchas(total int) error {
+ dbtotal, err := GetCaptchaTotal()
+ if err != nil {
+ return err
+ }
+
+ difference := total - dbtotal
+
+ for i := 0; i < difference; i++ {
+ if err := CreateNewCaptcha(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func GetActorReported(w http.ResponseWriter, r *http.Request, id string) error {
+ auth := r.Header.Get("Authorization")
+ verification := strings.Split(auth, " ")
+
+ if len(verification) < 2 {
+ w.WriteHeader(http.StatusBadRequest)
+ _, err := w.Write([]byte(""))
+ return err
+ }
+
+ if res, err := HasAuth(verification[1], id); err == nil && !res {
+ w.WriteHeader(http.StatusBadRequest)
+ _, err = w.Write([]byte(""))
+ return err
+ } else if err != nil {
+ return err
+ }
+
+ var following activitypub.Collection
+ var err error
+
+ following.AtContext.Context = "https://www.w3.org/ns/activitystreams"
+ following.Type = "Collection"
+ following.TotalItems, err = activitypub.GetActorReportedTotal(id)
+ if err != nil {
+ return err
+ }
+
+ following.Items, err = activitypub.GetActorReportedDB(id)
+ if err != nil {
+ return err
+ }
+
+ enc, err := json.MarshalIndent(following, "", "\t")
+ if err != nil {
+ return err
+ }
+
+ w.Header().Set("Content-Type", config.ActivityStreams)
+
+ _, err = w.Write(enc)
+ return err
+}
+
+func PrintAdminAuth() error {
+ identifier, code, err := GetAdminAuth()
+ if err != nil {
+ return err
+ }
+
+ fmt.Println("Mod key: " + config.Key)
+ fmt.Println("Admin Login: " + identifier + ", Code: " + code)
+ return nil
+}
+
+func DeleteObjectRequest(id string) error {
+ var nObj activitypub.ObjectBase
+ var nActor activitypub.Actor
+ nObj.Id = id
+ nObj.Actor = nActor.Id
+
+ activity, err := webfinger.CreateActivity("Delete", nObj)
+ if err != nil {
+ return err
+ }
+
+ obj, err := activitypub.GetObjectFromPath(id)
+ if err != nil {
+ return err
+ }
+
+ actor, err := webfinger.FingerActor(obj.Actor)
+ if err != nil {
+ return err
+ }
+ activity.Actor = &actor
+
+ followers, err := activitypub.GetActorFollowDB(obj.Actor)
+ if err != nil {
+ return err
+ }
+
+ for _, e := range followers {
+ activity.To = append(activity.To, e.Id)
+ }
+
+ following, err := activitypub.GetActorFollowingDB(obj.Actor)
+ if err != nil {
+ return err
+ }
+ for _, e := range following {
+ activity.To = append(activity.To, e.Id)
+ }
+
+ return MakeActivityRequest(activity)
+}
+
+func DeleteObjectAndRepliesRequest(id string) error {
+ var nObj activitypub.ObjectBase
+ var nActor activitypub.Actor
+ nObj.Id = id
+ nObj.Actor = nActor.Id
+
+ activity, err := webfinger.CreateActivity("Delete", nObj)
+ if err != nil {
+ return err
+ }
+
+ obj, err := activitypub.GetObjectByIDFromDB(id)
+ if err != nil {
+ return err
+ }
+
+ activity.Actor.Id = obj.OrderedItems[0].Actor
+
+ activity.Object = &obj.OrderedItems[0]
+
+ followers, err := activitypub.GetActorFollowDB(obj.OrderedItems[0].Actor)
+ if err != nil {
+ return err
+ }
+ for _, e := range followers {
+ activity.To = append(activity.To, e.Id)
+ }
+
+ following, err := activitypub.GetActorFollowingDB(obj.OrderedItems[0].Actor)
+ if err != nil {
+ return err
+ }
+
+ for _, e := range following {
+ activity.To = append(activity.To, e.Id)
+ }
+
+ return MakeActivityRequest(activity)
+}
diff --git a/main.go b/main.go
index bafa0ee..363a88f 100644
--- a/main.go
+++ b/main.go
@@ -1,8 +1,14 @@
package main
import (
- "encoding/json"
"fmt"
+ "html/template"
+ "io/ioutil"
+ "math/rand"
+ "path"
+ "regexp"
+ "strings"
+ "time"
"github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/config"
@@ -15,91 +21,17 @@ import (
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/template/html"
- // "github.com/gofrs/uuid"
_ "github.com/lib/pq"
-
- "html/template"
- // "io"
- "io/ioutil"
- // "log"
- "math/rand"
- "net/http"
- "os"
- "os/exec"
- "path"
- "regexp"
- "strings"
- "time"
)
-var MediaHashs = make(map[string]string)
-
-var Themes []string
-
-func init() {
- rand.Seed(time.Now().UnixNano())
-}
-
func main() {
- var err error
- CreatedNeededDirectories()
+ Init()
- db.ConnectDB()
defer db.Close()
-
- db.InitCache()
defer db.CloseCache()
- db.RunDatabaseSchema()
-
- go MakeCaptchas(100)
-
- config.Key = util.CreateKey(32)
-
- webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain)
-
- if err != nil {
- panic(err)
- }
-
- go db.StartupArchive()
-
- go db.CheckInactive()
-
- webfinger.Boards, err = webfinger.GetBoardCollection()
-
- if err != nil {
- panic(err)
- }
-
- // root actor is used to follow remote feeds that are not local
- //name, prefname, summary, auth requirements, restricted
- if config.InstanceName != "" {
- if _, err = db.CreateNewBoardDB(*activitypub.CreateNewActor("", config.InstanceName, config.InstanceSummary, config.AuthReq, false)); err != nil {
- //panic(err)
- }
-
- if config.PublicIndexing == "true" {
- // TODO: comment out later
- //AddInstanceToIndex(config.Domain)
- }
- }
-
- // get list of themes
- themes, err := ioutil.ReadDir("./static/css/themes")
- if err != nil {
- panic(err)
- }
-
- for _, f := range themes {
- if e := path.Ext(f.Name()); e == ".css" {
- config.Themes = append(config.Themes, strings.TrimSuffix(f.Name(), e))
- }
- }
-
- /* Routing and templates */
-
+ // Routing and templates
template := html.New("./views", ".html")
template.Debug(true)
@@ -116,485 +48,114 @@ func main() {
app.Static("/static", "./static")
app.Static("/public", "./public")
- /*
- Main actor
- */
-
+ // Main actor
app.Get("/", routes.Index)
-
app.Get("/inbox", routes.Inbox)
app.Get("/outbox", routes.Outbox)
-
app.Get("/following", routes.Following)
app.Get("/followers", routes.Followers)
- /*
- Admin routes
- */
-
+ // Admin routes
app.Get("/verify", routes.AdminVerify)
-
app.Get("/auth", routes.AdminAuth)
-
app.Get("/"+config.Key+"/", routes.AdminIndex)
-
app.Get("/"+config.Key+"/addboard", routes.AdminAddBoard)
-
app.Get("/"+config.Key+"/postnews", routes.AdminPostNews)
app.Get("/"+config.Key+"/newsdelete", routes.AdminNewsDelete)
app.Get("/news", routes.NewsGet)
- /*
- Board managment
- */
-
+ // Board managment
app.Get("/banmedia", routes.BoardBanMedia)
app.Get("/delete", routes.BoardDelete)
-
app.Get("/deleteattach", routes.BoardDeleteAttach)
app.Get("/marksensitive", routes.BoardMarkSensitive)
-
app.Get("/remove", routes.BoardRemove)
app.Get("/removeattach", routes.BoardRemoveAttach)
-
app.Get("/addtoindex", routes.BoardAddToIndex)
-
app.Get("/poparchive", routes.BoardPopArchive)
-
app.Get("/autosubscribe", routes.BoardAutoSubscribe)
-
app.Get("/blacklist", routes.BoardBlacklist)
app.Get("/report", routes.BoardBlacklist)
+ app.Get("/.well-known/webfinger", routes.Webfinger)
+ app.Get("/api/media", routes.Media)
- app.Get("/.well-known/webfinger", func(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("accpets only subject form of acct:board@instance"))
- }
-
- if actorDomain[0] == "main" {
- actorDomain[0] = ""
- } else {
- actorDomain[0] = "/" + actorDomain[0]
- }
-
- if res, err := activitypub.IsActorLocal(config.TP + "" + actorDomain[1] + "" + actorDomain[0]); err == nil && !res {
- c.Status(fiber.StatusBadRequest)
- return c.Send([]byte("actor not local"))
- } else if err != nil {
- return err
- }
-
- var finger webfinger.Webfinger
- var link webfinger.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)
- })
-
- app.Get("/api/media", func(c *fiber.Ctx) error {
- if c.Query("hash") != "" {
- return RouteImages(c, c.Query("hash"))
- }
-
- return c.SendStatus(404)
- })
-
- /*
- Board actor
- */
-
+ // Board actor
app.Get("/:actor", routes.OutboxGet)
app.Post("/:actor", routes.ActorPost)
-
app.Get("/:actor/catalog", routes.CatalogGet)
app.Get("/:actor/:post", routes.PostGet)
-
app.Get("/:actor/inbox", routes.ActorInbox)
app.Post("/:actor/outbox", routes.ActorOutbox)
-
app.Get("/:actor/following", routes.ActorFollowing)
app.Get("/:actor/followers", routes.ActorFollowers)
-
app.Get("/:actor/reported", routes.ActorReported)
app.Get("/:actor/archive", routes.ActorArchive)
//404 handler
app.Use(routes.NotFound)
- fmt.Println("Mod key: " + config.Key)
- PrintAdminAuth()
+ db.PrintAdminAuth()
app.Listen(config.Port)
}
-func neuter(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if strings.HasSuffix(r.URL.Path, "/") {
- http.NotFound(w, r)
- return
- }
-
- next.ServeHTTP(w, r)
- })
-}
-
-func IsValidActor(id string) (activitypub.Actor, bool, error) {
- actor, err := webfinger.FingerActor(id)
- return actor, actor.Id != "", err
-}
-
-func MakeCaptchas(total int) error {
- dbtotal, err := db.GetCaptchaTotal()
- if err != nil {
- return err
- }
-
- difference := total - dbtotal
-
- for i := 0; i < difference; i++ {
- if err := db.CreateNewCaptcha(); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func GetActorReported(w http.ResponseWriter, r *http.Request, id string) error {
- auth := r.Header.Get("Authorization")
- verification := strings.Split(auth, " ")
-
- if len(verification) < 2 {
- w.WriteHeader(http.StatusBadRequest)
- _, err := w.Write([]byte(""))
- return err
- }
-
- if res, err := db.HasAuth(verification[1], id); err == nil && !res {
- w.WriteHeader(http.StatusBadRequest)
- _, err = w.Write([]byte(""))
- return err
- } else if err != nil {
- return err
- }
-
- var following activitypub.Collection
+func Init() {
var err error
- following.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- following.Type = "Collection"
- following.TotalItems, err = activitypub.GetActorReportedTotal(id)
- if err != nil {
- return err
- }
-
- following.Items, err = activitypub.GetActorReportedDB(id)
- if err != nil {
- return err
- }
-
- enc, err := json.MarshalIndent(following, "", "\t")
- if err != nil {
- return err
- }
-
- w.Header().Set("Content-Type", config.ActivityStreams)
-
- _, err = w.Write(enc)
- return err
-}
-
-func PrintAdminAuth() error {
- identifier, code, err := db.GetAdminAuth()
- if err != nil {
- return err
- }
-
- fmt.Println("Admin Login: " + identifier + ", Code: " + code)
- return nil
-}
-
-func DeleteObjectRequest(id string) error {
- var nObj activitypub.ObjectBase
- var nActor activitypub.Actor
- nObj.Id = id
- nObj.Actor = nActor.Id
-
- activity, err := webfinger.CreateActivity("Delete", nObj)
- if err != nil {
- return err
- }
-
- obj, err := activitypub.GetObjectFromPath(id)
- if err != nil {
- return err
- }
-
- actor, err := webfinger.FingerActor(obj.Actor)
- if err != nil {
- return err
- }
- activity.Actor = &actor
-
- followers, err := activitypub.GetActorFollowDB(obj.Actor)
- if err != nil {
- return err
- }
-
- for _, e := range followers {
- activity.To = append(activity.To, e.Id)
- }
-
- following, err := activitypub.GetActorFollowingDB(obj.Actor)
- if err != nil {
- return err
- }
- for _, e := range following {
- activity.To = append(activity.To, e.Id)
- }
+ rand.Seed(time.Now().UnixNano())
- return db.MakeActivityRequest(activity)
-}
+ util.CreatedNeededDirectories()
-func DeleteObjectAndRepliesRequest(id string) error {
- var nObj activitypub.ObjectBase
- var nActor activitypub.Actor
- nObj.Id = id
- nObj.Actor = nActor.Id
+ db.ConnectDB()
- activity, err := webfinger.CreateActivity("Delete", nObj)
- if err != nil {
- return err
- }
+ db.InitCache()
- obj, err := activitypub.GetObjectByIDFromDB(id)
- if err != nil {
- return err
- }
+ db.RunDatabaseSchema()
- activity.Actor.Id = obj.OrderedItems[0].Actor
+ go db.MakeCaptchas(100)
- activity.Object = &obj.OrderedItems[0]
+ config.Key = util.CreateKey(32)
- followers, err := activitypub.GetActorFollowDB(obj.OrderedItems[0].Actor)
- if err != nil {
- return err
- }
- for _, e := range followers {
- activity.To = append(activity.To, e.Id)
- }
+ webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain)
- following, err := activitypub.GetActorFollowingDB(obj.OrderedItems[0].Actor)
if err != nil {
- return err
- }
-
- for _, e := range following {
- activity.To = append(activity.To, e.Id)
- }
-
- return db.MakeActivityRequest(activity)
-}
-
-func ResizeAttachmentToPreview() error {
- return activitypub.GetObjectsWithoutPreviewsCallback(func(id, href, mediatype, name string, size int, published time.Time) error {
- re := regexp.MustCompile(`^\w+`)
-
- _type := re.FindString(mediatype)
-
- if _type == "image" {
-
- re = regexp.MustCompile(`.+/`)
-
- file := re.ReplaceAllString(mediatype, "")
-
- nHref := util.GetUniqueFilename(file)
-
- var nPreview activitypub.NestedObjectBase
-
- re = regexp.MustCompile(`/\w+$`)
- actor := re.ReplaceAllString(id, "")
-
- nPreview.Type = "Preview"
- uid, err := util.CreateUniqueID(actor)
- if err != nil {
- return err
- }
-
- nPreview.Id = fmt.Sprintf("%s/%s", actor, uid)
- nPreview.Name = name
- nPreview.Href = config.Domain + "" + nHref
- nPreview.MediaType = mediatype
- nPreview.Size = int64(size)
- nPreview.Published = published
- nPreview.Updated = published
-
- re = regexp.MustCompile(`/public/.+`)
-
- objFile := re.FindString(href)
-
- if id != "" {
- cmd := exec.Command("convert", "."+objFile, "-resize", "250x250>", "-strip", "."+nHref)
-
- if err := cmd.Run(); err == nil {
- fmt.Println(objFile + " -> " + nHref)
- if err := activitypub.WritePreviewToDB(nPreview); err != nil {
- return err
- }
- if err := activitypub.UpdateObjectWithPreview(id, nPreview.Id); err != nil {
- return err
- }
- } else {
- return err
- }
- }
- }
-
- return nil
- })
-}
-
-func CreatedNeededDirectories() {
- if _, err := os.Stat("./public"); os.IsNotExist(err) {
- os.Mkdir("./public", 0755)
+ panic(err)
}
- if _, err := os.Stat("./pem/board"); os.IsNotExist(err) {
- os.MkdirAll("./pem/board", 0700)
- }
-}
+ go db.StartupArchive()
-func AddInstanceToIndex(actor string) error {
- // TODO: completely disabling this until it is actually reasonable to turn it on
- // only actually allow this when it more or less works, i.e. can post, make threads, manage boards, etc
- return nil
+ go db.CheckInactive()
- // if local testing enviroment do not add to index
- re := regexp.MustCompile(`(.+)?(localhost|\d+\.\d+\.\d+\.\d+)(.+)?`)
- if re.MatchString(actor) {
- return nil
- }
+ webfinger.Boards, err = webfinger.GetBoardCollection()
- // also while i'm here
- // TODO: maybe allow different indexes?
- followers, err := activitypub.GetCollectionFromID("https://fchan.xyz/followers")
if err != nil {
- return err
- }
-
- var alreadyIndex = false
- for _, e := range followers.Items {
- if e.Id == actor {
- alreadyIndex = true
- }
+ panic(err)
}
- if !alreadyIndex {
- req, err := http.NewRequest("GET", "https://fchan.xyz/addtoindex?id="+actor, nil)
- if err != nil {
- return err
- }
-
- if _, err := http.DefaultClient.Do(req); err != nil {
- return err
+ // root actor is used to follow remote feeds that are not local
+ //name, prefname, summary, auth requirements, restricted
+ if config.InstanceName != "" {
+ if _, err = db.CreateNewBoardDB(*activitypub.CreateNewActor("", config.InstanceName, config.InstanceSummary, config.AuthReq, false)); err != nil {
+ //panic(err)
}
- }
-
- return nil
-}
-
-func AddInstanceToIndexDB(actor string) error {
- // TODO: completely disabling this until it is actually reasonable to turn it on
- // only actually allow this when it more or less works, i.e. can post, make threads, manage boards, etc
- return nil
- //sleep to be sure the webserver is fully initialized
- //before making finger request
- time.Sleep(15 * time.Second)
-
- nActor, err := webfinger.FingerActor(actor)
- if err != nil {
- return err
- }
-
- if nActor.Id == "" {
- return nil
- }
-
- // TODO: maybe allow different indexes?
- followers, err := activitypub.GetCollectionFromID("https://fchan.xyz/followers")
- if err != nil {
- return err
- }
-
- var alreadyIndex = false
- for _, e := range followers.Items {
- if e.Id == nActor.Id {
- alreadyIndex = true
+ if config.PublicIndexing == "true" {
+ // TODO: comment out later
+ //AddInstanceToIndex(config.Domain)
}
}
- if !alreadyIndex {
- return activitypub.AddFollower("https://fchan.xyz", nActor.Id)
- }
-
- return nil
-}
-
-func RouteImages(ctx *fiber.Ctx, media string) error {
- req, err := http.NewRequest("GET", MediaHashs[media], nil)
- if err != nil {
- return err
- }
-
- client := http.Client{
- Timeout: 5 * time.Second,
- }
-
- resp, err := client.Do(req)
+ // get list of themes
+ themes, err := ioutil.ReadDir("./static/css/themes")
if err != nil {
- return err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- fileBytes, err := ioutil.ReadFile("./static/notfound.png")
- if err != nil {
- return err
- }
-
- return ctx.Send(fileBytes)
+ panic(err)
}
- body, _ := ioutil.ReadAll(resp.Body)
- for name, values := range resp.Header {
- for _, value := range values {
- ctx.Append(name, value)
+ for _, f := range themes {
+ if e := path.Ext(f.Name()); e == ".css" {
+ config.Themes = append(config.Themes, strings.TrimSuffix(f.Name(), e))
}
}
-
- return ctx.Send(body)
}
func TemplateFunctions(engine *html.Engine) {
diff --git a/post/util.go b/post/util.go
index a9d326a..c653276 100644
--- a/post/util.go
+++ b/post/util.go
@@ -1,6 +1,7 @@
package post
import (
+ "fmt"
"io/ioutil"
"mime/multipart"
"os"
@@ -323,3 +324,61 @@ func ObjectFromForm(ctx *fiber.Ctx, obj activitypub.ObjectBase) (activitypub.Obj
return obj, nil
}
+
+func ResizeAttachmentToPreview() error {
+ return activitypub.GetObjectsWithoutPreviewsCallback(func(id, href, mediatype, name string, size int, published time.Time) error {
+ re := regexp.MustCompile(`^\w+`)
+
+ _type := re.FindString(mediatype)
+
+ if _type == "image" {
+
+ re = regexp.MustCompile(`.+/`)
+
+ file := re.ReplaceAllString(mediatype, "")
+
+ nHref := util.GetUniqueFilename(file)
+
+ var nPreview activitypub.NestedObjectBase
+
+ re = regexp.MustCompile(`/\w+$`)
+ actor := re.ReplaceAllString(id, "")
+
+ nPreview.Type = "Preview"
+ uid, err := util.CreateUniqueID(actor)
+ if err != nil {
+ return err
+ }
+
+ nPreview.Id = fmt.Sprintf("%s/%s", actor, uid)
+ nPreview.Name = name
+ nPreview.Href = config.Domain + "" + nHref
+ nPreview.MediaType = mediatype
+ nPreview.Size = int64(size)
+ nPreview.Published = published
+ nPreview.Updated = published
+
+ re = regexp.MustCompile(`/public/.+`)
+
+ objFile := re.FindString(href)
+
+ if id != "" {
+ cmd := exec.Command("convert", "."+objFile, "-resize", "250x250>", "-strip", "."+nHref)
+
+ if err := cmd.Run(); err == nil {
+ fmt.Println(objFile + " -> " + nHref)
+ if err := activitypub.WritePreviewToDB(nPreview); err != nil {
+ return err
+ }
+ if err := activitypub.UpdateObjectWithPreview(id, nPreview.Id); err != nil {
+ return err
+ }
+ } else {
+ return err
+ }
+ }
+ }
+
+ return nil
+ })
+}
diff --git a/routes/api.go b/routes/api.go
new file mode 100644
index 0000000..2fb0f3f
--- /dev/null
+++ b/routes/api.go
@@ -0,0 +1,54 @@
+package routes
+
+import (
+ "io/ioutil"
+ "net/http"
+ "time"
+
+ "github.com/FChannel0/FChannel-Server/config"
+ "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 err
+ }
+
+ client := http.Client{
+ Timeout: 5 * time.Second,
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ fileBytes, err := ioutil.ReadFile("./static/notfound.png")
+ if err != nil {
+ return err
+ }
+
+ _, err = ctx.Write(fileBytes)
+ return err
+ }
+
+ 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/webfinger.go b/routes/webfinger.go
new file mode 100644
index 0000000..221b7dc
--- /dev/null
+++ b/routes/webfinger.go
@@ -0,0 +1,57 @@
+package routes
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "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("accpets only subject form of acct:board@instance"))
+ }
+
+ if actorDomain[0] == "main" {
+ actorDomain[0] = ""
+ } else {
+ actorDomain[0] = "/" + actorDomain[0]
+ }
+
+ if res, err := activitypub.IsActorLocal(config.TP + "" + actorDomain[1] + "" + actorDomain[0]); err == nil && !res {
+ c.Status(fiber.StatusBadRequest)
+ return c.Send([]byte("actor not local"))
+ } else if err != nil {
+ return err
+ }
+
+ var finger webfinger.Webfinger
+ var link webfinger.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/util/util.go b/util/util.go
index ade5eae..0ffbf01 100644
--- a/util/util.go
+++ b/util/util.go
@@ -251,3 +251,13 @@ func GetContentType(location string) string {
return location
}
}
+
+func CreatedNeededDirectories() {
+ if _, err := os.Stat("./public"); os.IsNotExist(err) {
+ os.Mkdir("./public", 0755)
+ }
+
+ if _, err := os.Stat("./pem/board"); os.IsNotExist(err) {
+ os.MkdirAll("./pem/board", 0700)
+ }
+}
diff --git a/webfinger/webfinger.go b/webfinger/webfinger.go
index 7e58d00..a58c9ab 100644
--- a/webfinger/webfinger.go
+++ b/webfinger/webfinger.go
@@ -6,6 +6,7 @@ import (
"net/http"
"regexp"
"strings"
+ "time"
"github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/config"
@@ -239,3 +240,46 @@ func AddFollowersToActivity(activity activitypub.Activity) (activitypub.Activity
return activity, nil
}
+
+func IsValidActor(id string) (activitypub.Actor, bool, error) {
+ actor, err := FingerActor(id)
+ return actor, actor.Id != "", err
+}
+
+func AddInstanceToIndexDB(actor string) error {
+ // TODO: completely disabling this until it is actually reasonable to turn it on
+ // only actually allow this when it more or less works, i.e. can post, make threads, manage boards, etc
+ return nil
+
+ //sleep to be sure the webserver is fully initialized
+ //before making finger request
+ time.Sleep(15 * time.Second)
+
+ nActor, err := FingerActor(actor)
+ if err != nil {
+ return err
+ }
+
+ if nActor.Id == "" {
+ return nil
+ }
+
+ // TODO: maybe allow different indexes?
+ followers, err := activitypub.GetCollectionFromID("https://fchan.xyz/followers")
+ if err != nil {
+ return err
+ }
+
+ var alreadyIndex = false
+ for _, e := range followers.Items {
+ if e.Id == nActor.Id {
+ alreadyIndex = true
+ }
+ }
+
+ if !alreadyIndex {
+ return activitypub.AddFollower("https://fchan.xyz", nActor.Id)
+ }
+
+ return nil
+}