Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"stream-concat": "^1.0.0",
"tar": "^7.5.11",
"uint8arrays": "^4.0.6",
"unique-names-generator": "^4.7.1",
"url-join": "^5.0.0",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1",
Expand Down
9 changes: 9 additions & 0 deletions src/@types/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,15 @@ export interface PersistentStorageCreateBucketCommand extends Command {
signature: string
nonce: string
accessLists: AccessList[]
label?: string
}

export interface PersistentStorageUpdateBucketCommand extends Command {
consumerAddress: string
signature: string
nonce: string
bucketId: string
label?: string
}

export interface PersistentStorageGetBucketsCommand extends Command {
Expand Down
5 changes: 5 additions & 0 deletions src/components/core/handler/coreHandlersRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
PersistentStorageGetBucketsHandler,
PersistentStorageGetFileObjectHandler,
PersistentStorageListFilesHandler,
PersistentStorageUpdateBucketHandler,
PersistentStorageUploadFileHandler
} from './persistentStorage.js'
import { GetAccessListHandler, SearchAccessListHandler } from './accessListHandler.js'
Expand Down Expand Up @@ -181,6 +182,10 @@ export class CoreHandlersRegistry {
PROTOCOL_COMMANDS.PERSISTENT_STORAGE_CREATE_BUCKET,
new PersistentStorageCreateBucketHandler(node)
)
this.registerCoreHandler(
PROTOCOL_COMMANDS.PERSISTENT_STORAGE_UPDATE_BUCKET,
new PersistentStorageUpdateBucketHandler(node)
)
this.registerCoreHandler(
PROTOCOL_COMMANDS.PERSISTENT_STORAGE_GET_BUCKETS,
new PersistentStorageGetBucketsHandler(node)
Expand Down
77 changes: 76 additions & 1 deletion src/components/core/handler/persistentStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
PersistentStorageGetBucketsCommand,
PersistentStorageGetFileObjectCommand,
PersistentStorageListFilesCommand,
PersistentStorageUpdateBucketCommand,
PersistentStorageUploadFileCommand
} from '../../../@types/commands.js'
import {
Expand All @@ -23,6 +24,21 @@ import {
} from '../../httpRoutes/validateCommands.js'
import { CommandHandler } from './handler.js'

const MAX_BUCKET_LABEL_LENGTH = 256

function validateOptionalLabel(label: unknown): ValidateParams | null {
if (label === undefined || label === null) return null
if (typeof label !== 'string') {
return buildInvalidRequestMessage('Invalid parameter: "label" must be a string')
}
if (label.length > MAX_BUCKET_LABEL_LENGTH) {
return buildInvalidRequestMessage(
`Invalid parameter: "label" must be at most ${MAX_BUCKET_LABEL_LENGTH} characters`
)
}
return null
}

function requirePersistentStorage(handler: CommandHandler): PersistentStorageFactory {
const node = handler.getOceanNode() as any
if (!node.getPersistentStorage) {
Expand All @@ -44,6 +60,8 @@ export class PersistentStorageCreateBucketHandler extends CommandHandler {
'Invalid parameter: "accessLists" must be an array of objects'
)
}
const labelError = validateOptionalLabel(command.label)
if (labelError) return labelError
return { valid: true }
}

Expand Down Expand Up @@ -97,7 +115,11 @@ export class PersistentStorageCreateBucketHandler extends CommandHandler {
}
}

const result = await storage.createNewBucket(task.accessLists, ownerNormalized)
const result = await storage.createNewBucket(
task.accessLists,
ownerNormalized,
task.label
)
return {
stream: Readable.from(JSON.stringify(result)),
status: { httpStatus: 200, error: null }
Expand All @@ -110,6 +132,59 @@ export class PersistentStorageCreateBucketHandler extends CommandHandler {
}
}

export class PersistentStorageUpdateBucketHandler extends CommandHandler {
validate(command: PersistentStorageUpdateBucketCommand): ValidateParams {
const base = validateCommandParameters(command, ['bucketId'])
if (!base.valid) return base
if (!command.bucketId || typeof command.bucketId !== 'string') {
return buildInvalidRequestMessage('Invalid parameter: "bucketId" must be a string')
}
const labelError = validateOptionalLabel(command.label)
if (labelError) return labelError
return { valid: true }
}

async handle(task: PersistentStorageUpdateBucketCommand): Promise<P2PCommandResponse> {
const validationResponse = await this.verifyParamsAndRateLimits(task)
if (this.shouldDenyTaskHandling(validationResponse)) return validationResponse

const isAuthRequestValid = await this.validateTokenOrSignature(
task.authorization,
task.consumerAddress,
task.nonce,
task.signature,
task.command
)
if (isAuthRequestValid.status.httpStatus !== 200) return isAuthRequestValid

try {
const storage = requirePersistentStorage(this)
const ownerNormalized = task.consumerAddress
? getAddress(task.consumerAddress)
: getAddress(await this.getAddressFromToken(task.authorization))
const label = await storage.updateBucketLabel(
task.bucketId,
task.label,
ownerNormalized
)
return {
stream: Readable.from(JSON.stringify({ bucketId: task.bucketId, label })),
status: { httpStatus: 200, error: null }
}
} catch (e) {
if (e instanceof PersistentStorageAccessDeniedError) {
return { stream: null, status: { httpStatus: 403, error: e.message } }
}
const message = e instanceof Error ? e.message : String(e)
if (message.toLowerCase().includes('not found')) {
return { stream: null, status: { httpStatus: 404, error: message } }
}
CORE_LOGGER.error(`PersistentStorageUpdateBucketHandler error: ${message}`)
return { stream: null, status: { httpStatus: 500, error: message } }
}
}
}

export class PersistentStorageGetBucketsHandler extends CommandHandler {
validate(command: PersistentStorageGetBucketsCommand): ValidateParams {
const base = validateCommandParameters(command, ['owner'])
Expand Down
32 changes: 32 additions & 0 deletions src/components/httpRoutes/persistentStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PersistentStorageGetBucketsHandler,
PersistentStorageGetFileObjectHandler,
PersistentStorageListFilesHandler,
PersistentStorageUpdateBucketHandler,
PersistentStorageUploadFileHandler
} from '../core/handler/persistentStorage.js'

Expand Down Expand Up @@ -43,6 +44,37 @@ persistentStorageRoutes.post(
}
)

// Update bucket (rename / set label)
persistentStorageRoutes.patch(
`${SERVICES_API_BASE_PATH}/persistentStorage/buckets/:bucketId`,
express.json(),
async (req, res) => {
try {
const response = await new PersistentStorageUpdateBucketHandler(
req.oceanNode
).handle({
command: PROTOCOL_COMMANDS.PERSISTENT_STORAGE_UPDATE_BUCKET,
consumerAddress: req.query.consumerAddress as string,
signature: req.query.signature as string,
nonce: req.query.nonce as string,
bucketId: req.params.bucketId,
label: req.body?.label,
authorization: req.headers?.authorization,
caller: req.caller
} as any)
if (!response.stream) {
res.status(response.status.httpStatus).send(response.status.error)
return
}
const payload = await streamToObject(response.stream as Readable)
res.status(200).json(payload)
} catch (error) {
HTTP_LOGGER.error(`PersistentStorage update bucket error: ${error}`)
res.status(500).send('Internal Server Error')
}
}
)

// List buckets for an owner (then filtered by ACL in handler)
persistentStorageRoutes.get(
`${SERVICES_API_BASE_PATH}/persistentStorage/buckets`,
Expand Down
Loading
Loading