80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
// HTTP routes and handlers.
|
|
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/nalgeon/codapi/internal/engine"
|
|
"github.com/nalgeon/codapi/internal/logx"
|
|
"github.com/nalgeon/codapi/internal/sandbox"
|
|
"github.com/nalgeon/codapi/internal/stringx"
|
|
)
|
|
|
|
// NewRouter creates HTTP routes and handlers for them.
|
|
func NewRouter() http.Handler {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/v1/exec", enableCORS(exec))
|
|
return mux
|
|
}
|
|
|
|
// exec runs a sandbox command on the supplied code.
|
|
func exec(w http.ResponseWriter, r *http.Request) {
|
|
// only POST is allowed
|
|
if r.Method != http.MethodPost {
|
|
err := fmt.Errorf("unsupported method: %s", r.Method)
|
|
writeError(w, http.StatusMethodNotAllowed, engine.Fail("-", err))
|
|
return
|
|
}
|
|
|
|
// read the input data - language, command, code
|
|
in, err := readJson[engine.Request](r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, engine.Fail("-", err))
|
|
return
|
|
}
|
|
in.GenerateID()
|
|
|
|
// validate the input data
|
|
err = sandbox.Validate(in)
|
|
if errors.Is(err, sandbox.ErrUnknownSandbox) || errors.Is(err, sandbox.ErrUnknownCommand) {
|
|
writeError(w, http.StatusNotFound, engine.Fail(in.ID, err))
|
|
return
|
|
}
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, engine.Fail(in.ID, err))
|
|
return
|
|
}
|
|
|
|
// execute the code using the sandbox
|
|
out := sandbox.Exec(in)
|
|
|
|
// fail on application error
|
|
if out.Err != nil {
|
|
logx.Log("✗ %s: %s", out.ID, out.Err)
|
|
if errors.Is(out.Err, engine.ErrBusy) {
|
|
writeError(w, http.StatusTooManyRequests, out)
|
|
} else {
|
|
writeError(w, http.StatusInternalServerError, out)
|
|
}
|
|
return
|
|
}
|
|
|
|
// log results
|
|
if out.OK {
|
|
logx.Log("✓ %s: took %d ms", out.ID, out.Duration)
|
|
} else {
|
|
msg := stringx.Compact(stringx.Shorten(out.Stderr, 80))
|
|
logx.Log("✗ %s: %s", out.ID, msg)
|
|
}
|
|
|
|
// write the response
|
|
err = writeJson(w, out)
|
|
if err != nil {
|
|
err = engine.NewExecutionError("write response", err)
|
|
writeError(w, http.StatusInternalServerError, engine.Fail(in.ID, err))
|
|
return
|
|
}
|
|
}
|