Merge pull request #2 from ProjectSegfault/announcements

initial announcements
This commit is contained in:
Odyssey346 2022-08-04 21:17:26 +02:00 committed by GitHub
commit 3d7f8235b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 223 additions and 24 deletions

101
api/announcements.go Normal file
View File

@ -0,0 +1,101 @@
package api
import (
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"time"
"github.com/goccy/go-json"
)
var (
authToken = os.Getenv("SEGFAUTILS_AUTHTOKEN")
)
func Announcements() {
http.HandleFunc("/api/announcements", getAnnouncements)
http.HandleFunc("/api/announcements/post", handleAnnouncements)
http.HandleFunc("/api/announcements/delete", handleAnnouncementDeleteRequest)
}
func handleAnnouncements(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if r.FormValue("token") != authToken {
http.Error(w, "You need to provide the authorization token given to you by your system administrator in order to post an announcement.", http.StatusUnauthorized)
return
} else {
if r.FormValue("title") == "" || r.FormValue("link") == "" || r.FormValue("severity") == "" {
http.Error(w, "Your request is not proper. Please add a title, link, and severity.", http.StatusBadRequest)
return
} else {
w.WriteHeader(http.StatusOK)
now := time.Now()
data := map[string]interface{}{
"title": r.FormValue("title"),
"link": r.FormValue("link"),
"severity": r.FormValue("severity"),
"created": now,
}
jsonData, err := json.Marshal(data)
if err != nil {
log.Printf("could not marshal json: %s\n", err)
return
}
ioutil.WriteFile("./static/announcements.json", jsonData, os.ModePerm)
w.Write([]byte("Announcement posted!"))
}
return
}
}
func handleAnnouncementDeleteRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if r.FormValue("token") != authToken {
http.Error(w, "You need to provide the authorization token given to you by your system administrator in order to delete an announcement.", http.StatusUnauthorized)
return
} else {
if _, err := os.Stat("./static/announcements.json"); errors.Is(err, os.ErrNotExist) {
http.Error(w, "If you're gonna delete the annoucement, there has to be an announcement in the first place.", http.StatusNotFound)
return
} else {
err := os.Remove("./static/announcements.json")
if err != nil {
log.Fatal(err)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Announcement deleted!"))
return
}
}
}
func getAnnouncements(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if _, err := os.Stat("./static/announcements.json"); errors.Is(err, os.ErrNotExist) {
http.Error(w, "There are no announcements.", http.StatusNotFound)
return
} else {
f, err := os.Open("./static/announcements.json")
if err != nil {
log.Fatal(err)
}
defer f.Close()
io.Copy(w, f)
}
}

View File

@ -12,7 +12,7 @@ import (
"io" "io"
"net/url" "net/url"
"github.com/ProjectSegfault/segfautils/otherthings" "github.com/ProjectSegfault/segfautils/utils"
) )
var ( var (
@ -34,7 +34,7 @@ func theActualFormCode(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, "Seems like captcha failed, you didn't complete the captcha or you are a bot. Please try again.\nPlease note that your IP has been logged in our systems for manual review to check if you're an abusive user. If you're seen as abusive, you will be blacklisted.\nYour message has not been sent.") fmt.Fprint(w, "Seems like captcha failed, you didn't complete the captcha or you are a bot. Please try again.\nPlease note that your IP has been logged in our systems for manual review to check if you're an abusive user. If you're seen as abusive, you will be blacklisted.\nYour message has not been sent.")
postData := url.Values{ postData := url.Values{
"content": {"IP " + otherthings.GetUserIP(r) + "failed captcha!\nhttps://abuseipdb.com/check/" + otherthings.GetUserIP(r)}, "content": {"IP " + utils.GetUserIP(r) + "failed captcha!\nhttps://abuseipdb.com/check/" + utils.GetUserIP(r)},
} }
req, err := http.PostForm(webhookURL, postData) req, err := http.PostForm(webhookURL, postData)
if err != nil { if err != nil {
@ -45,12 +45,12 @@ func theActualFormCode(w http.ResponseWriter, r *http.Request) {
} else { } else {
fmt.Fprintf(w, "Thanks for your message, and thanks for doing the captcha!\nPlease ignore how different this page looks to the page you were on earlier. I'll figure it out eventually!\n%#+v", hcaptchaResp) fmt.Fprintf(w, "Thanks for your message, and thanks for doing the captcha!\nPlease ignore how different this page looks to the page you were on earlier. I'll figure it out eventually!\n%#+v", hcaptchaResp)
postData := url.Values{ postData := url.Values{
"content": {"IP " + otherthings.GetUserIP(r) + "\nFrom " + r.FormValue("email") + " with feedback type " + r.FormValue("commentType") + ":\n" + "**" + r.FormValue("message") + "**\n https://abuseipdb.com/check/" + otherthings.GetUserIP(r)}, "content": {"IP " + utils.GetUserIP(r) + "\nFrom " + r.FormValue("email") + " with feedback type " + r.FormValue("commentType") + ":\n" + "**" + r.FormValue("message") + "**\n https://abuseipdb.com/check/" + utils.GetUserIP(r)},
} }
if r.FormValue("webhook") != "" { if r.FormValue("webhook") != "" {
fmt.Fprintf(w, "\nThanks for trying Segfautils Contact Form :)") fmt.Fprintf(w, "\nThanks for trying Segfautils Contact Form :)")
postData := url.Values{ postData := url.Values{
"content": {"**Note: you are currently testing our form example. Please check out the actual project at https://github.com/ProjectSegfault/segfautils if you found this neat! It's not hard to self-host :)**\n" + "IP " + otherthings.GetUserIP(r) + "\nFrom " + r.FormValue("email") + " with feedback type " + r.FormValue("commentType") + ":\n" + "**" + r.FormValue("message") + "**\n https://abuseipdb.com/check/" + otherthings.GetUserIP(r)}, "content": {"**Note: you are currently testing our form example. Please check out the actual project at https://github.com/ProjectSegfault/segfautils if you found this neat! It's not hard to self-host :)**\n" + "IP " + utils.GetUserIP(r) + "\nFrom " + r.FormValue("email") + " with feedback type " + r.FormValue("commentType") + ":\n" + "**" + r.FormValue("message") + "**\n https://abuseipdb.com/check/" + utils.GetUserIP(r)},
} }
req, err := http.PostForm(r.FormValue("webhook"), postData) req, err := http.PostForm(r.FormValue("webhook"), postData)
if err != nil { if err != nil {
@ -68,5 +68,5 @@ func theActualFormCode(w http.ResponseWriter, r *http.Request) {
default: default:
http.Error(w, "Method isn't allowed!\nYou may only POST here, not "+r.Method, http.StatusMethodNotAllowed) http.Error(w, "Method isn't allowed!\nYou may only POST here, not "+r.Method, http.StatusMethodNotAllowed)
} }
log.Println("[HTTP] " + otherthings.GetUserIP(r) + " accessed /api/form with method " + r.Method) log.Println("[HTTP] " + utils.GetUserIP(r) + " accessed /api/form with method " + r.Method)
} }

2
go.mod
View File

@ -3,5 +3,7 @@ module github.com/ProjectSegfault/segfautils
go 1.18 go 1.18
require ( require (
github.com/goccy/go-json v0.9.10 // indirect
github.com/gorilla/feeds v1.1.1 // indirect
github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f // indirect github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f // indirect
) )

4
go.sum
View File

@ -1,2 +1,6 @@
github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f h1:ktcW6v946XnYiNU2taSVx79C5eDDQ8MxWepJ8S1Mz5A= github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f h1:ktcW6v946XnYiNU2taSVx79C5eDDQ8MxWepJ8S1Mz5A=
github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f/go.mod h1:9FC7gVUVZcXkyq6vFY+JVGMrmw1xoe4nD41Whc+gSbo= github.com/kataras/hcaptcha v0.0.0-20200711031247-2927d4faf32f/go.mod h1:9FC7gVUVZcXkyq6vFY+JVGMrmw1xoe4nD41Whc+gSbo=

View File

@ -8,7 +8,7 @@ import (
"os" "os"
"github.com/ProjectSegfault/segfautils/api" "github.com/ProjectSegfault/segfautils/api"
"github.com/ProjectSegfault/segfautils/otherthings" "github.com/ProjectSegfault/segfautils/utils"
) )
type StaticThingy struct { type StaticThingy struct {
@ -21,7 +21,7 @@ var shit bool
func main() { func main() {
log.Println("[Segfautils] Starting") log.Println("[Segfautils] Starting")
otherthings.CheckEnv() utils.CheckEnv()
log.Println("[HTTP] Starting server") log.Println("[HTTP] Starting server")
port := os.Getenv("SEGFAUTILS_PORT") port := os.Getenv("SEGFAUTILS_PORT")
hcaptcha_site_key := os.Getenv("HCAPTCHA_SITE_KEY") hcaptcha_site_key := os.Getenv("HCAPTCHA_SITE_KEY")
@ -44,7 +44,11 @@ func main() {
http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "welcome to hell") io.WriteString(w, "welcome to hell")
}) })
http.HandleFunc("/announcements", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/announcements.html")
})
api.Form() api.Form()
api.Announcements()
log.Println("[HTTP] HTTP server is now running at " + port + "!") log.Println("[HTTP] HTTP server is now running at " + port + "!")
log.Println(http.ListenAndServe(":"+port, nil)) log.Println(http.ListenAndServe(":"+port, nil))
} }

View File

@ -1,15 +0,0 @@
package otherthings
import (
"net/http"
)
// Thanks random StackOverflow answerer
func GetUserIP(r *http.Request) string {
IPAddress := r.Header.Get("X-REAL-IP")
if IPAddress == "" {
IPAddress = r.Header.Get("X-FORWARDED-FOR")
}
return IPAddress
}

82
static/announcements.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Segfautils announcement command centre</title>
<style>
body {
background-color: #252525;
color: #fff;
font-family: 'JetBrains Mono', 'JetBrainsMono Nerd Font', monospace
}
a {
color: #00d4aa;
}
a:hover {
color: #4beacb;
}
</style>
</head>
<body>
<h1>Post Announcement</h1>
<form
action="/api/announcements/post"
method="POST"
target="_blank"
>
<div class="meta">
<input
type="password"
name="token"
placeholder="Your authentication token"
required
/>
<br />
<select id="severity" name="severity" required>
<option value="" selected disabled>Select severity of announcement</option>
<option value="info">Information announcement</option>
<option value="low">Low severity</option>
<option value="medium">Medium severity</option>
<option value="high">High severity</option>
</select>
</div>
<textarea
id="title"
name="title"
rows="4"
cols="25"
required
placeholder="The announcement text"></textarea>
<br />
<input
type="text"
name="link"
placeholder="Your link for more details"
required
/>
<br />
<input type="submit" value="Submit" />
</form>
<h1>Delete Announcement</h1>
<form
action="/api/announcements/delete"
method="POST"
target="_blank"
>
<div class="meta">
<input
type="password"
name="token"
placeholder="Your authentication token"
required
/>
<br />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>

View File

@ -1,5 +1,5 @@
// Please ignore my terrible code :) It works // Please ignore my terrible code :) It works
package otherthings package utils
import ( import (
"log" "log"
@ -20,7 +20,7 @@ func CheckEnv() {
} }
unused, ok1 = os.LookupEnv("HCAPTCHA_SITE_KEY") unused, ok1 = os.LookupEnv("HCAPTCHA_SITE_KEY")
if !ok1 || unused == "YOURSITEKEY" { if !ok1 || unused == "YOURSITEKEY" {
log.Fatal("[Segfautils] Environment variable HCAPTCHA_SITE_KEY is not set! Please set it to the site key you got from hCaptcha.") log.Println("[Segfautils] Environment variable HCAPTCHA_SITE_KEY is not not set. It isn't required to be set, but without it, the example form will not work.")
} else { } else {
log.Println("[Segfautils] Environment variable HCAPTCHA_SITE_KEY is set as " + unused) log.Println("[Segfautils] Environment variable HCAPTCHA_SITE_KEY is set as " + unused)
} }
@ -36,5 +36,11 @@ func CheckEnv() {
} else { } else {
log.Println("[Segfautils] Environment variable SEGFAUTILS_WEBHOOK_URL is set!") log.Println("[Segfautils] Environment variable SEGFAUTILS_WEBHOOK_URL is set!")
} }
unused, ok1 = os.LookupEnv("SEGFAUTILS_AUTHTOKEN")
if !ok1 || unused == "YOURAUTHTOKEN" {
log.Fatal("[Segfautils] Environment variable SEGFAUTILS_AUTHTOKEN is not set! Please set it to a token you'd like to use for authorizing actions like announcements.")
} else {
log.Println("[Segfautils] Environment variable SEGFAUTILS_AUTHTOKEN is set!")
}
log.Println("[Segfautils] ✅ Passed the Environment Variables check") log.Println("[Segfautils] ✅ Passed the Environment Variables check")
} }

15
utils/getip.go Normal file
View File

@ -0,0 +1,15 @@
package utils
import (
"net/http"
)
// Thanks random StackOverflow answerer
func GetUserIP(r *http.Request) string {
IPAddress := r.Header.Get("X-REAL-IP")
if IPAddress == "" {
IPAddress = r.Header.Get("X-FORWARDED-FOR")
}
return IPAddress
}