Merge branch 'master' of ssh://git.andreafazzi.eu:10022/andrea/youtube-dl-service

This commit is contained in:
Andrea Fazzi 2022-02-15 11:53:04 +01:00
commit 9691ca6c5c
6 changed files with 127 additions and 1 deletions

26
backend/config.go Normal file
View file

@ -0,0 +1,26 @@
package main
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
type Config struct {
Password string `yaml:"password"`
CookieStoreKey string `yaml:"cookie_store_key"`
JWTSigningKey string `yaml:"jwt_signing_key"`
JWTExpireTime uint `yaml:"jwt_expire_time"`
}
// ReadFile reads the config file placed at the given path.
func ReadConfig(path string, config *Config) error {
cf, err := ioutil.ReadFile(path)
if err != nil {
return err
}
if err := yaml.Unmarshal(cf, config); err != nil {
return err
}
return nil
}

4
backend/config.yaml Normal file
View file

@ -0,0 +1,4 @@
password: "secret"
jwt_signing_key: "abracadabra"
jwt_expire_time: 60
cookie_store_key: "abracadabra"

View file

@ -3,8 +3,10 @@ module git.andreafazzi.eu/andrea/youtube-dl-service
go 1.16 go 1.16
require ( require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.4 github.com/gin-gonic/gin v1.7.4
github.com/kkdai/youtube/v2 v2.7.4 github.com/kkdai/youtube/v2 v2.7.4
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8 // indirect github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8 // indirect
gopkg.in/yaml.v2 v2.4.0
) )

View file

@ -75,6 +75,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=

View file

@ -6,14 +6,21 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"strconv" "strconv"
"time"
"git.andreafazzi.eu/andrea/youtube-dl-service/downloader" "git.andreafazzi.eu/andrea/youtube-dl-service/downloader"
youtube_dl "git.andreafazzi.eu/andrea/youtube-dl-service/downloader/youtube-dl" youtube_dl "git.andreafazzi.eu/andrea/youtube-dl-service/downloader/youtube-dl"
"git.andreafazzi.eu/andrea/youtube-dl-service/task" "git.andreafazzi.eu/andrea/youtube-dl-service/task"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
const (
DefaultJWTExpireTime = 60
DefaultSigningKey = "secret"
)
var tasks task.Tasks var tasks task.Tasks
func init() { func init() {
@ -58,12 +65,51 @@ func data(c *gin.Context, id string) error {
return nil return nil
} }
func postLogin(c *gin.Context, config *Config) error {
err := c.Request.ParseForm()
if err != nil {
return err
}
token, err := getToken(config, c.PostForm("password"))
if err != nil {
return err
}
c.JSON(http.StatusOK, token)
return nil
}
func getToken(config *Config, password string) ([]byte, error) {
if password != config.Password {
return nil, fmt.Errorf("Wrong password!")
}
/* Set token claims */
claims := make(map[string]interface{})
// Token expire time
claims["exp"] = time.Now().Add(time.Minute * time.Duration(config.JWTExpireTime)).Unix()
/* Create the token */
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(claims))
/* Sign the token with our secret */
tokenString, err := token.SignedString(config.JWTSigningKey)
if err != nil {
log.Println(err)
return nil, err
}
return []byte(tokenString), nil
}
// API outline // API outline
// //
// POST /task content: url=https://foo.org start a new download task // POST /task content: url=https://foo.org start a new download task
// GET /task/:id get the status for the task // GET /task/:id get the status for the task
// GET /task/:id/download download the file resulted from the task execution // GET /task/:id/download download the file resulted from the task execution
//
func main() { func main() {
r := gin.Default() r := gin.Default()
r.Use(cors.New(cors.Config{ r.Use(cors.New(cors.Config{
@ -76,6 +122,12 @@ func main() {
AllowHeaders: []string{"*"}, AllowHeaders: []string{"*"},
})) }))
config := new(Config)
err := ReadConfig("config.yaml", config)
if err != nil {
panic(err)
}
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(string); ok { if err, ok := recovered.(string); ok {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
@ -97,5 +149,11 @@ func main() {
} }
}) })
r.POST("/login", func(c *gin.Context) {
if err := postLogin(c, config); err != nil {
panic(err)
}
})
r.Run() r.Run()
} }

View file

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"testing" "testing"
"time"
"git.andreafazzi.eu/andrea/youtube-dl-service/downloader" "git.andreafazzi.eu/andrea/youtube-dl-service/downloader"
"git.andreafazzi.eu/andrea/youtube-dl-service/logger" "git.andreafazzi.eu/andrea/youtube-dl-service/logger"
@ -15,6 +16,8 @@ import (
"github.com/remogatto/prettytest" "github.com/remogatto/prettytest"
) )
var config *Config
// Start of setup // Start of setup
type testSuite struct { type testSuite struct {
prettytest.Suite prettytest.Suite
@ -30,6 +33,12 @@ func TestRunner(t *testing.T) {
func (t *testSuite) BeforeAll() { func (t *testSuite) BeforeAll() {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
logger.SetLevel(logger.Disabled) logger.SetLevel(logger.Disabled)
config = new(Config)
err := ReadConfig("config.yaml", config)
if err != nil {
panic(err)
}
} }
// Test the creation of a new task. A new task is created with a POST // Test the creation of a new task. A new task is created with a POST
@ -49,6 +58,9 @@ func (t *testSuite) TestGetTask() {
panic(err) panic(err)
} }
timer := time.NewTimer(time.Second * 10)
<-timer.C
// Create a new request // Create a new request
req, err := http.NewRequest("GET", "/task/"+video.ID, nil) req, err := http.NewRequest("GET", "/task/"+video.ID, nil)
if err != nil { if err != nil {
@ -75,6 +87,29 @@ func (t *testSuite) TestGetTask() {
t.Equal(task.StatusCompleted, ts.Status) t.Equal(task.StatusCompleted, ts.Status)
} }
func (t *testSuite) TestPostLogin() {
// Set form values
form := url.Values{}
form.Add("password", "secret")
req, err := http.NewRequest("POST", "/login", strings.NewReader((form.Encode())))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// Create an http recorder
rr := httptest.NewRecorder()
// Set the context and call the handler
c, _ := gin.CreateTestContext(rr)
c.Request = req
err = postLogin(c, config)
t.Nil(err)
}
func postTask(ytUrl string) (*downloader.Video, error) { func postTask(ytUrl string) (*downloader.Video, error) {
// Set form values // Set form values
form := url.Values{} form := url.Values{}