mlmym/routes.go

1682 lines
48 KiB
Go
Raw Normal View History

2023-06-30 19:41:35 +00:00
package main
import (
"bytes"
"context"
_ "embed"
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
2023-06-30 19:41:35 +00:00
"regexp"
"strconv"
"strings"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter"
2023-07-05 15:22:46 +00:00
"github.com/k3a/html2text"
2023-12-17 01:09:35 +00:00
"github.com/rystaf/go-lemmy"
2023-06-30 19:41:35 +00:00
"golang.org/x/text/language"
"golang.org/x/text/message"
)
2024-06-02 17:45:42 +00:00
var pictrs = regexp.MustCompile(`\/pictrs\/image\/([a-z0-9\-]+)\.([a-z]+)$`)
var imgur = regexp.MustCompile(`^https:\/\/(i\.)?imgur.com\/([a-zA-Z0-9]{5,})(\.[a-zA-Z0-9]+)?`)
var isImage = func(u string) bool {
p, err := url.Parse(u)
if err != nil || p.Path == "" {
return false
}
ext := filepath.Ext(p.Path)
if ext == ".jpeg" || ext == ".jpg" || ext == ".png" || ext == ".webp" || ext == ".gif" {
return true
}
return false
}
2023-06-30 19:41:35 +00:00
var funcMap = template.FuncMap{
"host": func(host string) string {
if l := os.Getenv("LEMMY_DOMAIN"); l != "" {
return l
}
return host
},
"localize": func(s string) string {
2023-06-30 19:41:35 +00:00
u, err := url.Parse(s)
if err != nil {
return s
}
return "." + u.Path + "@" + u.Host
2023-06-30 19:41:35 +00:00
},
"printer": func(n any) string {
p := message.NewPrinter(language.English)
return p.Sprintf("%d", n)
},
2023-12-16 14:43:43 +00:00
"likedPerc": func(c lemmy.PostAggregates) string {
2023-06-30 19:41:35 +00:00
return fmt.Sprintf("%.1f", (float64(c.Upvotes)/float64(c.Upvotes+c.Downvotes))*100)
},
2023-12-16 14:43:43 +00:00
"fullname": func(person lemmy.Person) string {
2023-06-30 19:41:35 +00:00
if person.Local {
return person.Name
}
l, err := url.Parse(person.ActorID)
if err != nil {
fmt.Println(err)
return person.Name
}
return person.Name + "@" + l.Host
},
2023-12-16 14:43:43 +00:00
"fullcname": func(c lemmy.Community) string {
2023-06-30 19:41:35 +00:00
if c.Local {
return c.Name
}
l, err := url.Parse(c.ActorID)
if err != nil {
fmt.Println(err)
return c.Name
}
return c.Name + "@" + l.Host
},
2023-12-16 14:43:43 +00:00
"isMod": func(c *lemmy.GetCommunityResponse, username string) bool {
2023-06-30 19:41:35 +00:00
for _, mod := range c.Moderators {
if mod.Moderator.Local && username == mod.Moderator.Name {
return true
}
}
return false
},
2023-12-17 02:24:26 +00:00
"instance": func(actorID string) string {
l, err := url.Parse(actorID)
if err != nil {
fmt.Println(err)
return ""
}
return l.Host
},
"domain": func(p Post) string {
2023-06-30 19:41:35 +00:00
if p.Post.URL.IsValid() {
l, err := url.Parse(p.Post.URL.String())
if err != nil {
return ""
}
return l.Host
}
if p.Post.Local {
return "self." + p.Community.Name
}
l, err := url.Parse(p.Post.ApID)
if err != nil {
return ""
}
return l.Host
},
2023-12-16 14:43:43 +00:00
"membership": func(s lemmy.SubscribedType) string {
2023-06-30 19:41:35 +00:00
switch s {
2023-12-16 14:43:43 +00:00
case lemmy.SubscribedTypeSubscribed:
2023-06-30 19:41:35 +00:00
return "leave"
2023-12-16 14:43:43 +00:00
case lemmy.SubscribedTypeNotSubscribed:
2023-06-30 19:41:35 +00:00
return "join"
2023-12-16 14:43:43 +00:00
case lemmy.SubscribedTypePending:
2023-06-30 19:41:35 +00:00
return "pending"
}
return ""
},
2024-06-02 17:45:42 +00:00
"isImage": isImage,
2024-04-20 02:49:22 +00:00
"isYoutube": func(u string) bool {
re := regexp.MustCompile(`^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|live\/|v\/)?)([\w\-]+)(\S+)?$`)
2024-05-14 23:46:33 +00:00
return re.MatchString(u)
2024-04-20 02:49:22 +00:00
},
2023-12-16 14:43:43 +00:00
"thumbnail": func(p lemmy.Post) string {
2024-06-02 17:45:42 +00:00
if pictrs.MatchString(p.ThumbnailURL.String()) {
return p.ThumbnailURL.String() + "?format=jpg&thumbnail=64"
} else if pictrs.MatchString(p.URL.String()) {
return p.URL.String() + "?format=jpg&thumbnail=64"
}
if imgur.MatchString(p.URL.String()) {
return imgur.ReplaceAllString(p.URL.String(), "https://i.imgur.com/${2}s.jpg")
2023-07-07 17:42:57 +00:00
}
2024-06-02 17:45:42 +00:00
if p.ThumbnailURL.IsValid() {
return p.ThumbnailURL.String()
2023-07-07 17:57:01 +00:00
}
2024-06-02 17:45:42 +00:00
if isImage(p.URL.String()) {
return "/_/static/photo.png"
2023-07-07 17:57:01 +00:00
}
if p.URL.IsValid() {
2023-07-07 17:42:57 +00:00
return "/_/static/link.png"
}
return "/_/static/text.png"
},
2024-06-02 17:45:42 +00:00
"banner": func(site lemmy.Site) string {
bannerURL := ""
if site.Banner.IsValid() {
bannerURL = site.Banner.String()
} else if site.Icon.IsValid() {
bannerURL = site.Icon.String()
}
if pictrs.MatchString(bannerURL) {
return bannerURL + "?format=jpg&thumbnail=300"
}
return bannerURL
},
"cbanner": func(c lemmy.Community) string {
bannerURL := ""
if c.Banner.IsValid() {
bannerURL = c.Banner.String()
} else if c.Icon.IsValid() {
bannerURL = c.Icon.String()
}
if pictrs.MatchString(bannerURL) {
return bannerURL + "?format=jpg&thumbnail=300"
}
return bannerURL
},
"shrink": func(u string) string {
if pictrs.MatchString(u) {
return u + "?format=png&thumbnail=60"
}
return u
},
2023-06-30 19:41:35 +00:00
"humanize": humanize.Time,
"markdown": func(host string, body string) template.HTML {
var buf bytes.Buffer
2023-07-07 16:25:40 +00:00
re := regexp.MustCompile(`\s---\s`)
body = re.ReplaceAllString(body, "\n***\n")
2023-07-24 03:19:49 +00:00
// community bangs
2023-07-24 04:24:29 +00:00
body = RegReplace(body, `([^\[])!([a-zA-Z0-9_]+)@([a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+)`, `$1[!$2@$3](/c/$2@$3)`)
2023-06-30 19:41:35 +00:00
if err := md.Convert([]byte(body), &buf); err != nil {
2023-07-05 15:22:46 +00:00
fmt.Println(err)
return template.HTML(body)
2023-06-30 19:41:35 +00:00
}
body = buf.String()
body = strings.Replace(body, `<img `, `<img loading="lazy" `, -1)
if host != "." {
body = RegReplace(body, `href="/`, `href="./`)
}
2024-02-28 11:16:43 +00:00
//body = RegReplace(body, `href="(https:\/\/[a-zA-Z0-9\.\-]+\/(c|u|comment|post)\/[^#\?]*?)"`, `href="/`+host+`/link?url=$1"`)
2023-07-16 17:45:40 +00:00
body = RegReplace(body, `::: ?spoiler (.*?)\n([\S\s]*?):::`, "<details><summary>$1</summary>$2</details>")
return template.HTML(body)
2023-06-30 19:41:35 +00:00
},
2023-07-05 15:22:46 +00:00
"rmmarkdown": func(body string) string {
var buf bytes.Buffer
if err := md.Convert([]byte(body), &buf); err != nil {
fmt.Println(err)
return body
}
text := html2text.HTML2TextWithOptions(buf.String(), html2text.WithLinksInnerText())
2023-07-28 17:04:41 +00:00
re := regexp.MustCompile(`\<(https?:\/\/|mailto)(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)\>`)
2023-07-05 15:22:46 +00:00
return re.ReplaceAllString(text, "")
},
2023-06-30 19:41:35 +00:00
"contains": strings.Contains,
2023-12-16 14:43:43 +00:00
"sub": func(a int64, b int) int {
2023-06-30 19:41:35 +00:00
return int(a) - b
},
2023-12-16 14:43:43 +00:00
"add": func(a int64, b int) int {
return int(a) + b
},
2023-12-21 14:30:41 +00:00
"is": func(x *bool) bool {
if x == nil {
return false
}
return *x
},
2023-06-30 19:41:35 +00:00
}
func LemmyLinkRewrite(input string, host string, lemmy_domain string) (body string) {
body = input
// localize community and user links
body = RegReplace(body, `href="https:\/\/([a-zA-Z0-9\.\-]+)\/((c|u|comment|post)\/[^#\?]*?)"`, `href="/$2@$1"`)
// remove extra instance tag
2023-07-16 02:41:39 +00:00
body = RegReplace(body, `href="(https:\/)?(\/[a-zA-Z0-9\.\-]+)?\/((c|u)\/[a-zA-Z0-9]+@[a-zA-Z0-9\.\-]+)@([a-zA-Z0-9\.\-]+)"`, `href="/$3"`)
if lemmy_domain == "" {
// add domain to relative links
2023-07-16 13:00:49 +00:00
body = RegReplace(body, `href="\/((c|u|post|comment)\/(.*?)")`, `href="/`+host+`/$1`)
// convert links to relative
2023-07-16 13:00:49 +00:00
body = RegReplace(body, `href="https:\/\/([a-zA-Z0-9\.\-]+\/((c|u|post|comment)\/[a-zA-Z0-9]+"))`, `href="/$1`)
} else {
// convert local links to relative
2023-07-16 13:00:49 +00:00
body = RegReplace(body, `href="https:\/\/`+lemmy_domain+`\/(c\/[a-zA-Z0-9]+"|(c|u|post|comment)\/(.*?)")`, `href="/$1`)
body = RegReplace(body, `href="(.*)@`+lemmy_domain+`"`, `href="$1"`)
}
re := regexp.MustCompile(`href="\/?([a-zA-Z0-9\.\-]*)\/(c|u|post|comment)\/(.*?)@(.*?)"`)
// assume "old." subdomain is mlmym and remove
matches := re.FindAllStringSubmatch(body, -1)
for _, match := range matches {
if match[4][0:4] == "old." {
s := 1
if match[1] == "" {
s += 1
}
body = strings.Replace(body, match[0], `href="/`+strings.Join(match[s:4], "/")+"@"+match[4][4:]+`"`, -1)
}
}
// remove redundant instance tag
matches = re.FindAllStringSubmatch(body, -1)
for _, match := range matches {
if match[1] == match[4] {
body = strings.Replace(body, match[0], `href="/`+strings.Join(match[1:4], "/")+`"`, -1)
}
}
return body
}
func RegReplace(input string, match string, replace string) string {
re := regexp.MustCompile(match)
return re.ReplaceAllString(input, replace)
}
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}
2023-06-30 19:41:35 +00:00
func Initialize(Host string, r *http.Request) (State, error) {
state := State{
2023-07-23 22:19:53 +00:00
Host: Host,
Page: 1,
Status: http.StatusOK,
Version: version,
2023-06-30 19:41:35 +00:00
}
2023-07-26 20:53:06 +00:00
if watch != nil {
state.Watch = *watch
}
lemmyDomain := os.Getenv("LEMMY_DOMAIN")
if lemmyDomain != "" {
state.Host = "."
Host = lemmyDomain
}
2023-07-01 02:10:24 +00:00
remoteAddr := r.RemoteAddr
if r.Header.Get("CF-Connecting-IP") != "" {
remoteAddr = r.Header.Get("CF-Connecting-IP")
}
client := http.Client{Transport: NewAddHeaderTransport(remoteAddr)}
c, err := lemmy.NewWithClient("https://"+Host, &client)
2023-06-30 19:41:35 +00:00
if err != nil {
fmt.Println(err)
state.Status = http.StatusInternalServerError
return state, err
}
state.HTTPClient = &client
state.Client = c
token := getCookie(r, "jwt")
user := getCookie(r, "user")
parts := strings.Split(user, ":")
2023-07-10 15:53:15 +00:00
if len(parts) == 2 && token != "" {
if id, err := strconv.Atoi(parts[1]); err == nil {
2023-06-30 19:41:35 +00:00
state.Client.Token = token
sess := Session{
UserName: parts[0],
UserID: id,
2023-06-30 19:41:35 +00:00
}
state.Session = &sess
}
}
state.Listing = getCookie(r, "DefaultListingType")
state.Sort = getCookie(r, "DefaultSortType")
state.CommentSort = getCookie(r, "DefaultCommentSortType")
2024-06-02 23:08:22 +00:00
if hideSidebar := getCookie(r, "HideSidebar"); hideSidebar == "1" {
state.HideSidebar = true
}
if dark := getCookie(r, "Dark"); dark != "" {
2023-12-21 14:30:41 +00:00
state.Dark = new(bool)
*state.Dark = dark != "0"
} else if dark := os.Getenv("DARK"); dark != "" {
state.Dark = new(bool)
*state.Dark = true
}
state.ShowNSFW = getCookie(r, "ShowNSFW") != ""
state.HideInstanceNames = getCookie(r, "HideInstanceNames") != ""
if hide := getCookie(r, "HideThumbnails"); hide != "" {
state.HideThumbnails = hide != "0"
} else {
2023-08-08 21:57:47 +00:00
state.HideThumbnails = os.Getenv("HIDE_THUMBNAILS") != ""
}
state.ParseQuery(r.URL.RawQuery)
if state.Sort == "" {
state.Sort = getenv("SORT", "Hot")
}
if state.CommentSort == "" {
state.CommentSort = getenv("COMMENT_SORT", "Hot")
}
if state.Listing == "" || state.Session == nil && state.Listing == "Subscribed" {
state.Listing = getenv("LISTING", "All")
2023-06-30 19:41:35 +00:00
}
if linksInNewWindow := getCookie(r, "LinksInNewWindow"); linksInNewWindow != "" {
state.LinksInNewWindow = linksInNewWindow != "0"
} else {
state.LinksInNewWindow = os.Getenv("LINKS_IN_NEW_WINDOW") != ""
}
2023-06-30 19:41:35 +00:00
return state, nil
}
func GetTemplate(name string) (*template.Template, error) {
if *watch {
t := template.New(name).Funcs(funcMap)
glob, err := t.ParseGlob("templates/*")
if err != nil {
return nil, err
}
return glob, nil
}
t, ok := templates[name]
if !ok {
2023-09-03 16:48:04 +00:00
return nil, errors.New("template not found: " + name)
2023-06-30 19:41:35 +00:00
}
return t, nil
}
func Render(w http.ResponseWriter, templateName string, state State) {
tmpl, err := GetTemplate(templateName)
if err != nil {
w.Write([]byte("500 - Server Error"))
2023-07-03 01:50:03 +00:00
fmt.Println(err)
2023-06-30 19:41:35 +00:00
return
}
if len(state.TopCommunities) == 0 {
state.GetCommunities()
}
if state.Session != nil {
state.GetUnreadCount()
}
if state.Status != http.StatusOK {
w.WriteHeader(state.Status)
}
2023-07-10 15:53:15 +00:00
header := w.Header()
header.Set("Content-Security-Policy", "script-src 'self'")
2023-06-30 19:41:35 +00:00
err = tmpl.Execute(w, state)
if err != nil {
fmt.Println("execute fail", err)
w.Write([]byte("500 - Server Error"))
return
}
}
func GetRoot(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
data := make(map[string]any)
2023-07-06 18:57:48 +00:00
data["Title"] = r.Host
2024-05-11 18:48:49 +00:00
d := true
2023-09-03 16:52:41 +00:00
if dark := getCookie(r, "Dark"); dark != "" {
2024-05-11 18:48:49 +00:00
d = dark != "0"
data["Dark"] = &d
} else if dark := os.Getenv("DARK"); dark != "" {
2024-05-11 18:48:49 +00:00
data["Dark"] = &d
2023-09-03 16:52:41 +00:00
}
2023-06-30 19:41:35 +00:00
tmpl, err := GetTemplate("root.html")
if err != nil {
2024-05-11 18:48:49 +00:00
fmt.Println("template fail", err)
2023-06-30 19:41:35 +00:00
w.Write([]byte("500 - Server Error"))
return
}
2024-05-11 18:48:49 +00:00
if err = tmpl.Execute(w, data); err != nil {
fmt.Println("execute fail", err)
}
2023-06-30 19:41:35 +00:00
}
type NodeSoftware struct {
Name string `json:"name"`
Version string `json:"version"`
}
type NodeInfo struct {
Software NodeSoftware `json:"software"`
}
2023-07-01 14:57:04 +00:00
func IsLemmy(domain string, remoteAddr string) bool {
client := http.Client{Transport: NewAddHeaderTransport(remoteAddr)}
2023-06-30 19:41:35 +00:00
var nodeInfo NodeInfo
2023-07-01 14:57:04 +00:00
res, err := client.Get("https://" + domain + "/nodeinfo/2.0.json")
2023-06-30 19:41:35 +00:00
if err != nil {
return false
}
err = json.NewDecoder(res.Body).Decode(&nodeInfo)
if err != nil {
return false
}
if nodeInfo.Software.Name == "lemmy" {
return true
}
return false
}
func PostRoot(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
data := make(map[string]any)
tmpl, err := GetTemplate("root.html")
if err != nil {
fmt.Println("execute fail", err)
w.Write([]byte("500 - Server Error"))
return
}
2023-07-01 02:10:24 +00:00
input := r.FormValue("destination")
2023-06-30 19:41:35 +00:00
re := regexp.MustCompile(`^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z
2023-07-01 02:10:24 +00:00
]{2,3})`)
if re.MatchString(input) {
input = "https://" + input
2023-06-30 19:41:35 +00:00
}
2024-05-14 23:46:33 +00:00
dest, _ := url.Parse(input)
2023-07-01 14:57:04 +00:00
if dest.Host != "" {
state, _ := Initialize(dest.Host, r)
if err := state.LemmyError(dest.Host); err != nil {
data["Error"] = err
} else {
redirectUrl := "/" + dest.Host + dest.Path
if dest.RawQuery != "" {
redirectUrl = redirectUrl + "?" + dest.RawQuery
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, redirectUrl, http.StatusFound)
2023-07-01 14:57:04 +00:00
return
2023-06-30 19:41:35 +00:00
}
2023-07-01 14:57:04 +00:00
} else {
data["Error"] = "Invalid destination"
2023-06-30 19:41:35 +00:00
}
tmpl.Execute(w, data)
}
func GetIcon(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if ps.ByName("host") == "favicon.ico" {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 - Not Found"))
}
2024-05-14 23:46:33 +00:00
state, _ := Initialize(ps.ByName("host"), r)
2023-06-30 19:41:35 +00:00
state.Client.Token = ""
2023-12-16 14:43:43 +00:00
resp, err := state.Client.Site(context.Background())
2023-06-30 19:41:35 +00:00
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - Server Error"))
return
}
if !resp.SiteView.Site.Icon.IsValid() {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 - Not Found"))
return
}
2024-06-02 17:45:42 +00:00
u := resp.SiteView.Site.Icon.String()
if pictrs.MatchString(u) {
2024-06-02 23:08:22 +00:00
u += "?format=png&thumbnail=60"
2024-06-02 17:45:42 +00:00
}
iresp, err := state.HTTPClient.Get(u)
2023-06-30 19:41:35 +00:00
if err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - Server Error"))
return
}
defer iresp.Body.Close()
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Cache-Control", "max-age=2592000")
io.Copy(w, iresp.Body)
}
func GetFrontpage(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["edit"]) > 0 {
state.Op = "edit_community"
}
if ps.ByName("community") == "" || state.Op == "edit_community" {
state.GetSite()
}
state.GetCommunity(ps.ByName("community"))
if state.Op == "" {
state.GetPosts()
}
2023-07-05 22:34:46 +00:00
if state.XHR {
Render(w, "xhr.html", state)
} else {
Render(w, "frontpage.html", state)
}
2023-06-30 19:41:35 +00:00
}
func GetCommunities(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
r.URL.Path = "/search"
2023-07-23 19:18:14 +00:00
if ps.ByName("host") != "" {
r.URL.Path = "/" + ps.ByName("host") + "/search"
}
r.URL.RawQuery = "searchtype=Communities&sort=TopMonth"
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, r.URL.String(), http.StatusFound)
}
2023-07-16 13:00:49 +00:00
func ResolveId(r *http.Request, class string, id string, host string) string {
remoteAddr := r.RemoteAddr
if r.Header.Get("CF-Connecting-IP") != "" {
remoteAddr = r.Header.Get("CF-Connecting-IP")
}
client := http.Client{Transport: NewAddHeaderTransport(remoteAddr)}
c, err := lemmy.NewWithClient("https://"+host, &client)
if err != nil {
return ""
}
2023-12-16 14:43:43 +00:00
idn, _ := strconv.ParseInt(id, 10, 64)
2023-07-16 13:00:49 +00:00
if class == "post" {
2023-12-16 14:43:43 +00:00
resp, err := c.Post(context.Background(), lemmy.GetPost{
ID: lemmy.NewOptional(idn),
2023-07-16 13:00:49 +00:00
})
if err != nil {
return ""
}
return resp.PostView.Post.ApID
}
2023-12-16 14:43:43 +00:00
resp, err := c.Comment(context.Background(), lemmy.GetComment{
2023-07-16 13:00:49 +00:00
ID: idn,
})
if err != nil {
return ""
}
return resp.CommentView.Comment.ApID
}
2023-06-30 19:41:35 +00:00
func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
2023-09-03 16:48:04 +00:00
// redirect /post/remote_id@instance to /post/local_id
2023-07-16 13:00:49 +00:00
if path := strings.Split(ps.ByName("postid"), "@"); len(path) > 1 {
apid := ResolveId(r, "post", path[0], path[1])
if apid != "" {
2023-12-16 14:43:43 +00:00
resp, err := state.Client.ResolveObject(context.Background(), lemmy.ResolveObject{
2023-07-16 13:00:49 +00:00
Q: apid,
})
if err != nil {
dest := apid
if os.Getenv("LEMMY_DOMAIN") == "" {
dest = RegReplace(dest, `https:\/\/([a-zA-Z0-9\.\-]+\/post\/\d+)`, `/$1`)
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, dest, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
}
post, _ := resp.Post.Value()
if post.Post.ID > 0 {
dest := RegReplace(r.URL.String(), `(([a-zA-Z0-9\.\-]+)?/post/)([a-zA-Z0-9\-\.@]+)`, `$1`)
2023-12-16 14:43:43 +00:00
dest += strconv.FormatInt(post.Post.ID, 10)
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, dest, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
} else {
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, apid, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
}
}
}
2023-06-30 19:41:35 +00:00
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["edit"]) > 0 {
state.Op = "edit_post"
state.GetSite()
}
if len(m["content"]) > 0 {
state.Content = m["content"][0]
}
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(ps.ByName("postid"), 10, 64)
2023-06-30 19:41:35 +00:00
state.GetPost(postid)
2023-09-03 16:48:04 +00:00
if ps.ByName("op") == "block" {
state.Op = "block"
Render(w, "block.html", state)
return
}
2023-06-30 19:41:35 +00:00
state.GetComments()
Render(w, "index.html", state)
}
func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
2023-07-16 13:00:49 +00:00
if path := strings.Split(ps.ByName("commentid"), "@"); len(path) > 1 {
apid := ResolveId(r, "comment", path[0], path[1])
if apid != "" {
2023-12-16 14:43:43 +00:00
resp, err := state.Client.ResolveObject(context.Background(), lemmy.ResolveObject{
2023-07-16 13:00:49 +00:00
Q: apid,
})
if err != nil {
dest := apid
if os.Getenv("LEMMY_DOMAIN") == "" {
dest = RegReplace(dest, `https:\/\/([a-zA-Z0-9\.\-]+\/comment\/\d+)`, `/$1`)
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, dest, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
}
comment, _ := resp.Comment.Value()
if comment.Comment.ID > 0 {
dest := RegReplace(r.URL.String(), `(([a-zA-Z0-9\.\-]+)?/comment/)([a-zA-Z0-9\-\.@]+)`, `$1`)
2023-12-16 14:43:43 +00:00
dest += strconv.FormatInt(comment.Comment.ID, 10)
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, dest, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
} else {
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, apid, http.StatusFound)
2023-07-16 13:00:49 +00:00
return
}
}
}
2023-06-30 19:41:35 +00:00
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["reply"]) > 0 {
state.Op = "reply"
}
if len(m["edit"]) > 0 {
state.Op = "edit"
}
if r.Method == "POST" && len(m["content"]) > 0 {
state.Content = m["content"][0]
}
2023-06-30 19:41:35 +00:00
if len(m["source"]) > 0 {
state.Op = "source"
}
2023-07-13 13:45:51 +00:00
if len(m["context"]) > 0 {
ctx, _ := strconv.Atoi(m["context"][0])
state.Context = ctx
}
2023-12-16 14:43:43 +00:00
commentid, _ := strconv.ParseInt(ps.ByName("commentid"), 10, 64)
2023-06-30 19:41:35 +00:00
state.GetComment(commentid)
if state.XHR && len(m["content"]) > 0 {
Render(w, "create_comment.html", state)
return
}
2023-06-30 19:41:35 +00:00
state.GetPost(state.PostID)
Render(w, "index.html", state)
}
func GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
2024-05-10 00:55:37 +00:00
state.Sort = "New"
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["sort"]) > 0 {
state.Sort = m["sort"][0]
}
2023-06-30 19:41:35 +00:00
if err != nil {
Render(w, "index.html", state)
return
}
state.GetUser(ps.ByName("username"))
2023-09-03 16:48:04 +00:00
if state.Site == nil {
state.GetSite()
}
2023-06-30 19:41:35 +00:00
Render(w, "index.html", state)
}
func GetMessageForm(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
state.Op = "send_message"
state.GetUser(ps.ByName("username"))
Render(w, "index.html", state)
}
func SendMessage(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
2023-12-16 14:43:43 +00:00
userid, _ := strconv.ParseInt(r.FormValue("userid"), 10, 64)
_, err = state.Client.CreatePrivateMessage(context.Background(), lemmy.CreatePrivateMessage{
2023-06-30 19:41:35 +00:00
Content: r.FormValue("content"),
RecipientID: userid,
})
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
r.URL.Path = "/" + state.Host + "/inbox"
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, r.URL.String(), http.StatusFound)
2023-06-30 19:41:35 +00:00
}
func GetCreatePost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["url"]) > 0 {
state.SubmitURL = m["url"][0]
}
if len(m["title"]) > 0 {
state.SubmitTitle = m["title"][0]
}
if len(m["body"]) > 0 {
state.SubmitBody = m["body"][0]
}
2023-06-30 19:41:35 +00:00
state.GetSite()
state.GetCommunity("")
state.Op = "create_post"
Render(w, "index.html", state)
}
func GetCreateCommunity(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
fmt.Println(err)
2023-06-30 19:41:35 +00:00
Render(w, "index.html", state)
return
}
state.GetSite()
state.Op = "create_community"
if ps.ByName("community") != "" {
state.GetCommunity(ps.ByName("community"))
state.Op = "edit_community"
}
2023-06-30 19:41:35 +00:00
Render(w, "index.html", state)
}
func Inbox(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
state.GetMessages()
Render(w, "index.html", state)
state.MarkAllAsRead()
}
func getCookie(r *http.Request, name string) string {
cookie, err := r.Cookie(name)
if err != nil {
return ""
}
return cookie.Value
}
2023-07-03 01:06:39 +00:00
func setCookie(w http.ResponseWriter, host string, name string, value string) {
if host == "." {
host = ""
}
cookie := http.Cookie{
2023-07-10 15:53:15 +00:00
Name: name,
Value: value,
MaxAge: 86400 * 30,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
Secure: true,
Path: "/" + host,
}
http.SetCookie(w, &cookie)
}
2023-07-03 01:06:39 +00:00
func deleteCookie(w http.ResponseWriter, host string, name string) {
if host == "." {
host = ""
}
cookie := http.Cookie{
Name: name,
2023-07-03 01:06:39 +00:00
Path: "/" + host,
MaxAge: -1,
}
http.SetCookie(w, &cookie)
}
func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
2023-07-28 16:51:17 +00:00
state.GetSite()
switch r.Method {
case "POST":
for _, name := range []string{"DefaultSortType", "DefaultListingType", "DefaultCommentSortType"} {
2023-07-07 16:27:44 +00:00
deleteCookie(w, state.Host, name)
setCookie(w, "", name, r.FormValue(name))
}
2024-05-27 16:41:13 +00:00
dark := true
switch r.FormValue("Dark") {
case "1":
2023-12-21 14:30:41 +00:00
setCookie(w, "", "Dark", "1")
2024-05-27 16:41:13 +00:00
state.Dark = &dark
case "0":
setCookie(w, "", "Dark", "0")
dark = false
state.Dark = &dark
default:
deleteCookie(w, "", "Dark")
state.Dark = nil
2023-07-03 00:11:13 +00:00
}
if r.FormValue("shownsfw") != "" {
2023-07-07 16:27:44 +00:00
setCookie(w, "", "ShowNSFW", "1")
state.ShowNSFW = true
} else {
deleteCookie(w, state.Host, "ShowNSFW")
2023-07-07 16:27:44 +00:00
deleteCookie(w, "", "ShowNSFW")
state.ShowNSFW = false
}
if r.FormValue("hideInstanceNames") != "" {
setCookie(w, "", "HideInstanceNames", "1")
state.HideInstanceNames = true
} else {
deleteCookie(w, "", "HideInstanceNames")
state.HideInstanceNames = false
}
if r.FormValue("hideThumbnails") != "" {
setCookie(w, "", "HideThumbnails", "1")
state.HideInstanceNames = true
} else {
setCookie(w, "", "HideThumbnails", "0")
state.HideInstanceNames = false
}
if r.FormValue("linksInNewWindow") != "" {
setCookie(w, "", "LinksInNewWindow", "1")
state.LinksInNewWindow = true
} else {
setCookie(w, "", "LinksInNewWindow", "0")
state.LinksInNewWindow = false
}
state.Listing = r.FormValue("DefaultListingType")
state.Sort = r.FormValue("DefaultSortType")
state.CommentSort = r.FormValue("DefaultCommentSortType")
// TODO save user settings
2024-05-14 23:46:33 +00:00
// TODO fetch user settings
//case "GET":
// if state.Session != nil {
// }
}
Render(w, "settings.html", state)
}
2023-06-30 19:41:35 +00:00
func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
2023-07-04 01:53:18 +00:00
fmt.Println(err)
2023-06-30 19:41:35 +00:00
Render(w, "index.html", state)
return
}
var token string
var username string
2023-06-30 19:41:35 +00:00
switch r.FormValue("submit") {
case "log in":
2023-12-16 14:43:43 +00:00
login := lemmy.Login{
2023-06-30 19:41:35 +00:00
UsernameOrEmail: r.FormValue("username"),
Password: r.FormValue("password"),
2023-07-04 01:53:18 +00:00
}
if r.FormValue("totp") != "" {
2023-12-16 14:43:43 +00:00
login.TOTP2FAToken = lemmy.NewOptional(r.FormValue("totp"))
2023-07-04 01:53:18 +00:00
}
resp, err := state.Client.Login(context.Background(), login)
2023-06-30 19:41:35 +00:00
if err != nil {
2023-07-04 01:53:18 +00:00
if strings.Contains(fmt.Sprintf("%v", err), "missing_totp_token") {
state.Op = "2fa"
}
fmt.Println(err)
2023-06-30 19:41:35 +00:00
state.Error = err
state.GetSite()
state.GetCaptcha()
Render(w, "login.html", state)
return
}
if resp.JWT.IsValid() {
token = resp.JWT.String()
username = r.FormValue("username")
deleteCookie(w, state.Host, "ShowNSFW")
2023-06-30 19:41:35 +00:00
}
case "sign up":
2023-12-16 14:43:43 +00:00
register := lemmy.Register{
2023-06-30 19:41:35 +00:00
Username: r.FormValue("username"),
Password: r.FormValue("password"),
PasswordVerify: r.FormValue("passwordverify"),
ShowNSFW: r.FormValue("nsfw") != "",
}
if r.FormValue("email") != "" {
2023-12-16 14:43:43 +00:00
register.Email = lemmy.NewOptional(r.FormValue("email"))
2023-06-30 19:41:35 +00:00
}
if r.FormValue("answer") != "" {
2023-12-16 14:43:43 +00:00
register.Answer = lemmy.NewOptional(r.FormValue("answer"))
2023-06-30 19:41:35 +00:00
}
if r.FormValue("captchauuid") != "" {
2023-12-16 14:43:43 +00:00
register.CaptchaUUID = lemmy.NewOptional(r.FormValue("captchauuid"))
2023-06-30 19:41:35 +00:00
}
if r.FormValue("captchaanswer") != "" {
2023-12-16 14:43:43 +00:00
register.CaptchaAnswer = lemmy.NewOptional(r.FormValue("captchaanswer"))
2023-06-30 19:41:35 +00:00
}
resp, err := state.Client.Register(context.Background(), register)
if err != nil {
state.Error = err
state.GetSite()
state.GetCaptcha()
Render(w, "login.html", state)
return
}
if resp.JWT.IsValid() {
username = r.FormValue("username")
2023-06-30 19:41:35 +00:00
token = resp.JWT.String()
} else {
var alert string
if resp.RegistrationCreated {
alert = "Registration application submitted. "
}
if resp.VerifyEmailSent {
alert = alert + "Email verification sent. "
}
q := r.URL.Query()
q.Add("alert", alert)
r.URL.RawQuery = q.Encode()
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, r.URL.String(), http.StatusFound)
2023-07-11 14:21:57 +00:00
return
2023-06-30 19:41:35 +00:00
}
}
if token != "" {
state.GetUser(username)
2023-07-11 14:21:57 +00:00
if state.User == nil {
return
}
2023-07-03 01:06:39 +00:00
setCookie(w, state.Host, "jwt", token)
2023-12-16 14:43:43 +00:00
userid := strconv.FormatInt(state.User.PersonView.Person.ID, 10)
2023-07-03 01:06:39 +00:00
setCookie(w, state.Host, "user", state.User.PersonView.Person.Name+":"+userid)
setCookie(w, state.Host, "jwt", token)
2023-06-30 19:41:35 +00:00
r.URL.Path = "/" + state.Host
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, r.URL.String(), http.StatusFound)
2023-06-30 19:41:35 +00:00
return
}
}
func GetLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
state.GetSite()
2023-07-11 14:21:57 +00:00
if state.Site != nil && state.Site.SiteView.LocalSite.CaptchaEnabled {
2023-07-01 02:42:45 +00:00
state.GetCaptcha()
}
2023-06-30 19:41:35 +00:00
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["alert"]) > 0 {
state.Alert = m["alert"][0]
}
Render(w, "login.html", state)
}
func Search(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
if state.CommunityName != "" {
2023-07-03 00:11:13 +00:00
state.GetCommunity(state.CommunityName)
2023-06-30 19:41:35 +00:00
}
if state.UserName != "" {
state.GetUser(state.UserName)
} else if state.Community == nil {
state.GetSite()
}
m, _ := url.ParseQuery(r.URL.RawQuery)
2024-04-27 19:22:47 +00:00
state.SearchType = "All"
2023-06-30 19:41:35 +00:00
if len(m["searchtype"]) > 0 {
switch m["searchtype"][0] {
2024-04-27 19:22:47 +00:00
case "Posts":
state.SearchType = "Posts"
2023-06-30 19:41:35 +00:00
case "Comments":
state.SearchType = "Comments"
case "Communities":
state.SearchType = "Communities"
2024-04-27 19:22:47 +00:00
case "Url":
state.SearchType = "Url"
2023-06-30 19:41:35 +00:00
}
2024-04-27 19:22:47 +00:00
2023-06-30 19:41:35 +00:00
}
state.Search(state.SearchType)
Render(w, "index.html", state)
}
type PictrsFile struct {
Filename string `json:"file"`
DeleteToken string `json:"delete_token"`
}
type PictrsResponse struct {
Message string `json:"msg"`
Files []PictrsFile `json:"files"`
}
func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
host := state.Host
if host == "." {
host = os.Getenv("LEMMY_DOMAIN")
}
2023-06-30 19:41:35 +00:00
switch r.FormValue("op") {
case "leave":
2023-12-16 14:43:43 +00:00
communityid, _ := strconv.ParseInt(r.FormValue("communityid"), 10, 64)
state.Client.FollowCommunity(context.Background(), lemmy.FollowCommunity{
2023-06-30 19:41:35 +00:00
CommunityID: communityid,
Follow: false,
})
case "join":
2023-12-16 14:43:43 +00:00
communityid, _ := strconv.ParseInt(r.FormValue("communityid"), 10, 64)
state.Client.FollowCommunity(context.Background(), lemmy.FollowCommunity{
2023-06-30 19:41:35 +00:00
CommunityID: communityid,
Follow: true,
})
case "block":
2023-12-16 14:43:43 +00:00
communityid, _ := strconv.ParseInt(r.FormValue("communityid"), 10, 64)
state.Client.BlockCommunity(context.Background(), lemmy.BlockCommunity{
CommunityID: communityid,
Block: true,
})
case "unblock":
2023-12-16 14:43:43 +00:00
communityid, _ := strconv.ParseInt(r.FormValue("communityid"), 10, 64)
state.Client.BlockCommunity(context.Background(), lemmy.BlockCommunity{
CommunityID: communityid,
Block: false,
})
2023-09-03 16:48:04 +00:00
case "block_user":
2023-12-16 14:43:43 +00:00
personId, _ := strconv.ParseInt(r.FormValue("user_id"), 10, 64)
2023-09-03 16:48:04 +00:00
if personId == 0 {
state.GetUser(ps.ByName("username"))
personId = state.User.PersonView.Person.ID
}
2023-12-16 14:43:43 +00:00
state.Client.BlockPerson(context.Background(), lemmy.BlockPerson{
2023-09-03 16:48:04 +00:00
PersonID: personId,
Block: r.FormValue("submit") == "block",
})
if r.FormValue("xhr") == "1" {
w.Write([]byte{})
return
}
2024-06-02 23:08:22 +00:00
case "sidetoggle":
if state.HideSidebar {
deleteCookie(w, state.Host, "HideSidebar")
} else {
setCookie(w, state.Host, "HideSidebar", "1")
}
if r.FormValue("xhr") != "" {
return
}
2023-06-30 19:41:35 +00:00
case "logout":
2023-07-03 01:06:39 +00:00
deleteCookie(w, state.Host, "jwt")
deleteCookie(w, state.Host, "user")
2023-06-30 19:41:35 +00:00
case "login":
2023-12-16 14:43:43 +00:00
login := lemmy.Login{
2023-07-04 01:53:18 +00:00
UsernameOrEmail: r.FormValue("username"),
Password: r.FormValue("password"),
}
if r.FormValue("totp") != "" {
2023-12-16 14:43:43 +00:00
login.TOTP2FAToken = lemmy.NewOptional(r.FormValue("totp"))
2023-07-04 01:53:18 +00:00
}
resp, err := state.Client.Login(context.Background(), login)
2023-06-30 19:41:35 +00:00
if err != nil {
2023-07-04 01:53:18 +00:00
if strings.Contains(fmt.Sprintf("%v", err), "missing_totp_token") {
state.Op = "2fa"
2023-07-23 19:05:31 +00:00
}
state.GetSite()
if state.Site != nil && state.Site.SiteView.LocalSite.CaptchaEnabled {
state.GetCaptcha()
2023-07-04 01:53:18 +00:00
}
2023-06-30 19:41:35 +00:00
state.Status = http.StatusUnauthorized
2023-07-23 19:05:31 +00:00
state.Error = err
Render(w, "login.html", state)
return
2023-07-04 01:53:18 +00:00
} else if resp.JWT.IsValid() {
state.GetUser(r.FormValue("username"))
if state.User != nil {
setCookie(w, state.Host, "jwt", resp.JWT.String())
2023-12-16 14:43:43 +00:00
userid := strconv.FormatInt(state.User.PersonView.Person.ID, 10)
2023-07-04 01:53:18 +00:00
setCookie(w, state.Host, "user", state.User.PersonView.Person.Name+":"+userid)
}
2023-06-30 19:41:35 +00:00
}
case "create_community":
state.GetSite()
2023-12-16 14:43:43 +00:00
community := lemmy.CreateCommunity{
2023-06-30 19:41:35 +00:00
Name: r.FormValue("name"),
Title: r.FormValue("title"),
}
if r.FormValue("description") != "" {
2023-12-16 14:43:43 +00:00
community.Description = lemmy.NewOptional(r.FormValue("description"))
2023-06-30 19:41:35 +00:00
}
if file, handler, err := r.FormFile("icon"); err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
community.Icon = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
if file, handler, err := r.FormFile("banner"); err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
community.Banner = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
resp, err := state.Client.CreateCommunity(context.Background(), community)
if err == nil {
r.URL.Path = "/" + state.Host + "/c/" + resp.CommunityView.Community.Name
} else {
fmt.Println(err)
}
case "edit_community":
state.CommunityName = ps.ByName("community")
state.GetCommunity("")
if state.Community == nil {
Render(w, "index.html", state)
return
}
state.GetSite()
2023-12-16 14:43:43 +00:00
community := lemmy.EditCommunity{
2023-06-30 19:41:35 +00:00
CommunityID: state.Community.CommunityView.Community.ID,
}
if r.FormValue("title") != "" {
2023-12-16 14:43:43 +00:00
community.Title = lemmy.NewOptional(r.FormValue("title"))
2023-06-30 19:41:35 +00:00
}
if r.FormValue("description") != "" {
2023-12-16 14:43:43 +00:00
community.Description = lemmy.NewOptional(r.FormValue("description"))
2023-06-30 19:41:35 +00:00
}
if file, handler, err := r.FormFile("icon"); err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
community.Icon = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
if file, handler, err := r.FormFile("banner"); err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
community.Banner = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
resp, err := state.Client.EditCommunity(context.Background(), community)
if err == nil {
r.URL.Path = "/" + state.Host + "/c/" + resp.CommunityView.Community.Name
} else {
fmt.Println(err)
}
case "create_post":
2023-07-04 02:43:16 +00:00
if state.CommunityName == "" {
state.CommunityName = r.FormValue("communityname")
}
2023-07-03 00:11:13 +00:00
state.GetCommunity(state.CommunityName)
2023-06-30 19:41:35 +00:00
state.GetSite()
if state.Community == nil {
state.Status = http.StatusBadRequest
state.Op = "create_post"
Render(w, "index.html", state)
return
}
2023-12-16 14:43:43 +00:00
post := lemmy.CreatePost{
2023-06-30 19:41:35 +00:00
Name: r.FormValue("name"),
CommunityID: state.Community.CommunityView.Community.ID,
}
if r.FormValue("url") != "" {
2023-12-16 14:43:43 +00:00
post.URL = lemmy.NewOptional(r.FormValue("url"))
2023-06-30 19:41:35 +00:00
}
file, handler, err := r.FormFile("file")
if err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
post.URL = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
if r.FormValue("body") != "" {
2023-12-16 14:43:43 +00:00
post.Body = lemmy.NewOptional(r.FormValue("body"))
2023-06-30 19:41:35 +00:00
}
if r.FormValue("language") != "" {
2023-12-16 14:43:43 +00:00
languageid, _ := strconv.ParseInt(r.FormValue("language"), 10, 64)
post.LanguageID = lemmy.NewOptional(languageid)
2023-06-30 19:41:35 +00:00
}
resp, err := state.Client.CreatePost(context.Background(), post)
if err == nil {
2023-12-16 14:43:43 +00:00
postid := strconv.FormatInt(resp.PostView.Post.ID, 10)
2023-06-30 19:41:35 +00:00
r.URL.Path = "/" + state.Host + "/post/" + postid
} else {
fmt.Println(err)
}
case "edit_post":
r.ParseMultipartForm(10 << 20)
state.GetSite()
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(ps.ByName("postid"), 10, 64)
post := lemmy.EditPost{
2023-06-30 19:41:35 +00:00
PostID: postid,
2023-12-16 14:43:43 +00:00
Body: lemmy.NewOptional(r.FormValue("body")),
Name: lemmy.NewOptional(r.FormValue("name")),
URL: lemmy.NewOptional(r.FormValue("url")),
2023-06-30 19:41:35 +00:00
}
if r.FormValue("url") == "" {
2023-12-16 14:43:43 +00:00
post.URL = lemmy.Optional[string]{}
2023-06-30 19:41:35 +00:00
}
if r.FormValue("language") != "" {
2023-12-16 14:43:43 +00:00
languageid, _ := strconv.ParseInt(r.FormValue("language"), 10, 64)
post.LanguageID = lemmy.NewOptional(languageid)
2023-06-30 19:41:35 +00:00
}
file, handler, err := r.FormFile("file")
if err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
post.URL = lemmy.NewOptional("https://" + host + "/pictrs/image/" + pres.Files[0].Filename)
2023-06-30 19:41:35 +00:00
}
resp, err := state.Client.EditPost(context.Background(), post)
if err == nil {
2023-12-16 14:43:43 +00:00
postid := strconv.FormatInt(resp.PostView.Post.ID, 10)
2023-06-30 19:41:35 +00:00
r.URL.Path = "/" + state.Host + "/post/" + postid
r.URL.RawQuery = ""
} else {
state.Status = http.StatusBadRequest
state.Error = err
fmt.Println(err)
}
2023-07-05 14:20:07 +00:00
case "save_post":
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(r.FormValue("postid"), 10, 64)
_, err := state.Client.SavePost(context.Background(), lemmy.SavePost{
2023-07-05 14:20:07 +00:00
PostID: postid,
Save: r.FormValue("submit") == "save",
})
if err != nil {
fmt.Println(err)
}
2023-07-12 15:23:13 +00:00
if r.FormValue("xhr") != "" {
state.GetPost(postid)
state.PostID = 0
state.Op = "save_post"
state.XHR = true
Render(w, "index.html", state)
return
}
2023-07-05 14:20:07 +00:00
case "save_comment":
2023-12-16 14:43:43 +00:00
commentid, _ := strconv.ParseInt(r.FormValue("commentid"), 10, 64)
_, err := state.Client.SaveComment(context.Background(), lemmy.SaveComment{
2023-07-05 14:20:07 +00:00
CommentID: commentid,
Save: r.FormValue("submit") == "save",
})
if err != nil {
fmt.Println(err)
}
if r.FormValue("xhr") != "" {
state.XHR = true
state.GetComment(commentid)
Render(w, "index.html", state)
return
}
2023-06-30 19:41:35 +00:00
case "delete_post":
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(r.FormValue("postid"), 10, 64)
post := lemmy.DeletePost{
2023-06-30 19:41:35 +00:00
PostID: postid,
Deleted: true,
}
if r.FormValue("undo") != "" {
post.Deleted = false
}
resp, err := state.Client.DeletePost(context.Background(), post)
if err != nil {
fmt.Println(err)
} else {
r.URL.Path = "/" + state.Host + "/c/" + resp.PostView.Community.Name
r.URL.RawQuery = ""
}
2023-09-03 16:48:04 +00:00
case "block_post":
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(r.FormValue("postid"), 10, 64)
2023-09-03 16:48:04 +00:00
state.GetPost(postid)
if r.FormValue("blockcommunity") != "" && len(state.Posts) > 0 {
2023-12-16 14:43:43 +00:00
state.Client.BlockCommunity(context.Background(), lemmy.BlockCommunity{
2023-09-03 16:48:04 +00:00
CommunityID: state.Posts[0].Post.CommunityID,
Block: true,
})
}
if r.FormValue("blockuser") != "" && len(state.Posts) > 0 {
2023-12-16 14:43:43 +00:00
state.Client.BlockPerson(context.Background(), lemmy.BlockPerson{
2023-09-03 16:48:04 +00:00
PersonID: state.Posts[0].Post.CreatorID,
Block: true,
})
}
2023-12-17 02:24:26 +00:00
if r.FormValue("blockpostinstance") != "" && len(state.Posts) > 0 {
instanceID, _ := strconv.ParseInt(r.FormValue("postinstanceid"), 10, 64)
state.Client.BlockInstance(context.Background(), lemmy.BlockInstance{
InstanceID: instanceID,
Block: true,
})
}
if r.FormValue("blockuserinstance") != "" && len(state.Posts) > 0 {
instanceID, _ := strconv.ParseInt(r.FormValue("userinstanceid"), 10, 64)
state.Client.BlockInstance(context.Background(), lemmy.BlockInstance{
InstanceID: instanceID,
Block: true,
})
}
2023-07-20 23:36:12 +00:00
case "read_post":
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(r.FormValue("postid"), 10, 64)
post := lemmy.MarkPostAsRead{
PostID: lemmy.NewOptional(postid),
2023-07-20 23:36:12 +00:00
Read: true,
}
if r.FormValue("submit") == "mark unread" {
post.Read = false
}
_, err := state.Client.MarkPostAsRead(context.Background(), post)
if err != nil {
fmt.Println(err)
} else if r.FormValue("xhr") != "" {
w.Write([]byte{})
return
}
2023-06-30 19:41:35 +00:00
case "vote_post":
2023-12-16 14:43:43 +00:00
var score int64
2023-06-30 19:41:35 +00:00
score = 1
if r.FormValue("vote") != "▲" {
score = -1
}
if r.FormValue("undo") == strconv.Itoa(int(score)) {
score = 0
}
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(r.FormValue("postid"), 10, 64)
post := lemmy.CreatePostLike{
2023-06-30 19:41:35 +00:00
PostID: postid,
Score: score,
}
2023-12-16 14:43:43 +00:00
state.Client.LikePost(context.Background(), post)
if r.FormValue("xhr") != "" {
state.GetPost(postid)
2023-07-05 22:34:46 +00:00
state.PostID = 0
state.Op = "vote_post"
state.XHR = true
Render(w, "index.html", state)
return
}
2023-06-30 19:41:35 +00:00
case "vote_comment":
2023-12-16 14:43:43 +00:00
var score int64
2023-06-30 19:41:35 +00:00
score = 1
2023-07-05 14:20:07 +00:00
if r.FormValue("submit") != "▲" {
2023-06-30 19:41:35 +00:00
score = -1
}
if r.FormValue("undo") == strconv.Itoa(int(score)) {
score = 0
}
2023-12-16 14:43:43 +00:00
commentid, _ := strconv.ParseInt(r.FormValue("commentid"), 10, 64)
post := lemmy.CreateCommentLike{
2023-06-30 19:41:35 +00:00
CommentID: commentid,
Score: score,
}
2023-12-16 14:43:43 +00:00
state.Client.LikeComment(context.Background(), post)
if r.FormValue("xhr") != "" {
state.XHR = true
if r.URL.Path == "/inbox" || r.URL.Path[:3] == "/u/" {
state.Query = "show context link"
state.GetSingleComment(commentid)
} else {
state.GetComment(commentid)
}
Render(w, "index.html", state)
return
}
2023-06-30 19:41:35 +00:00
case "create_comment":
if ps.ByName("postid") != "" {
2023-12-16 14:43:43 +00:00
postid, _ := strconv.ParseInt(ps.ByName("postid"), 10, 64)
2023-06-30 19:41:35 +00:00
state.PostID = postid
}
2023-10-04 17:31:49 +00:00
if r.FormValue("parentid") != "" && r.FormValue("parentid") != "0" {
2023-12-16 14:43:43 +00:00
parentid, _ := strconv.ParseInt(r.FormValue("parentid"), 10, 64)
2023-06-30 19:41:35 +00:00
state.GetComment(parentid)
}
content := r.FormValue("content")
file, handler, err := r.FormFile("file")
if err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
content += ("![](https://" + host + "/pictrs/image/" + pres.Files[0].Filename + ")")
}
if r.FormValue("submit") == "save" {
2023-12-16 14:43:43 +00:00
createComment := lemmy.CreateComment{
Content: content,
PostID: state.PostID,
}
if state.CommentID > 0 {
2023-12-16 14:43:43 +00:00
createComment.ParentID = lemmy.NewOptional(state.CommentID)
}
resp, err := state.Client.CreateComment(context.Background(), createComment)
if err == nil {
if r.FormValue("xhr") != "" {
state.XHR = true
state.Comments = nil
state.GetComment(resp.CommentView.Comment.ID)
Render(w, "index.html", state)
return
}
2023-12-16 14:43:43 +00:00
postid := strconv.FormatInt(state.PostID, 10)
commentid := strconv.FormatInt(resp.CommentView.Comment.ID, 10)
r.URL.Path = "/" + state.Host + "/post/" + postid
r.URL.Fragment = "c" + commentid
} else {
fmt.Println(err)
}
} else if r.FormValue("submit") == "preview" {
q := r.URL.Query()
q.Set("content", content)
q.Set("reply", "")
if r.FormValue("xhr") != "" {
q.Set("xhr", "1")
}
r.URL.RawQuery = q.Encode()
if ps.ByName("postid") != "" {
GetPost(w, r, ps)
return
}
if ps.ByName("commentid") != "" {
GetComment(w, r, ps)
return
}
} else if r.FormValue("xhr") != "" {
w.Write([]byte{})
return
2023-06-30 19:41:35 +00:00
}
if r.FormValue("submit") == "cancel" {
r.URL.RawQuery = ""
2023-06-30 19:41:35 +00:00
}
case "edit_comment":
2023-12-16 14:43:43 +00:00
commentid, _ := strconv.ParseInt(r.FormValue("commentid"), 10, 64)
q := r.URL.Query()
content := r.FormValue("content")
file, handler, err := r.FormFile("file")
if err == nil {
pres, err := state.UploadImage(file, handler)
if err != nil {
state.Error = err
Render(w, "index.html", state)
return
}
2024-02-02 03:54:22 +00:00
content += ("![](https://" + host + "/pictrs/image/" + pres.Files[0].Filename + ")")
}
if r.FormValue("submit") == "save" {
2023-12-16 14:43:43 +00:00
resp, err := state.Client.EditComment(context.Background(), lemmy.EditComment{
CommentID: commentid,
2023-12-16 14:43:43 +00:00
Content: lemmy.NewOptional(content),
})
if err != nil {
fmt.Println(err)
} else {
2023-12-16 14:43:43 +00:00
commentid := strconv.FormatInt(resp.CommentView.Comment.ID, 10)
r.URL.Fragment = "c" + commentid
r.URL.RawQuery = ""
}
} else if r.FormValue("submit") == "preview" {
q.Set("content", content)
q.Set("edit", "")
if r.FormValue("xhr") != "" {
q.Set("xhr", "1")
}
r.URL.RawQuery = q.Encode()
if ps.ByName("commentid") != "" {
GetComment(w, r, ps)
return
}
} else if r.FormValue("submit") == "cancel" {
if ps.ByName("commentid") != "" {
if r.FormValue("xhr") != "" {
q.Set("xhr", "1")
}
r.URL.RawQuery = q.Encode()
GetComment(w, r, ps)
return
}
} else if r.FormValue("xhr") != "" {
w.Write([]byte{})
return
}
if r.FormValue("xhr") != "" {
state.XHR = true
state.GetComment(commentid)
Render(w, "index.html", state)
return
}
if r.FormValue("submit") == "cancel" {
2023-06-30 19:41:35 +00:00
r.URL.RawQuery = ""
}
case "delete_comment":
2023-12-16 14:43:43 +00:00
commentid, _ := strconv.ParseInt(r.FormValue("commentid"), 10, 64)
resp, err := state.Client.DeleteComment(context.Background(), lemmy.DeleteComment{
2023-06-30 19:41:35 +00:00
CommentID: commentid,
Deleted: true,
})
if err != nil {
fmt.Println(err)
} else {
2023-10-04 17:31:49 +00:00
if r.FormValue("xhr") != "" {
state.XHR = true
state.Comments = append(state.Comments, Comment{P: resp.CommentView, State: &state})
state.CommentID = commentid
Render(w, "index.html", state)
return
}
2023-12-16 14:43:43 +00:00
commentid := strconv.FormatInt(resp.CommentView.Comment.ID, 10)
2023-06-30 19:41:35 +00:00
r.URL.Fragment = "c" + commentid
r.URL.RawQuery = ""
}
case "shownsfw":
if r.FormValue("submit") == "continue" {
2023-07-07 16:27:44 +00:00
setCookie(w, "", "ShowNSFW", "1")
} else {
r.URL.Path = "/" + state.Host
}
2023-06-30 19:41:35 +00:00
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, r.URL.String(), http.StatusFound)
2023-06-30 19:41:35 +00:00
}
func GetLink(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
2024-02-28 11:16:43 +00:00
ref, _ := url.Parse(r.Referer())
if r.Host != ref.Host {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Invalid Referer"))
return
}
var dest *url.URL
m, _ := url.ParseQuery(r.URL.RawQuery)
if len(m["url"]) > 0 {
dest, _ = url.Parse(m["url"][0])
}
if dest.Host == r.Host || !IsLemmy(dest.Host, RemoteAddr(r)) {
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, dest.String(), http.StatusFound)
return
}
if host := ps.ByName("host"); host != "" {
redirect := "/" + host + dest.Path
if host != dest.Host && !strings.Contains(redirect, "@") {
redirect += ("@" + dest.Host)
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, redirect, http.StatusFound)
return
}
if host := os.Getenv("LEMMY_DOMAIN"); host != "" {
redirect := dest.Path
if host != dest.Host && !strings.Contains(redirect, "@") {
redirect += ("@" + dest.Host)
}
2024-05-14 23:46:33 +00:00
http.Redirect(w, r, redirect, http.StatusFound)
return
}
}
func GetRouter() *httprouter.Router {
host := os.Getenv("LEMMY_DOMAIN")
router := httprouter.New()
if host == "" {
router.ServeFiles("/:host/static/*filepath", http.Dir("public"))
router.GET("/", GetRoot)
router.POST("/", PostRoot)
router.GET("/:host/", middleware(GetFrontpage))
router.GET("/:host/search", middleware(Search))
router.POST("/:host/search", middleware(UserOp))
router.GET("/:host/inbox", middleware(Inbox))
router.POST("/:host/inbox", middleware(UserOp))
router.GET("/:host/login", middleware(GetLogin))
router.POST("/:host/login", middleware(SignUpOrLogin))
router.GET("/:host/settings", middleware(Settings))
router.POST("/:host/settings", middleware(Settings))
router.POST("/:host/", middleware(UserOp))
router.GET("/:host/icon.jpg", middleware(GetIcon))
router.GET("/:host/c/:community", middleware(GetFrontpage))
router.POST("/:host/c/:community", middleware(UserOp))
router.GET("/:host/c/:community/search", middleware(Search))
router.GET("/:host/c/:community/edit", middleware(GetCreateCommunity))
router.GET("/:host/post/:postid", middleware(GetPost))
router.POST("/:host/post/:postid", middleware(UserOp))
2023-09-03 16:48:04 +00:00
router.GET("/:host/post/:postid/:op", middleware(GetPost))
router.GET("/:host/comment/:commentid", middleware(GetComment))
router.GET("/:host/comment/:commentid/:op", middleware(GetComment))
router.POST("/:host/comment/:commentid", middleware(UserOp))
router.GET("/:host/u/:username", middleware(GetUser))
router.GET("/:host/u/:username/message", middleware(GetMessageForm))
router.POST("/:host/u/:username/message", middleware(SendMessage))
router.POST("/:host/u/:username", middleware(UserOp))
router.GET("/:host/u/:username/search", middleware(Search))
router.GET("/:host/create_post", middleware(GetCreatePost))
router.POST("/:host/create_post", middleware(UserOp))
router.GET("/:host/create_community", middleware(GetCreateCommunity))
router.POST("/:host/create_community", middleware(UserOp))
router.GET("/:host/communities", middleware(GetCommunities))
2024-02-28 11:20:07 +00:00
//router.GET("/:host/link", middleware(GetLink))
} else {
router.ServeFiles("/_/static/*filepath", http.Dir("public"))
router.GET("/", middleware(GetFrontpage))
router.GET("/search", middleware(Search))
router.POST("/search", middleware(UserOp))
router.GET("/inbox", middleware(Inbox))
router.POST("/inbox", middleware(UserOp))
router.GET("/login", middleware(GetLogin))
router.POST("/login", middleware(SignUpOrLogin))
router.GET("/settings", middleware(Settings))
router.POST("/settings", middleware(Settings))
router.POST("/", middleware(UserOp))
router.GET("/icon.jpg", middleware(GetIcon))
router.GET("/c/:community", middleware(GetFrontpage))
router.POST("/c/:community", middleware(UserOp))
router.GET("/c/:community/search", middleware(Search))
router.GET("/c/:community/edit", middleware(GetCreateCommunity))
router.GET("/post/:postid", middleware(GetPost))
router.POST("/post/:postid", middleware(UserOp))
2024-04-17 00:00:57 +00:00
router.GET("/post/:postid/:op", middleware(GetPost))
router.GET("/comment/:commentid", middleware(GetComment))
router.GET("/comment/:commentid/:op", middleware(GetComment))
router.POST("/comment/:commentid", middleware(UserOp))
router.GET("/u/:username", middleware(GetUser))
router.GET("/u/:username/message", middleware(GetMessageForm))
router.POST("/u/:username/message", middleware(SendMessage))
router.POST("/u/:username", middleware(UserOp))
router.GET("/u/:username/search", middleware(Search))
router.GET("/create_post", middleware(GetCreatePost))
router.POST("/create_post", middleware(UserOp))
router.GET("/create_community", middleware(GetCreateCommunity))
router.POST("/create_community", middleware(UserOp))
router.GET("/communities", middleware(GetCommunities))
2024-02-28 11:20:07 +00:00
//router.GET("/link", middleware(GetLink))
}
return router
}