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 13, 2024
1 parent 5157953 commit 834f658
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 30 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)
}

// Sign files for downloading
post "/util/signfiles", ctx => {
if _, ok := ensureUser(ctx); !ok {
return
}
files := &controller.SignedFiles{}
if ok := parseJson(ctx, files); !ok {
return
}
signedFiles, err := ctrl.SignFiles(utils.getCtx(ctx), files)
if err != nil {
handlerInnerError(ctx, err)
return
}
replyWithData(ctx, signedFiles)
}

var err error
logger := log.GetLogger()
ctrl, err = controller.NewController(context.Background())
Expand Down
42 changes: 36 additions & 6 deletions spx-backend/internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"fmt"
"os"
"regexp"
"strings"
"time"

_ "image/png"

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

type kodoConfig struct {
ak string
sk string
cred *qiniuAuth.Credentials
bucket string
bucketRegion string
baseUrl string
Expand All @@ -60,8 +61,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 +522,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 +531,31 @@ func (ctrl *Controller) GetUpInfo(ctx context.Context) (*UpInfo, error) {
BaseUrl: ctrl.kodo.baseUrl,
}, nil
}

type SignedFiles struct {
Files model.FileCollection `json:"files"`
}

func (ctrl *Controller) SignFiles(ctx context.Context, files *SignedFiles) (*SignedFiles, error) {
const expires = 2 * 24 * 3600 // 2 days
logger := log.GetReqLogger(ctx)
signedFiles := &SignedFiles{
Files: make(model.FileCollection, len(files.Files)),
}
for k, v := range files.Files {
if !strings.HasPrefix(v, ctrl.kodo.baseUrl) {
err := fmt.Errorf("unrecognized file url: %s", v)
logger.Printf("%v", err)
return nil, err
}

// 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

signedURL := fmt.Sprintf("%s?e=%d", v, e)
signedURL += "&token=" + ctrl.kodo.cred.Sign([]byte(signedURL))
signedFiles.Files[k] = signedURL
}
return signedFiles, 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 SignedFiles = {
/** Signed files */
files: FileUrls
}

export function signFiles(files: FileUrls) {
return client.post('/util/signfiles', { files: files }) as Promise<SignedFiles>
}
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
17 changes: 9 additions & 8 deletions spx-gui/src/models/common/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ 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, signFiles } 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 +21,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 +40,11 @@ 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 { files: signedFileUrls } = await signFiles(fileUrls)
const files: Files = {}
Object.keys(fileUrls).forEach((path) => {
const url = fileUrls[path]
Object.keys(signedFileUrls).forEach((path) => {
const url = signedFileUrls[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 834f658

Please sign in to comment.