Skip to content

Commit

Permalink
feat: rewrite presign url generation (#75)
Browse files Browse the repository at this point in the history
* feat: re-wrote presign url generation

* chore: bump go version
  • Loading branch information
abskrj authored Oct 27, 2023
1 parent 903ff5f commit 6ef7d90
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 26 deletions.
4 changes: 3 additions & 1 deletion .env-template
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PORT=8080
RABBITMQ_URI=rabbitmq_uri
MAX_CONCURRENT_PROCESSING=1
CDN_BASE_URL=http://localhost

GOOGLE_APPLICATION_CREDENTIALS=path of the json file
GCP_BUCKET_NAME=
Expand All @@ -12,4 +13,5 @@ AZURE_ACCESS_KEY=azure_access_key

AWS_ACCESS_KEY_ID=YOUR_AKID
AWS_SECRET_ACCESS_KEY=YOUR_SECRET_KEY
AWS_SESSION_TOKEN=TOKEN
AWS_SESSION_TOKEN=TOKEN
AWS_S3_BUCKET_NAME=NAME
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
go-version: 1.21

- name: Build
run: go build -v
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.19
go-version: 1.21
- uses: actions/checkout@v3
- name: Tidy
run: go mod tidy
Expand Down
2 changes: 1 addition & 1 deletion configs/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func getAWSSession() *session.Session {
Credentials: credentials.NewStaticCredentials(
EnvVar[AWS_ACCESS_KEY_ID],
EnvVar[AWS_SECRET_ACCESS_KEY],
EnvVar[AWS_SESSION_TOKEN],
"",
),
CredentialsChainVerboseErrors: aws.Bool(true),
})
Expand Down
3 changes: 3 additions & 0 deletions configs/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
GCP_CREDS_JSON_BASE64
GCP_SERVICE_USER_CREDS_JSON_BASE64
MAX_CONCURRENT_PROCESSING
CDN_BASE_URL
)

var configVars = map[CONFIG_KEY]string{
Expand All @@ -43,6 +44,7 @@ var configVars = map[CONFIG_KEY]string{
AWS_SECRET_ACCESS_KEY: "AWS_SECRET_ACCESS_KEY",
AWS_SESSION_TOKEN: "AWS_SESSION_TOKEN",
MAX_CONCURRENT_PROCESSING: "MAX_CONCURRENT_PROCESSING",
CDN_BASE_URL: "CDN_BASE_URL",
}

var EnvVar = map[CONFIG_KEY]string{
Expand All @@ -61,6 +63,7 @@ var EnvVar = map[CONFIG_KEY]string{
AWS_SECRET_ACCESS_KEY: "",
AWS_SESSION_TOKEN: "",
MAX_CONCURRENT_PROCESSING: "1",
CDN_BASE_URL: "",
}

func LoadEnv() {
Expand Down
17 changes: 15 additions & 2 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,26 @@ const WRITE_TIMEOUT = 5 * time.Second
const IDLE_TIMEOUT = 30 * time.Second

// folders
type FOLDER_TYPE int

const (
Dashes FOLDER_TYPE = iota
Images
Audios
)

var CloudContainerNames = map[FOLDER_TYPE]string{
Dashes: "dashes",
Images: "i",
Audios: "a",
}

const DOWNLOAD_FILE_PATH_PREFIX = "assets/downloads"
const OUTPUT_FILE_PATH_PREFIX = "assets/output"
const CLOUD_CONTAINER_NAME = "dashes"
const DOWNLOAD_FOLDER_PERM = 0666

const AWS_ENDPOINT = "http://localhost:4566"
const PRESIGNED_URL_EXPIRATION = 1 * time.Hour
const PRESIGNED_URL_EXPIRATION = 24 * time.Hour

// event queue
const RABBIT_MQ_CHANNEL = "VideoProcessing"
Expand Down
4 changes: 3 additions & 1 deletion controllers/processVideo.go → controllers/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"github.com/gin-gonic/gin"
)

func ProcessVideo(c *gin.Context) {
type Process struct{}

func (*Process) Video(c *gin.Context) {
var request types.Video

if err := c.ShouldBindJSON(&request); err != nil {
Expand Down
11 changes: 5 additions & 6 deletions controllers/upload.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package controllers

import (
"log"
"net/http"
"path/filepath"
"zestream-server/utils"
Expand All @@ -11,18 +10,18 @@ import (

func GetPresignedURL(c *gin.Context) {
fileName := c.Query("fileName")
fileType := c.Query("fileType")
basePath := c.Query("basePath")
extension := filepath.Ext(fileName)

if fileName == "" || extension == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required query parameter 'fileName' or 'fileName' provided without any extension"})
if fileName == "" || extension == "" || fileType == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required query parameter 'fileName' or 'fileType'"})
return
}

videoID := utils.VideoIDGen(extension)

log.Println(videoID, "id")

url := utils.GetSignedURL(videoID)
url := utils.GetSignedURL(videoID, fileType, basePath)
if url == "" {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error generating presigned URL", "details": ""})
return
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module zestream-server

go 1.19
go 1.21

require (
cloud.google.com/go/storage v1.28.1
Expand Down
4 changes: 3 additions & 1 deletion routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ func Init() *gin.Engine {

r.GET("health", controllers.Ping)

process := new(controllers.Process)

// /api/v1
apiV1.POST("video/process", controllers.ProcessVideo)
apiV1.POST("video/process", process.Video)
apiV1.GET("url/presigned", controllers.GetPresignedURL)

return r
Expand Down
2 changes: 1 addition & 1 deletion service/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func processVideo(video *types.Video, guard <-chan int) {

generateDash(videoFileName, video.Watermark)

uploader := utils.GetUploader(constants.CLOUD_CONTAINER_NAME, video.ID)
uploader := utils.GetUploader(constants.CloudContainerNames[constants.Dashes], video.ID)

outputDir, err := utils.GetOutputFilePathName(videoFileName, "")
utils.LogErr(err)
Expand Down
7 changes: 7 additions & 0 deletions service/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package service

type Image struct{}

func (*Image) Get() {

}
101 changes: 91 additions & 10 deletions utils/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,25 @@ type Uploader interface {
func GetUploader(containerName string, videoId string) Uploader {

cloudSession := configs.GetCloudSession()
isAWS, isGCP, isAzure := GetWhichCloudIsEnabled()

if configs.EnvVar[configs.AWS_ACCESS_KEY_ID] != "" {
if isAWS {
return AwsUploader{
ContainerName: containerName,
VideoId: videoId,
Session: cloudSession.AWS,
}
}

if configs.EnvVar[configs.GCP_PROJECT_ID] != "" {
if isGCP {
return &GcpUploader{
ContainerName: containerName,
VideoId: videoId,
Client: cloudSession.GCP,
}
}

if configs.EnvVar[configs.AZURE_ACCESS_KEY] != "" {
if isAzure {
return AzureUploader{
ContainerName: containerName,
VideoId: videoId,
Expand Down Expand Up @@ -228,25 +229,50 @@ func (a AzureUploader) Upload(walker fileWalk) {

}

func GetSignedURL(videoId string) string {
func GetWhichCloudIsEnabled() (bool, bool, bool) {
if configs.EnvVar[configs.AWS_ACCESS_KEY_ID] != "" {
return generateAWSSignedURL(videoId)
return true, false, false
}

if configs.EnvVar[configs.GCP_BUCKET_NAME] != "" {
return generateGCPSignedURL(videoId)
return false, true, false
}

if configs.EnvVar[configs.AZURE_ACCOUNT_NAME] != "" {
return false, false, true
}

return false, false, false
}

func GetSignedURL(videoId string, fileType string, basePath string) string {
isAWS, isGCP, isAzure := GetWhichCloudIsEnabled()

filePath, err := GetCloudStoragePath(basePath, videoId, fileType)
LogErr(err)

if isAWS {
return generateAWSSignedURL(filePath)
}

if isGCP {
return generateGCPSignedURL(filePath)
}

if isAzure {
return generateAzureSignedURL(filePath)
}

return ""
}

func generateGCPSignedURL(videoId string) string {
func generateGCPSignedURL(filePath string) string {
client := configs.GetGCPClient(true)
bucket := client.Bucket(configs.EnvVar[configs.GCP_BUCKET_NAME])

expirationTime := time.Now().Add(constants.PRESIGNED_URL_EXPIRATION)

url, err := bucket.SignedURL(videoId, &storage.SignedURLOptions{
url, err := bucket.SignedURL(filePath, &storage.SignedURLOptions{
Method: "GET",
Expires: expirationTime,
})
Expand All @@ -257,15 +283,16 @@ func generateGCPSignedURL(videoId string) string {
return url
}

func generateAWSSignedURL(videoId string) string {
func generateAWSSignedURL(filePath string) string {
session := configs.GetCloudSession()

client := s3.New(session.AWS)

req, err := client.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(configs.EnvVar[configs.AWS_S3_BUCKET_NAME]),
Key: aws.String(videoId),
Key: aws.String(filePath),
})

if err != nil {
log.Println(err)
}
Expand All @@ -277,3 +304,57 @@ func generateAWSSignedURL(videoId string) string {

return url
}

func generateBlobSAS(blobURL azblob.BlobURL, permissions string) (string, error) {
session := configs.GetCloudSession()
client := session.Azure

sasQueryParams, err := azblob.BlobSASSignatureValues{
Protocol: azblob.SASProtocolHTTPS,
ExpiryTime: time.Now().Add(constants.PRESIGNED_URL_EXPIRATION),
Permissions: permissions,
Version: time.Now().Format(time.DateOnly),
}.NewSASQueryParameters(client)

if err != nil {
return "", err
}

sasURL := blobURL.URL()
sasURL.RawQuery = sasQueryParams.Encode()
log.Println(sasQueryParams.Encode())
return sasURL.String(), nil
}

func generateAzureSignedURL(filePath string) string {
accountName := configs.EnvVar[configs.AZURE_ACCOUNT_NAME]
accountKey := configs.EnvVar[configs.AZURE_ACCESS_KEY]
containerName := constants.CloudContainerNames[constants.Images]

credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
fmt.Println("Failed to create shared key credential:", err)
return ""
}

pipeline := azblob.NewPipeline(credential, azblob.PipelineOptions{})
URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", accountName, containerName))
blobURL := azblob.NewContainerURL(*URL, pipeline).NewBlobURL(filePath)

permissions := "rw"

sasURL, err := generateBlobSAS(blobURL, permissions)
if err != nil {
fmt.Println("Failed to generate pre-signed URL:", err)
return ""
}

return sasURL
}

func GetCloudStoragePath(basePath, fileName string, _ string) (string, error) {
url, err := url.JoinPath(basePath, fileName)
LogErr(err)

return url, err
}

0 comments on commit 6ef7d90

Please sign in to comment.