Skip to content

Commit

Permalink
feat(server): Follow API REST standars
Browse files Browse the repository at this point in the history
  • Loading branch information
pando85 committed Jan 22, 2024
1 parent 7fd772b commit 6a9aeb5
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 51 deletions.
9 changes: 6 additions & 3 deletions scripts/test-upload.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
set -e

TOKEN=super-secret-token
JOBS_URL="http://localhost:8080/api/v1/job/?token=${TOKEN}"
JOBS_URL="http://localhost:8080/api/v1/job/"
AUTH_HEADER="Authorization: Bearer ${TOKEN}"

payload() {
cat <<EOF
Expand All @@ -23,16 +24,18 @@ data=$(payload "${url}")
echo "Upload job"

curl -s --location --request POST "${JOBS_URL}" \
--header "${AUTH_HEADER}" \
--header 'Content-Type: text/plain' \
--data "$data"

echo -e '\n'

MAX_ATTEMPTS=50
for attempt in $(seq 1 $MAX_ATTEMPTS); do
echo "Attempt $attempt to get job status"

id=$(curl -s "${JOBS_URL}" | jq -r '.[0].id')
status=$(curl -s "${JOBS_URL}&uuid=${id}" | jq -r '.status')
id=$(curl -s "${JOBS_URL}" --header "${AUTH_HEADER}" | jq -r '.[0].id')
status=$(curl -s "${JOBS_URL}${id}" --header "${AUTH_HEADER}" | jq -r '.status')

if [ "${status}" = "completed" ]; then
echo "OK"
Expand Down
6 changes: 3 additions & 3 deletions server/scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ func (R *RuntimeScheduler) scheduleJobRequest(ctx context.Context, jobRequest *m
}
}

downloadURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/download?uuid=%s", R.config.Domain.String(), video.Id.String()))
uploadURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/upload?uuid=%s", R.config.Domain.String(), video.Id.String()))
checksumURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/checksum?uuid=%s", R.config.Domain.String(), video.Id.String()))
downloadURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/download/%s", R.config.Domain.String(), video.Id.String()))
uploadURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/upload/%s", R.config.Domain.String(), video.Id.String()))
checksumURL, _ := url.Parse(fmt.Sprintf("%s/api/v1/checksum/%s", R.config.Domain.String(), video.Id.String()))
task := &model.TaskEncode{
Id: video.Id,
DownloadURL: downloadURL.String(),
Expand Down
11 changes: 8 additions & 3 deletions server/web/ui/src/JobTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ const JobTable: React.FC<JobTableProps> = ({ token, setShowJobTable }) => {
try {
setLoading(true);
const response = await axios.get('/api/v1/job/', {
params: { token, page },
params: { page },
headers: {
Authorization: `Bearer ${token}`,
},
});
const newJobs: Job[] = response.data;
setJobs((prevJobs) => [...prevJobs, ...newJobs]);
Expand Down Expand Up @@ -92,8 +95,10 @@ const JobTable: React.FC<JobTableProps> = ({ token, setShowJobTable }) => {
const fetchJobDetails = async (jobId: string) => {
if (!fetchedDetails.has(jobId)) {
try {
const response = await axios.get(`/api/v1/job/`, {
params: { token, uuid: jobId },
const response = await axios.get(`/api/v1/job/${jobId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});

const foundJob = jobs.find((job) => job.id === jobId);
Expand Down
8 changes: 7 additions & 1 deletion server/web/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,11 @@ func (f *fallbackFileSystem) Open(path string) (http.File, error) {
}

func (f *fallbackFileSystem) Exists(prefix string, path string) bool {
return true
// Exclude paths starting with "/api/"
if strings.HasPrefix(path, "/api/") {
return false
}

// Delegate to staticFileSystem for other paths
return f.staticFileSystem.Exists(prefix, path)
}
99 changes: 58 additions & 41 deletions server/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"transcoder/model"
"transcoder/server/scheduler"
Expand All @@ -25,13 +26,13 @@ type WebServer struct {
}

func (w *WebServer) cancelJob(c *gin.Context) {
uuid := c.Query("uuid")
if uuid == "" {
webError(c, fmt.Errorf("UUID query parameter not found"), 404)
id := c.Param("id")
if id == "" {
webError(c, fmt.Errorf("Job ID parameter not found"), 404)
return
}

err := w.scheduler.CancelJob(c.Request.Context(), uuid)
err := w.scheduler.CancelJob(c.Request.Context(), id)
if err != nil {
if errors.Is(err, scheduler.ErrorJobNotFound) {
webError(c, err, 404)
Expand Down Expand Up @@ -59,7 +60,13 @@ func (w *WebServer) addJobs(c *gin.Context) {
c.JSON(http.StatusOK, scheduleJobResults)
}

func (w *WebServer) getAllJobs(c *gin.Context) {
func getPageParams(c *gin.Context) (int, int) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
return page, pageSize
}

func (w *WebServer) getJobs(c *gin.Context) {
page, pageSize := getPageParams(c)

videos, err := w.scheduler.GetJobs(w.ctx, page, pageSize)
Expand All @@ -71,26 +78,20 @@ func (w *WebServer) getAllJobs(c *gin.Context) {
c.JSON(http.StatusOK, videos)
}

func getPageParams(c *gin.Context) (int, int) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
return page, pageSize
}

func (w *WebServer) getJobs(c *gin.Context) {
uuid := c.Query("uuid")

if uuid == "" {
w.getAllJobs(c)
} else {
video, err := w.scheduler.GetJob(w.ctx, uuid)
if err != nil {
webError(c, err, http.StatusInternalServerError)
return
}
func (w *WebServer) getJobByID(c *gin.Context) {
id := c.Param("id")
if id == "" {
webError(c, fmt.Errorf("Job ID parameter not found"), 404)
return
}

c.JSON(http.StatusOK, video)
video, err := w.scheduler.GetJob(w.ctx, id)
if err != nil {
webError(c, err, http.StatusInternalServerError)
return
}

c.JSON(http.StatusOK, video)
}

func (w *WebServer) jobs(c *gin.Context) {
Expand All @@ -106,13 +107,13 @@ func (w *WebServer) jobs(c *gin.Context) {
}

func (w *WebServer) upload(c *gin.Context) {
uuid := c.Query("uuid")
if uuid == "" {
webError(c, fmt.Errorf("UUID query parameter not found"), 404)
id := c.Param("id")
if id == "" {
webError(c, fmt.Errorf("Job ID parameter not found"), 404)
return
}

uploadStream, err := w.scheduler.GetUploadJobWriter(c.Request.Context(), uuid)
uploadStream, err := w.scheduler.GetUploadJobWriter(c.Request.Context(), id)
if errors.Is(err, scheduler.ErrorStreamNotAllowed) {
webError(c, err, 403)
return
Expand Down Expand Up @@ -164,13 +165,13 @@ loop:
}

func (w *WebServer) download(c *gin.Context) {
uuid := c.Query("uuid")
if uuid == "" {
webError(c, fmt.Errorf("UUID query parameter not found"), 404)
id := c.Param("id")
if id == "" {
webError(c, fmt.Errorf("Job ID parameter not found"), 404)
return
}

downloadStream, err := w.scheduler.GetDownloadJobWriter(c.Request.Context(), uuid)
downloadStream, err := w.scheduler.GetDownloadJobWriter(c.Request.Context(), id)
if errors.Is(err, scheduler.ErrorStreamNotAllowed) {
webError(c, err, 403)
return
Expand Down Expand Up @@ -203,12 +204,13 @@ loop:
}

func (w *WebServer) checksum(c *gin.Context) {
uuid := c.Query("uuid")
if uuid == "" {
webError(c, fmt.Errorf("UUID query parameter not found"), 404)
id := c.Param("id")
if id == "" {
webError(c, fmt.Errorf("Job ID parameter not found"), 404)
return
}
checksum, err := w.scheduler.GetChecksum(c.Request.Context(), uuid)

checksum, err := w.scheduler.GetChecksum(c.Request.Context(), id)
if webError(c, err, 404) {
return
}
Expand Down Expand Up @@ -242,10 +244,12 @@ func NewWebServer(config WebServerConfig, scheduler scheduler.Scheduler) *WebSer
api := r.Group("/api/v1")
api.GET("/job/", webServer.AuthFunc(webServer.jobs))
api.POST("/job/", webServer.AuthFunc(webServer.jobs))
api.GET("/job/cancel", webServer.cancelJob)
api.GET("/download", webServer.download)
api.GET("/checksum", webServer.checksum)
api.POST("/upload", webServer.upload)
api.GET("/job/:id", webServer.AuthFunc(webServer.getJobByID))
api.PUT("/job/cancel/:id", webServer.AuthFunc(webServer.cancelJob))
api.POST("/job/cancel/:id", webServer.AuthFunc(webServer.cancelJob))
api.GET("/download/:id", webServer.download)
api.GET("/checksum/:id", webServer.checksum)
api.POST("/upload/:id", webServer.upload)

ui.AddRoutes(r)

Expand Down Expand Up @@ -284,12 +288,25 @@ func (w *WebServer) stop(ctx context.Context) {

func (w *WebServer) AuthFunc(handler gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
t := c.Query("token")
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized: Missing Authorization header"})
return
}

const bearerPrefix = "Bearer "
if !strings.HasPrefix(authHeader, bearerPrefix) {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized: Invalid Authorization header format"})
return
}

t := strings.TrimPrefix(authHeader, bearerPrefix)

if t != w.Token {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized: Invalid token"})
return
}

handler(c)
}
}
Expand Down

0 comments on commit 6a9aeb5

Please sign in to comment.