2017-03-27 14:15:02 +02:00
|
|
|
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
|
2016-11-06 02:05:29 +01:00
|
|
|
// released under the MIT license
|
|
|
|
|
|
|
|
// viewing and modifying accounts, registered channels, dlines, rehashing, etc
|
|
|
|
|
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
2016-11-06 04:47:13 +01:00
|
|
|
"strconv"
|
2016-11-06 02:31:27 +01:00
|
|
|
"time"
|
2016-11-06 02:05:29 +01:00
|
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
2016-11-06 04:47:13 +01:00
|
|
|
"github.com/tidwall/buntdb"
|
2016-11-06 02:05:29 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
const restErr = "{\"error\":\"An unknown error occurred\"}"
|
|
|
|
|
|
|
|
// restAPIServer is used to keep a link to the current running server since this is the best
|
|
|
|
// way to do it, given how HTTP handlers dispatch and work.
|
|
|
|
var restAPIServer *Server
|
|
|
|
|
2016-11-06 04:47:13 +01:00
|
|
|
type restInfoResp struct {
|
|
|
|
ServerName string `json:"server-name"`
|
|
|
|
NetworkName string `json:"network-name"`
|
|
|
|
|
2016-11-06 02:59:10 +01:00
|
|
|
Version string `json:"version"`
|
|
|
|
}
|
|
|
|
|
2016-11-06 02:05:29 +01:00
|
|
|
type restStatusResp struct {
|
|
|
|
Clients int `json:"clients"`
|
|
|
|
Opers int `json:"opers"`
|
|
|
|
Channels int `json:"channels"`
|
|
|
|
}
|
|
|
|
|
2017-03-07 10:55:14 +01:00
|
|
|
type restXLinesResp struct {
|
2016-11-06 02:05:29 +01:00
|
|
|
DLines map[string]IPBanInfo `json:"dlines"`
|
2017-03-07 10:55:14 +01:00
|
|
|
KLines map[string]IPBanInfo `json:"klines"`
|
2016-11-06 02:05:29 +01:00
|
|
|
}
|
|
|
|
|
2016-11-06 02:31:27 +01:00
|
|
|
type restAcct struct {
|
2016-11-06 04:47:13 +01:00
|
|
|
Name string `json:"name"`
|
2016-11-06 02:31:27 +01:00
|
|
|
RegisteredAt time.Time `json:"registered-at"`
|
2016-11-06 04:47:13 +01:00
|
|
|
Clients int `json:"clients"`
|
2016-11-06 02:31:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type restAccountsResp struct {
|
2016-11-06 04:47:13 +01:00
|
|
|
Verified map[string]restAcct `json:"verified"`
|
2016-11-06 02:59:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type restRehashResp struct {
|
|
|
|
Successful bool `json:"successful"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
Time time.Time `json:"time"`
|
|
|
|
}
|
|
|
|
|
2016-11-06 04:47:13 +01:00
|
|
|
func restInfo(w http.ResponseWriter, r *http.Request) {
|
|
|
|
rs := restInfoResp{
|
|
|
|
Version: SemVer,
|
|
|
|
ServerName: restAPIServer.name,
|
|
|
|
NetworkName: restAPIServer.networkName,
|
2016-11-06 02:59:10 +01:00
|
|
|
}
|
|
|
|
b, err := json.Marshal(rs)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(w, restErr)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, string(b))
|
|
|
|
}
|
2016-11-06 02:31:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-06 02:05:29 +01:00
|
|
|
func restStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
rs := restStatusResp{
|
|
|
|
Clients: restAPIServer.clients.Count(),
|
|
|
|
Opers: len(restAPIServer.operators),
|
2017-04-17 13:01:39 +02:00
|
|
|
Channels: restAPIServer.channels.Len(),
|
2016-11-06 02:05:29 +01:00
|
|
|
}
|
|
|
|
b, err := json.Marshal(rs)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(w, restErr)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-07 10:55:14 +01:00
|
|
|
func restGetXLines(w http.ResponseWriter, r *http.Request) {
|
|
|
|
rs := restXLinesResp{
|
2016-11-06 02:05:29 +01:00
|
|
|
DLines: restAPIServer.dlines.AllBans(),
|
2017-03-07 10:55:14 +01:00
|
|
|
KLines: restAPIServer.klines.AllBans(),
|
2016-11-06 02:05:29 +01:00
|
|
|
}
|
|
|
|
b, err := json.Marshal(rs)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(w, restErr)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-06 02:59:10 +01:00
|
|
|
func restGetAccounts(w http.ResponseWriter, r *http.Request) {
|
2016-11-06 02:31:27 +01:00
|
|
|
rs := restAccountsResp{
|
2016-11-06 04:47:13 +01:00
|
|
|
Verified: make(map[string]restAcct),
|
2016-11-06 02:31:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-06 04:47:13 +01:00
|
|
|
// get accounts
|
|
|
|
err := restAPIServer.store.View(func(tx *buntdb.Tx) error {
|
|
|
|
tx.AscendKeys("account.exists *", func(key, value string) bool {
|
|
|
|
key = key[len("account.exists "):]
|
|
|
|
_, err := tx.Get(fmt.Sprintf(keyAccountVerified, key))
|
|
|
|
verified := err == nil
|
|
|
|
fmt.Println(fmt.Sprintf(keyAccountVerified, key))
|
|
|
|
|
|
|
|
// get other details
|
|
|
|
name, _ := tx.Get(fmt.Sprintf(keyAccountName, key))
|
|
|
|
regTimeStr, _ := tx.Get(fmt.Sprintf(keyAccountRegTime, key))
|
|
|
|
regTimeInt, _ := strconv.ParseInt(regTimeStr, 10, 64)
|
|
|
|
regTime := time.Unix(regTimeInt, 0)
|
|
|
|
|
|
|
|
var clients int
|
|
|
|
acct := restAPIServer.accounts[key]
|
|
|
|
if acct != nil {
|
|
|
|
clients = len(acct.Clients)
|
|
|
|
}
|
|
|
|
|
|
|
|
if verified {
|
|
|
|
rs.Verified[key] = restAcct{
|
|
|
|
Name: name,
|
|
|
|
RegisteredAt: regTime,
|
|
|
|
Clients: clients,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//TODO(dan): Add to unverified list
|
|
|
|
}
|
|
|
|
|
|
|
|
return true // true to continue I guess?
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
2016-11-06 02:31:27 +01:00
|
|
|
|
|
|
|
b, err := json.Marshal(rs)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(w, restErr)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-06 02:59:10 +01:00
|
|
|
func restRehash(w http.ResponseWriter, r *http.Request) {
|
|
|
|
err := restAPIServer.rehash()
|
|
|
|
|
|
|
|
rs := restRehashResp{
|
|
|
|
Successful: err == nil,
|
|
|
|
Time: time.Now(),
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
rs.Error = err.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := json.Marshal(rs)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(w, restErr)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-06 02:05:29 +01:00
|
|
|
func (s *Server) startRestAPI() {
|
|
|
|
// so handlers can ref it later
|
|
|
|
restAPIServer = s
|
|
|
|
|
|
|
|
// start router
|
|
|
|
r := mux.NewRouter()
|
2016-11-06 02:59:10 +01:00
|
|
|
|
|
|
|
// GET methods
|
|
|
|
rg := r.Methods("GET").Subrouter()
|
2016-11-06 04:47:13 +01:00
|
|
|
rg.HandleFunc("/info", restInfo)
|
2016-11-06 02:59:10 +01:00
|
|
|
rg.HandleFunc("/status", restStatus)
|
2017-03-07 10:55:14 +01:00
|
|
|
rg.HandleFunc("/xlines", restGetXLines)
|
2016-11-06 02:59:10 +01:00
|
|
|
rg.HandleFunc("/accounts", restGetAccounts)
|
|
|
|
|
|
|
|
// PUT methods
|
|
|
|
rp := r.Methods("POST").Subrouter()
|
|
|
|
rp.HandleFunc("/rehash", restRehash)
|
2016-11-06 02:05:29 +01:00
|
|
|
|
|
|
|
// start api
|
|
|
|
go http.ListenAndServe(s.restAPI.Listen, r)
|
|
|
|
}
|