2024-02-14 00:56:11 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-05-18 14:36:44 +02:00
|
|
|
"context"
|
2024-03-04 16:26:34 +01:00
|
|
|
"fmt"
|
2024-05-17 00:59:46 +02:00
|
|
|
"io"
|
|
|
|
"log"
|
2024-02-14 00:56:11 +01:00
|
|
|
"net/http"
|
2024-05-17 00:59:46 +02:00
|
|
|
"os"
|
2024-02-14 00:56:11 +01:00
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2024-07-12 00:49:38 +02:00
|
|
|
"github.com/jackc/pgx/v5"
|
2024-03-04 16:26:34 +01:00
|
|
|
"github.com/jaswdr/faker"
|
2024-02-14 00:56:11 +01:00
|
|
|
)
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
var dbconn *pgx.Conn
|
2024-06-03 19:44:53 +02:00
|
|
|
var uploadFolder string
|
|
|
|
|
2024-06-03 19:36:41 +02:00
|
|
|
func initUpload(c *gin.Context) {
|
|
|
|
fileName, err := io.ReadAll(c.Request.Body)
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusBadRequest, "Couldn't read html request body")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
rows, _ := dbconn.Query(context.Background(), "select filepath from videos")
|
2024-06-03 19:36:41 +02:00
|
|
|
for rows.Next() {
|
|
|
|
var filepath string
|
|
|
|
err = rows.Scan(&filepath)
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("initUpload: %v\n", err)
|
2024-06-03 19:36:41 +02:00
|
|
|
}
|
2024-06-03 19:44:53 +02:00
|
|
|
if filepath == fmt.Sprintf("%s/%s", uploadFolder, fileName) {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusForbidden, "File already exists")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = rows.Err()
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("initUpload: %v\n", err)
|
2024-06-03 19:36:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, "Upload can proceed")
|
|
|
|
}
|
|
|
|
|
2024-06-03 21:04:50 +02:00
|
|
|
func receiveChunk(c *gin.Context) {
|
2024-05-19 00:19:47 +02:00
|
|
|
chunk, err := io.ReadAll(c.Request.Body)
|
2024-05-17 00:59:46 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusBadRequest, "Couldn't read html request body")
|
2024-05-18 14:36:44 +02:00
|
|
|
return
|
|
|
|
}
|
2024-05-17 00:59:46 +02:00
|
|
|
|
2024-07-11 23:05:35 +02:00
|
|
|
f, err := os.OpenFile(fmt.Sprintf("%s/%s", uploadFolder, c.GetHeader("file-name")), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
|
2024-05-18 14:36:44 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 22:15:43 +02:00
|
|
|
log.Panicf("receiveChunk: %v\n", err)
|
2024-05-18 14:36:44 +02:00
|
|
|
}
|
2024-05-19 00:19:47 +02:00
|
|
|
if _, err := f.Write(chunk); err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 22:15:43 +02:00
|
|
|
log.Panicf("receiveChunk: %v\n", err)
|
2024-05-17 00:59:46 +02:00
|
|
|
}
|
2024-05-18 14:36:44 +02:00
|
|
|
if err := f.Close(); err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 22:15:43 +02:00
|
|
|
log.Panicf("receiveChunk: %v\n", err)
|
2024-05-18 14:36:44 +02:00
|
|
|
}
|
|
|
|
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusOK, "Received chunk")
|
2024-04-14 20:52:06 +02:00
|
|
|
}
|
|
|
|
|
2024-06-03 19:36:41 +02:00
|
|
|
func finishUpload(c *gin.Context) {
|
2024-05-19 00:19:47 +02:00
|
|
|
fileName, err := io.ReadAll(c.Request.Body)
|
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusBadRequest, "Couldn't read html request body")
|
2024-02-14 00:56:11 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
_, err = dbconn.Exec(context.Background(), "insert into videos(filepath) values($1)", fmt.Sprintf("%s/%s", uploadFolder, fileName))
|
2024-05-19 00:19:47 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("finishUpload: %v\n", err)
|
2024-02-23 23:23:06 +01:00
|
|
|
}
|
|
|
|
|
2024-06-03 22:15:43 +02:00
|
|
|
c.JSON(http.StatusOK, "File uploaded successfully")
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
|
|
|
|
2024-05-19 00:19:47 +02:00
|
|
|
func listVideos(c *gin.Context) {
|
2024-06-10 23:53:15 +02:00
|
|
|
allVideos := map[int]string{}
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
rows, _ := dbconn.Query(context.Background(), "select * from videos")
|
2024-05-19 00:19:47 +02:00
|
|
|
for rows.Next() {
|
|
|
|
var id int
|
|
|
|
var filepath string
|
2024-06-11 00:18:41 +02:00
|
|
|
err := rows.Scan(&id, &filepath)
|
2024-05-19 00:19:47 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("listVideos: %v\n", err)
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
2024-06-10 23:53:15 +02:00
|
|
|
allVideos[id] = filepath
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
2024-06-11 00:18:41 +02:00
|
|
|
err := rows.Err()
|
2024-05-19 00:19:47 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("listVideos: %v\n", err)
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
2024-06-01 13:58:16 +02:00
|
|
|
|
2024-06-03 21:19:36 +02:00
|
|
|
c.JSON(http.StatusOK, allVideos)
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
|
|
|
|
2024-05-19 00:19:47 +02:00
|
|
|
func getVideo(c *gin.Context) {
|
2024-07-12 00:49:38 +02:00
|
|
|
rows, _ := dbconn.Query(context.Background(), "select filepath from videos where id = $1", c.Param("id"))
|
2024-06-01 13:58:16 +02:00
|
|
|
rows.Next()
|
2024-06-11 00:18:41 +02:00
|
|
|
err := rows.Err()
|
2024-06-01 13:58:16 +02:00
|
|
|
if err != nil {
|
2024-06-03 19:36:41 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("getVideo: %v\n", err)
|
2024-06-01 13:58:16 +02:00
|
|
|
}
|
2024-06-10 23:53:15 +02:00
|
|
|
|
|
|
|
var filepath string
|
|
|
|
err = rows.Scan(&filepath)
|
2024-06-01 13:58:16 +02:00
|
|
|
if err != nil {
|
2024-06-10 23:53:15 +02:00
|
|
|
c.JSON(http.StatusBadRequest, "Video does not exist")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("getVideo: %v\n", err)
|
2024-06-01 13:58:16 +02:00
|
|
|
}
|
|
|
|
|
2024-06-03 22:15:43 +02:00
|
|
|
c.JSON(http.StatusOK, filepath)
|
2024-06-01 13:58:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func deleteVideo(c *gin.Context) {
|
2024-07-12 00:49:38 +02:00
|
|
|
rows, _ := dbconn.Query(context.Background(), "select filepath from videos where id = $1", c.Param("id"))
|
2024-06-01 13:58:16 +02:00
|
|
|
rows.Next()
|
2024-06-11 00:18:41 +02:00
|
|
|
err := rows.Err()
|
2024-06-01 13:58:16 +02:00
|
|
|
if err != nil {
|
2024-06-10 23:53:15 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "")
|
2024-06-03 22:15:43 +02:00
|
|
|
log.Panicf("deleteVideo: %v\n", err)
|
2024-05-19 00:19:47 +02:00
|
|
|
}
|
2024-06-10 23:53:15 +02:00
|
|
|
|
|
|
|
var filepath string
|
|
|
|
err = rows.Scan(&filepath)
|
2024-05-19 00:19:47 +02:00
|
|
|
if err != nil {
|
2024-06-10 23:53:15 +02:00
|
|
|
c.JSON(http.StatusBadRequest, "Video does not exist")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("deleteVideo: %v\n", err)
|
2024-06-01 13:58:16 +02:00
|
|
|
}
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
_, err = dbconn.Exec(context.Background(), "delete from videos where id = $1", c.Param("id"))
|
2024-06-01 13:58:16 +02:00
|
|
|
if err != nil {
|
2024-06-10 23:53:15 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "Id was likely invalid")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("deleteVideo: %v\n", err)
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
2024-06-01 13:58:16 +02:00
|
|
|
|
|
|
|
if err = os.Remove(filepath); err != nil {
|
2024-06-10 23:53:15 +02:00
|
|
|
c.JSON(http.StatusInternalServerError, "DB entry was deleted, but file likely doesn't exist")
|
2024-06-03 21:19:36 +02:00
|
|
|
log.Panicf("deleteVideo: %v\n", err)
|
2024-06-01 13:58:16 +02:00
|
|
|
}
|
|
|
|
|
2024-06-03 22:15:43 +02:00
|
|
|
c.JSON(http.StatusOK, "File deleted successfully")
|
2024-02-14 00:56:11 +01:00
|
|
|
}
|
2024-07-12 00:32:52 +02:00
|
|
|
|
|
|
|
func main() {
|
|
|
|
var err error
|
2024-07-12 00:49:38 +02:00
|
|
|
dbconn, err = pgx.Connect(context.Background(), "postgresql://postgres:postgres@172.18.0.3:5432/postgres")
|
2024-07-12 00:32:52 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2024-07-12 00:49:38 +02:00
|
|
|
defer dbconn.Close(context.Background())
|
2024-07-12 00:32:52 +02:00
|
|
|
|
|
|
|
sqlStmt := `
|
|
|
|
DROP TABLE IF EXISTS videos;
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS videos
|
|
|
|
(
|
|
|
|
id serial NOT NULL,
|
|
|
|
filepath text,
|
|
|
|
CONSTRAINT videos_pkey PRIMARY KEY (id)
|
|
|
|
)
|
|
|
|
`
|
2024-07-12 00:49:38 +02:00
|
|
|
_, err = dbconn.Exec(context.Background(), sqlStmt)
|
2024-07-12 00:32:52 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
faker := faker.New()
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2024-07-12 00:49:38 +02:00
|
|
|
_, err = dbconn.Exec(context.Background(), "insert into videos(filepath) values($1)", faker.File().AbsoluteFilePathForUnix(2))
|
2024-07-12 00:32:52 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
currentDir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
uploadFolder = fmt.Sprintf("%s/upload", currentDir)
|
|
|
|
|
|
|
|
if _, err := os.Stat(uploadFolder); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Mkdir(uploadFolder, 0750); err != nil {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
files, err := os.ReadDir(uploadFolder)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(files) != 0 {
|
|
|
|
for _, file := range files {
|
|
|
|
if file.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-07-12 00:49:38 +02:00
|
|
|
_, err = dbconn.Exec(context.Background(), "insert into videos(filepath) values($1)", fmt.Sprintf("%s/%s", uploadFolder, file.Name()))
|
2024-07-12 00:32:52 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Panicf("main: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
router := gin.Default()
|
|
|
|
router.SetTrustedProxies(nil)
|
|
|
|
|
|
|
|
router.POST("/video/init", initUpload)
|
|
|
|
router.POST("/video/chunk", receiveChunk)
|
|
|
|
router.POST("/video/completed", finishUpload)
|
|
|
|
router.GET("/videos", listVideos)
|
|
|
|
router.GET("/videos/:id", getVideo)
|
|
|
|
router.DELETE("/videos/:id", deleteVideo)
|
|
|
|
|
|
|
|
router.Run("localhost:8080")
|
|
|
|
}
|