Merge branch 'master' of ssh://git.andreafazzi.eu:10022/andrea/youtube-dl-service
This commit is contained in:
commit
9691ca6c5c
6 changed files with 127 additions and 1 deletions
26
backend/config.go
Normal file
26
backend/config.go
Normal 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
4
backend/config.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
password: "secret"
|
||||||
|
jwt_signing_key: "abracadabra"
|
||||||
|
jwt_expire_time: 60
|
||||||
|
cookie_store_key: "abracadabra"
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
Loading…
Reference in a new issue