lotus/api/api.go

148 lines
4.2 KiB
Go

package api
import (
"encoding/json"
"github.com/1f349/lotus/imap"
"github.com/1f349/lotus/smtp"
"github.com/julienschmidt/httprouter"
"net/http"
"time"
)
func SetupApiServer(listen string, auth func(callback AuthCallback) httprouter.Handle, send *smtp.Smtp, recv *imap.Imap) *http.Server {
r := httprouter.New()
// === ACCOUNT ===
r.GET("/account", auth(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
// TODO(melon): find users aliases and other account data
}))
// === SMTP ===
r.POST("/message", auth(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
// check body exists
if req.Body == nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
// parse json body
var j smtp.Json
err := json.NewDecoder(req.Body).Decode(&j)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
// TODO(melon): add alias support
if j.From == b.Subject {
}
mail, err := j.PrepareMail()
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
if send.Send(mail) != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
rw.WriteHeader(http.StatusAccepted)
}))
// === IMAP ===
type statusJson struct {
Folder string `json:"folder"`
}
r.GET("/status", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
status, err := cli.Status(t.Folder)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(status)
})))
r.GET("/list-messages", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
messages, err := cli.Fetch(t.Folder, 1, 100, 100)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(messages)
})))
r.GET("/search-messages", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
status, err := cli.Status(t.Folder)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(status)
})))
r.POST("/create-message", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
status, err := cli.Status(t.Folder)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(status)
})))
r.POST("/update-messages-flags", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
status, err := cli.Status(t.Folder)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(status)
})))
r.POST("/copy-messages", auth(imapClient(recv, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t statusJson) {
status, err := cli.Status(t.Folder)
if err != nil {
rw.WriteHeader(http.StatusForbidden)
return
}
_ = json.NewEncoder(rw).Encode(status)
})))
return &http.Server{
Addr: listen,
Handler: r,
ReadTimeout: time.Minute,
ReadHeaderTimeout: time.Minute,
WriteTimeout: time.Minute,
IdleTimeout: time.Minute,
MaxHeaderBytes: 2500,
}
}
// apiError outputs a generic JSON error message
func apiError(rw http.ResponseWriter, code int, m string) {
rw.WriteHeader(code)
_ = json.NewEncoder(rw).Encode(map[string]string{
"error": m,
})
}
type IcCallback[T any] func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, cli *imap.Client, t T)
func imapClient[T any](recv *imap.Imap, cb IcCallback[T]) AuthCallback {
return func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
if req.Body == nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
var t T
if json.NewDecoder(req.Body).Decode(&t) != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
cli, err := recv.MakeClient(b.Subject)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
cb(rw, req, params, cli, t)
}
}