fix: FTP access to deep subdirectories and pb_migrations

This commit is contained in:
Ben Allfree 2023-06-07 23:40:49 -07:00
parent 04deecf8fd
commit a601e53430
2 changed files with 51 additions and 41 deletions

View File

@ -17,6 +17,7 @@ export type FtpConfig = {}
export enum FolderNames { export enum FolderNames {
PbData = 'pb_data', PbData = 'pb_data',
PbStatic = 'pb_static', PbStatic = 'pb_static',
PbMigrations = 'pb_migrations',
PbWorker = 'worker', PbWorker = 'worker',
PbBackup = 'backup', PbBackup = 'backup',
} }
@ -26,6 +27,7 @@ export const README_CONTENTS: { [_ in FolderNames]: string } = {
[FolderNames.PbData]: `This directory contains your PocketBase data. For more information, see https://pockethost.io/docs/data`, [FolderNames.PbData]: `This directory contains your PocketBase data. For more information, see https://pockethost.io/docs/data`,
[FolderNames.PbStatic]: `This directory contains static files such as your web frontend. PocketHost will serve these when your instance URL receives a request. For more information, see https://pockethost.io/docs/static `, [FolderNames.PbStatic]: `This directory contains static files such as your web frontend. PocketHost will serve these when your instance URL receives a request. For more information, see https://pockethost.io/docs/static `,
[FolderNames.PbWorker]: `This directory contains your Deno worker. For more information, see https://pockethost.io/docs/workers`, [FolderNames.PbWorker]: `This directory contains your Deno worker. For more information, see https://pockethost.io/docs/workers`,
[FolderNames.PbMigrations]: `This directory contains your migrations. For more information, see https://pockethost.io/docs/migrations`,
} }
export const README_NAME = 'readme.md' export const README_NAME = 'readme.md'
@ -34,6 +36,7 @@ export const FOLDER_NAMES: FolderNames[] = [
FolderNames.PbData, FolderNames.PbData,
FolderNames.PbStatic, FolderNames.PbStatic,
FolderNames.PbWorker, FolderNames.PbWorker,
FolderNames.PbMigrations,
] ]
export function isFolder(name: string): name is FolderNames { export function isFolder(name: string): name is FolderNames {

View File

@ -47,8 +47,8 @@ export class PhFs extends FileSystem {
throw new Error(`Expected path`) throw new Error(`Expected path`)
} }
const _path = path.startsWith('/') ? path : join(this.cwd, path) const _path = path.startsWith('/') ? path : join(this.cwd, path)
const [empty, subdomain, folderName] = _path.split('/') const [empty, subdomain, folderName, ...restOfPath] = _path.split('/')
this.log.dbg({ _path, subdomain, folderName }) this.log.dbg({ _path, subdomain, folderName, restOfPath })
if (subdomain === '') { if (subdomain === '') {
const instances = await this.client.getInstances() const instances = await this.client.getInstances()
@ -62,10 +62,10 @@ export class PhFs extends FileSystem {
} }
}) })
} }
if (subdomain) { if (!subdomain) {
const [instance, user] = await this.client.getInstanceBySubdomain( throw new Error(`Subdomain expected in ${_path}`)
subdomain }
) const [instance, user] = await this.client.getInstanceBySubdomain(subdomain)
if (!instance) { if (!instance) {
throw new Error(`Expected instance here`) throw new Error(`Expected instance here`)
} }
@ -78,10 +78,14 @@ export class PhFs extends FileSystem {
name: name, name: name,
})) }))
} }
if (isFolder(folderName)) { if (!isFolder(folderName)) {
const dir = join(DAEMON_PB_DATA_DIR, instance.id, folderName) throw new Error(`Top level folder name ${folderName} not allowed.`)
}
const dir = join(DAEMON_PB_DATA_DIR, instance.id, folderName, ...restOfPath)
this.log.dbg({ dir, exists: existsSync(dir) }) this.log.dbg({ dir, exists: existsSync(dir) })
return [ return [
...(restOfPath.length === 0
? [
{ {
isDirectory: () => false, isDirectory: () => false,
mode: 0o444, mode: 0o444,
@ -89,22 +93,17 @@ export class PhFs extends FileSystem {
mtime: Date.parse(instance.updated), mtime: Date.parse(instance.updated),
name: README_NAME, name: README_NAME,
}, },
...(existsSync(dir)
? await super.list(
join(DAEMON_PB_DATA_DIR, instance.id, folderName)
)
: []),
] ]
} : []),
} ...(existsSync(dir) ? await super.list(dir) : []),
throw new Error(`Error parsing ${_path}`) ]
} }
async get(fileName: string): Promise<FileStat> { async get(fileName: string): Promise<FileStat> {
const _path = fileName.startsWith('/') ? fileName : join(this.cwd, fileName) const path = fileName.startsWith('/') ? fileName : join(this.cwd, fileName)
const [empty, subdomain, folderName, ...fNames] = _path.split('/') const [empty, subdomain, folderName, ...fNames] = path.split('/')
const fName = fNames.join('/') const fName = fNames.join('/')
this.log.dbg(`get`, { _path, subdomain, folderName, fName, fileName }) this.log.dbg(`get`, { _path: path, subdomain, folderName, fName, fileName })
if (!subdomain) { if (!subdomain) {
return { return {
@ -140,7 +139,15 @@ export class PhFs extends FileSystem {
name: folderName, name: folderName,
} }
} }
return super.get(_path) const physicalPath = join(
DAEMON_PB_DATA_DIR,
instance.id,
folderName,
fName
)
this.log.dbg({ physicalPath, exists: existsSync(physicalPath) })
return super.get(physicalPath)
} }
async write( async write(