basic API handler
parent
5be9e89f52
commit
e115492f0d
@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"faculty_media_report/dbi"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type jsonRPCRequest struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
ID *int `json:"id"`
|
||||
}
|
||||
|
||||
type jsonRPCErrorBody struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type jsonRPCResponse struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
Error *jsonRPCErrorBody `json:"error,omitempty"`
|
||||
ID *int `json:"id"`
|
||||
}
|
||||
|
||||
func writeJSONRPC(w http.ResponseWriter, resp jsonRPCResponse) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
func jsonRPCErr(w http.ResponseWriter, code int, message string, id *int) {
|
||||
writeJSONRPC(w, jsonRPCResponse{
|
||||
Jsonrpc: "2.0",
|
||||
Error: &jsonRPCErrorBody{Code: code, Message: message},
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
func handleAPI(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
db, err := dbi.GetDbConn()
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var user dbi.User
|
||||
token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
|
||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
|
||||
}
|
||||
sub, err := t.Claims.GetSubject()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("missing sub claim")
|
||||
}
|
||||
user, err = dbi.GetUser(conn, sub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
return []byte(user.APIKey), nil
|
||||
}, jwt.WithExpirationRequired())
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
if err != nil {
|
||||
http.Error(w, "unauthorized: "+err.Error(), http.StatusUnauthorized)
|
||||
} else {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
http.Error(w, "invalid token claims", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
iat, err := claims.GetIssuedAt()
|
||||
if err != nil || iat == nil {
|
||||
http.Error(w, "missing iat claim", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if iat.After(time.Now()) {
|
||||
http.Error(w, "iat is in the future", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var req jsonRPCRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
jsonRPCErr(w, -32700, "Parse error", nil)
|
||||
return
|
||||
}
|
||||
if req.Jsonrpc != "2.0" {
|
||||
jsonRPCErr(w, -32600, `Invalid Request: jsonrpc must be "2.0"`, req.ID)
|
||||
return
|
||||
}
|
||||
if req.Method == "" {
|
||||
jsonRPCErr(w, -32600, "Invalid Request: method is required", req.ID)
|
||||
return
|
||||
}
|
||||
if req.Params == nil {
|
||||
jsonRPCErr(w, -32600, "Invalid Request: params is required", req.ID)
|
||||
return
|
||||
}
|
||||
if req.ID == nil {
|
||||
jsonRPCErr(w, -32600, "Invalid Request: id is required", req.ID)
|
||||
return
|
||||
}
|
||||
|
||||
_ = user
|
||||
jsonRPCErr(w, -32601, "Method not found", req.ID)
|
||||
}
|
||||
Loading…
Reference in New Issue