(Null)
This commit is contained in:
parent
aa2d04368e
commit
03c3c58811
4
.gitignore
vendored
4
.gitignore
vendored
@ -28,6 +28,10 @@ go.work.sum
|
|||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
#sensitive config file and cred file
|
||||||
|
config.json
|
||||||
|
accounts.txt
|
||||||
|
ircserver
|
||||||
# Editor/IDE
|
# Editor/IDE
|
||||||
# .idea/
|
# .idea/
|
||||||
# .vscode/
|
# .vscode/
|
||||||
|
|||||||
190
http.go
190
http.go
@ -1,19 +1,201 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ModeResponse struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
Channel string `json:"channel"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
type KickResponse struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
Channel string `json:"channel"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
func startHTTP(cfg Config) {
|
func startHTTP(cfg Config) {
|
||||||
htport := cfg.Webapiport
|
htport := cfg.Webapiport
|
||||||
http.HandleFunc("/admin/", handleAdmin)
|
http.HandleFunc("/admin/kick", handleKick)
|
||||||
|
http.HandleFunc("/admin/mode", handleModeAPI)
|
||||||
|
http.HandleFunc("/admin/list", handleListAPI)
|
||||||
|
http.HandleFunc("/admin/reloadmotd", handleMOTDreload)
|
||||||
fmt.Printf("[web] starting API on port %d\n", htport)
|
fmt.Printf("[web] starting API on port %d\n", htport)
|
||||||
http.ListenAndServe(fmt.Sprintf(":%d", htport), nil)
|
http.ListenAndServe(fmt.Sprintf(":%d", htport), nil)
|
||||||
}
|
}
|
||||||
func handleAdmin(w http.ResponseWriter, r *http.Request) {
|
func handleKick(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.URL.Path {
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if !ok || !checkCredentials(user, pass) || !globalOps[user] {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "POST required", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channelName := normalizeChannelName(r.FormValue("channel"))
|
||||||
|
targetNick := r.FormValue("nick")
|
||||||
|
reason := r.FormValue("reason")
|
||||||
|
if reason == "" {
|
||||||
|
reason = "Kicked via API"
|
||||||
|
}
|
||||||
|
|
||||||
|
chMu.Lock()
|
||||||
|
ch, ok := channels[channelName]
|
||||||
|
chMu.Unlock()
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "No such channel", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.mu.Lock()
|
||||||
|
defer ch.mu.Unlock()
|
||||||
|
|
||||||
|
target, ok := ch.members[targetNick]
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Target not in channel", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if globalOps[targetNick] {
|
||||||
|
http.Error(w, "Cannot kick global operator", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, member := range ch.members {
|
||||||
|
member.fwrite(":server KICK %s %s :%s\r\n", channelName, targetNick, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove target
|
||||||
|
delete(ch.members, targetNick)
|
||||||
|
delete(target.channels, channelName)
|
||||||
|
|
||||||
|
resp := KickResponse{
|
||||||
|
Action: "kick",
|
||||||
|
Target: targetNick,
|
||||||
|
Channel: channelName,
|
||||||
|
Reason: reason,
|
||||||
|
Status: "ok",
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(resp)
|
||||||
|
}
|
||||||
|
func handleModeAPI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if !ok || !checkCredentials(user, pass) || !globalOps[user] {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "POST required", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channelName := normalizeChannelName(r.FormValue("channel"))
|
||||||
|
mode := r.FormValue("mode")
|
||||||
|
targetNick := r.FormValue("nick")
|
||||||
|
|
||||||
|
chMu.Lock()
|
||||||
|
ch, ok := channels[channelName]
|
||||||
|
chMu.Unlock()
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "No such channel", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.mu.Lock()
|
||||||
|
defer ch.mu.Unlock()
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case "+o":
|
||||||
|
if targetNick != "" {
|
||||||
|
ch.ops[targetNick] = true
|
||||||
|
for _, member := range ch.members {
|
||||||
|
member.fwrite(":api MODE %s +o %s\r\n", channelName, targetNick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "-o":
|
||||||
|
if targetNick != "" {
|
||||||
|
if globalOps[targetNick] {
|
||||||
|
http.Error(w, "Cannot remove global operator", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(ch.ops, targetNick)
|
||||||
|
for _, member := range ch.members {
|
||||||
|
member.fwrite(":api MODE %s -o %s\r\n", channelName, targetNick)
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
http.NotFound(w, r)
|
http.Error(w, "Unknown MODE flag", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := ModeResponse{
|
||||||
|
Action: "mode",
|
||||||
|
Mode: mode,
|
||||||
|
Target: targetNick,
|
||||||
|
Channel: channelName,
|
||||||
|
Status: "ok",
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Users int `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleListAPI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if !ok || !checkCredentials(user, pass) || !globalOps[user] {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chMu.Lock()
|
||||||
|
defer chMu.Unlock()
|
||||||
|
|
||||||
|
var list []ChannelInfo
|
||||||
|
for _, ch := range channels {
|
||||||
|
ch.mu.Lock()
|
||||||
|
count := len(ch.members)
|
||||||
|
desc := ch.description
|
||||||
|
name := ch.name
|
||||||
|
ch.mu.Unlock()
|
||||||
|
|
||||||
|
list = append(list, ChannelInfo{
|
||||||
|
Name: name,
|
||||||
|
Description: desc,
|
||||||
|
Users: count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(list); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func handleMOTDreload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if !ok || !checkCredentials(user, pass) || !globalOps[user] {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadMOTD(motdFile)
|
||||||
|
type htresp struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
resp := htresp{
|
||||||
|
Status: "ok",
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
main.go
6
main.go
@ -64,11 +64,11 @@ func loadConfig() (*Config, error) {
|
|||||||
var conf Config
|
var conf Config
|
||||||
jsdata, err := os.ReadFile("config.json")
|
jsdata, err := os.ReadFile("config.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(jsdata, &conf)
|
err = json.Unmarshal(jsdata, &conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, cfg := range conf.Channels {
|
for _, cfg := range conf.Channels {
|
||||||
addChannel(cfg.Channel, cfg.Description)
|
addChannel(cfg.Channel, cfg.Description)
|
||||||
@ -78,7 +78,7 @@ func loadConfig() (*Config, error) {
|
|||||||
globalOps[cleanedNick] = true
|
globalOps[cleanedNick] = true
|
||||||
}
|
}
|
||||||
motdFile = conf.Motd
|
motdFile = conf.Motd
|
||||||
return conf.Port, nil
|
return &conf, nil
|
||||||
}
|
}
|
||||||
func loadMOTD(filename string) error {
|
func loadMOTD(filename string) error {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user