Skip to content

Commit

Permalink
Switch downloading Kodo objects from unsigned to signed URLs
Browse files Browse the repository at this point in the history
Updates goplus#412
  • Loading branch information
aofei committed May 14, 2024
1 parent 5157953 commit 70678e5
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 29 deletions.
52 changes: 41 additions & 11 deletions spx-backend/cmd/spx-backend/gop_autogen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions spx-backend/cmd/spx-backend/project_yap.gox
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,23 @@ get "/util/upinfo", ctx => {
replyWithData(ctx, upInfo)
}

// Make file urls for downloading
post "/util/fileurls", ctx => {
if _, ok := ensureUser(ctx); !ok {
return
}
params := &controller.MakeFileURLsParams{}
if ok := parseJson(ctx, params); !ok {
return
}
fileURLs, err := ctrl.MakeFileURLs(utils.getCtx(ctx), params)
if err != nil {
handlerInnerError(ctx, err)
return
}
replyWithData(ctx, fileURLs)
}

var err error
logger := log.GetLogger()
ctrl, err = controller.NewController(context.Background())
Expand Down
60 changes: 54 additions & 6 deletions spx-backend/internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"database/sql"
"errors"
"fmt"
"net/url"
"os"
"regexp"
"strings"
"time"

_ "image/png"

Expand Down Expand Up @@ -35,8 +38,7 @@ type Controller struct {
}

type kodoConfig struct {
ak string
sk string
cred *qiniuAuth.Credentials
bucket string
bucketRegion string
baseUrl string
Expand All @@ -60,8 +62,10 @@ func NewController(ctx context.Context) (ret *Controller, err error) {
}

kc := &kodoConfig{
ak: mustEnv(logger, "KODO_AK"),
sk: mustEnv(logger, "KODO_SK"),
cred: qiniuAuth.New(
mustEnv(logger, "KODO_AK"),
mustEnv(logger, "KODO_SK"),
),
bucket: mustEnv(logger, "KODO_BUCKET"),
bucketRegion: mustEnv(logger, "KODO_BUCKET_REGION"),
baseUrl: mustEnv(logger, "KODO_BASE_URL"),
Expand Down Expand Up @@ -519,8 +523,7 @@ func (ctrl *Controller) GetUpInfo(ctx context.Context) (*UpInfo, error) {
// the future.
FsizeLimit: 25 << 20, // 25 MiB
}
mac := qiniuAuth.New(ctrl.kodo.ak, ctrl.kodo.sk)
upToken := putPolicy.UploadToken(mac)
upToken := putPolicy.UploadToken(ctrl.kodo.cred)
return &UpInfo{
Token: upToken,
Expires: putPolicy.Expires,
Expand All @@ -529,3 +532,48 @@ func (ctrl *Controller) GetUpInfo(ctx context.Context) (*UpInfo, error) {
BaseUrl: ctrl.kodo.baseUrl,
}, nil
}

type MakeFileURLsParams struct {
// Objects is a list of object keys.
Objects []string `json:"objects"`
}

type FileURLs struct {
// ObjectURLs is a map from object keys to signed URLs for the objects.
ObjectURLs map[string]string `json:"objectUrls"`
}

func (ctrl *Controller) MakeFileURLs(ctx context.Context, params *MakeFileURLsParams) (*FileURLs, error) {
const expires = 2 * 24 * 3600 // 2 days
logger := log.GetReqLogger(ctx)
fileURLs := &FileURLs{
ObjectURLs: make(map[string]string, len(params.Objects)),
}
for _, object := range params.Objects {
if !strings.HasPrefix(object, ctrl.kodo.baseUrl) {
err := fmt.Errorf("unrecognized object key: %s", object)
logger.Printf("%v", err)
return nil, err
}
u, err := url.Parse(object)
if err != nil {
logger.Printf("failed to parse object key: %s: %v", object, err)
return nil, err
}
u.Fragment = ""

// INFO: Workaround for browser caching issue with signed URLs, causing redundant downloads.
now := time.Now().UTC()
e := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).Unix() + expires

if u.RawQuery != "" {
u.RawQuery += "&"
}
u.RawQuery += fmt.Sprintf("e=%d", e)

objectURL := u.String()
objectURL += "&token=" + ctrl.kodo.cred.Sign([]byte(objectURL))
fileURLs.ObjectURLs[object] = objectURL
}
return fileURLs, nil
}
11 changes: 10 additions & 1 deletion spx-gui/src/apis/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @desc util-related APIs of spx-backend
*/

import { client } from './common'
import { client, type FileUrls } from './common'

export interface FormatError {
column: number
Expand Down Expand Up @@ -35,3 +35,12 @@ export type UpInfo = {
export function getUpInfo() {
return client.get('/util/upinfo') as Promise<UpInfo>
}

export type DownloadableFileUrls = {
/** Map from object keys to signed URLs for the objects */
objectUrls: FileUrls
}

export function makeDownloadableFileUrls(objects: string[]) {
return client.post('/util/fileurls', { objects: objects }) as Promise<DownloadableFileUrls>
}
6 changes: 3 additions & 3 deletions spx-gui/src/models/common/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function sprite2Asset(sprite: Sprite): Promise<PartialAssetData> {
}

export async function asset2Sprite(assetData: PartialAssetData) {
const files = getFiles(assetData.files)
const files = await getFiles(assetData.files)
const sprites = await Sprite.loadAll(files)
if (sprites.length === 0) throw new Error('no sprite loaded')
return sprites[0]
Expand All @@ -49,7 +49,7 @@ export async function backdrop2Asset(backdrop: Backdrop): Promise<PartialAssetDa
}

export async function asset2Backdrop(assetData: PartialAssetData) {
const files = getFiles(assetData.files)
const files = await getFiles(assetData.files)
const configFile = files[virtualBackdropConfigFileName]
if (configFile == null) throw new Error('no config file found')
const config = (await toConfig(configFile)) as BackdropInits
Expand All @@ -66,7 +66,7 @@ export async function sound2Asset(sound: Sound): Promise<PartialAssetData> {
}

export async function asset2Sound(assetData: PartialAssetData) {
const files = getFiles(assetData.files)
const files = await getFiles(assetData.files)
const sounds = await Sound.loadAll(files)
if (sounds.length === 0) throw new Error('no sound loaded')
return sounds[0]
Expand Down
20 changes: 13 additions & 7 deletions spx-gui/src/models/common/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { filename } from '@/utils/path'
import { File, toNativeFile, type Files } from './file'
import type { FileCollection, ProjectData } from '@/apis/project'
import { IsPublic, addProject, getProject, updateProject } from '@/apis/project'
import { getUpInfo as getRawUpInfo, type UpInfo as RawUpInfo } from '@/apis/util'
import {
getUpInfo as getRawUpInfo,
type UpInfo as RawUpInfo,
makeDownloadableFileUrls
} from '@/apis/util'
import { DefaultException } from '@/utils/exception'
import type { Metadata } from '../project'

export async function load(owner: string, name: string) {
const projectData = await getProject(owner, name)
return parseProjectData(projectData)
return await parseProjectData(projectData)
}

export async function save(metadata: Metadata, files: Files) {
Expand All @@ -21,11 +25,11 @@ export async function save(metadata: Metadata, files: Files) {
const projectData = await (id != null
? updateProject(owner, name, { isPublic, files: fileUrls })
: addProject({ name, isPublic, files: fileUrls }))
return parseProjectData(projectData)
return await parseProjectData(projectData)
}

export function parseProjectData({ files: fileUrls, ...metadata }: ProjectData) {
const files = getFiles(fileUrls)
export async function parseProjectData({ files: fileUrls, ...metadata }: ProjectData) {
const files = await getFiles(fileUrls)
return { metadata, files }
}

Expand All @@ -40,10 +44,12 @@ export async function uploadFiles(files: Files): Promise<FileCollection> {
return fileUrls
}

export function getFiles(fileUrls: FileCollection): Files {
export async function getFiles(fileUrls: FileCollection): Promise<Files> {
const objects = Object.values(fileUrls)
const { objectUrls } = await makeDownloadableFileUrls(objects)
const files: Files = {}
Object.keys(fileUrls).forEach((path) => {
const url = fileUrls[path]
const url = objectUrls[fileUrls[path]]
files[path] = createFileWithUrl(filename(path), url)
})
return files
Expand Down
2 changes: 1 addition & 1 deletion spx-gui/src/models/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class Project extends Disposble {
const { metadata, files } =
typeof ownerOrProjectData === 'string'
? await cloudHelper.load(ownerOrProjectData, name!)
: cloudHelper.parseProjectData(ownerOrProjectData)
: await cloudHelper.parseProjectData(ownerOrProjectData)
await this.load(metadata, files)
}

Expand Down

0 comments on commit 70678e5

Please sign in to comment.