refactor ftp

This commit is contained in:
Ben Allfree 2022-12-31 03:09:33 -08:00
parent f51fc0f413
commit b09014d0a1
5 changed files with 110 additions and 93 deletions

View File

@ -13,4 +13,7 @@ DAEMON_PB_BACKUP_SLEEP=100
DAEMON_PB_BACKUP_PAGE_COUNT=5
SSL_KEY=`pwd`/ssl/pockethost.test.key
SSL_CERT=`pwd`/ssl/pockethost.test.crt
PH_BIN_CACHE=`pwd`/.pbincache
PH_BIN_CACHE=`pwd`/.pbincache
PH_FTP_PASV_IP=0.0.0.0
PH_FTP_PASV_PORT_MIN=10000
PH_FTP_PASV_PORT_MAX=20000

View File

@ -56,3 +56,7 @@ export const PH_FTP_PORT = envi('PH_FTP_PORT', 21)
export const SSL_KEY = env('SSL_KEY')
export const SSL_CERT = env('SSL_CERT')
export const PH_FTP_PASV_IP = env('PH_FTP_PASV_IP', '0.0.0.0')
export const PH_FTP_PASV_PORT_MIN = envi('PH_FTP_PASV_PORT_MIN', 10000)
export const PH_FTP_PASV_PORT_MAX = envi('PH_FTP_PASV_PORT_MAX', 20000)

View File

@ -2,7 +2,7 @@ import { logger } from '@pockethost/common'
import { DEBUG, PH_BIN_CACHE, PUBLIC_PB_SUBDOMAIN } from './constants'
import { clientService } from './db/PbClient'
import { createBackupService } from './services/BackupService'
import { ftpService } from './services/FtpService'
import { ftpService } from './services/FtpService/FtpService'
import { createInstanceService } from './services/InstanceService'
import { pocketbase } from './services/PocketBaseService'
import { createProxyService } from './services/ProxyService'

View File

@ -0,0 +1,90 @@
import { logger, mkSingleton } from '@pockethost/common'
import { readFileSync } from 'fs'
import { FtpSrv } from 'ftp-srv'
import {
PH_FTP_PASV_IP,
PH_FTP_PASV_PORT_MAX,
PH_FTP_PASV_PORT_MIN,
PH_FTP_PORT,
SSL_CERT,
SSL_KEY,
} from '../../constants'
import { clientService, createPbClient } from '../../db/PbClient'
import { PhFs } from './PhFs'
export type FtpConfig = {}
export enum FolderNames {
PbData = 'pb_data',
PbStatic = 'pb_static',
PbWorkers = 'workers',
PbBackup = 'backup',
}
export const README_CONTENTS: { [_ in FolderNames]: string } = {
[FolderNames.PbBackup]: `This directory contains tgz backups of your data. PocketHost creates these automatically, or you can create them manually. For more information, see https://pockethost.io/docs/backups`,
[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.PbWorkers]: `This directory contains your Deno workers. For more information, see https://pockethost.io/docs/workers`,
}
export const README_NAME = 'readme.md'
export const FOLDER_NAMES: FolderNames[] = [
FolderNames.PbBackup,
FolderNames.PbData,
FolderNames.PbStatic,
FolderNames.PbWorkers,
]
export function isFolder(name: string): name is FolderNames {
return FOLDER_NAMES.includes(name as FolderNames)
}
const tls = {
key: readFileSync(SSL_KEY || ''),
cert: readFileSync(SSL_CERT || ''),
}
export const ftpService = mkSingleton((config: FtpConfig) => {
const log = logger().create('FtpService')
const { dbg, info } = log
const ftpServer = new FtpSrv({
url: 'ftp://0.0.0.0:' + PH_FTP_PORT,
anonymous: false,
log: log.create(`ftpServer`, { errorTrace: false }),
tls,
pasv_url: PH_FTP_PASV_IP,
pasv_max: PH_FTP_PASV_PORT_MAX,
pasv_min: PH_FTP_PASV_PORT_MIN,
})
ftpServer.on(
'login',
async ({ connection, username, password }, resolve, reject) => {
const url = (await clientService()).url
const client = createPbClient(url)
try {
await client.client
.collection('users')
.authWithPassword(username, password)
dbg(`Logged in`)
const fs = new PhFs(connection, client)
resolve({ fs })
} catch (e) {
reject(new Error(`Invalid username or password`))
}
}
)
ftpServer.listen().then(() => {
info('Ftp server is starting...')
})
const shutdown = () => {
info(`Closing FTP server`)
ftpServer.close()
}
return { shutdown }
})

View File

@ -1,49 +1,18 @@
import { Logger, logger, mkSingleton } from '@pockethost/common'
import { existsSync, mkdirSync, readFileSync } from 'fs'
import { FileStat, FileSystem, FtpConnection, FtpSrv } from 'ftp-srv'
import { Logger, logger } from '@pockethost/common'
import { existsSync, mkdirSync } from 'fs'
import { FileStat, FileSystem, FtpConnection } from 'ftp-srv'
import { join } from 'path'
import { Readable } from 'stream'
import { DAEMON_PB_DATA_DIR } from '../../constants'
import { PocketbaseClientApi } from '../../db/PbClient'
import {
DAEMON_PB_DATA_DIR,
PH_FTP_PORT,
SSL_CERT,
SSL_KEY,
} from '../constants'
import {
clientService,
createPbClient,
PocketbaseClientApi,
} from '../db/PbClient'
FOLDER_NAMES,
isFolder,
README_CONTENTS,
README_NAME,
} from './FtpService'
export type FtpConfig = {}
export enum FolderNames {
PbData = 'pb_data',
PbStatic = 'pb_static',
PbWorkers = 'workers',
PbBackup = 'backup',
}
const README_CONTENTS: { [_ in FolderNames]: string } = {
[FolderNames.PbBackup]: `This directory contains tgz backups of your data. PocketHost creates these automatically, or you can create them manually. For more information, see https://pockethost.io/docs/backups`,
[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.PbWorkers]: `This directory contains your Deno workers. For more information, see https://pockethost.io/docs/workers`,
}
const README_NAME = 'readme.md'
const FOLDER_NAMES: FolderNames[] = [
FolderNames.PbBackup,
FolderNames.PbData,
FolderNames.PbStatic,
FolderNames.PbWorkers,
]
function isFolder(name: string): name is FolderNames {
return FOLDER_NAMES.includes(name as FolderNames)
}
class PhFs extends FileSystem {
export class PhFs extends FileSystem {
private log: Logger
private client: PocketbaseClientApi
@ -264,52 +233,3 @@ class PhFs extends FileSystem {
throw new Error(`getUniqueName ${fileName}`)
}
}
const tls = {
key: readFileSync(SSL_KEY || ''),
cert: readFileSync(SSL_CERT || ''),
}
export const ftpService = mkSingleton((config: FtpConfig) => {
const log = logger().create('FtpService')
const { dbg, info } = log
const ftpServer = new FtpSrv({
url: 'ftp://0.0.0.0:' + PH_FTP_PORT,
anonymous: false,
log: log.create(`ftpServer`, { errorTrace: false }),
tls,
pasv_url: `147.182.196.168`,
pasv_max: 20000,
pasv_min: 10000,
})
ftpServer.on(
'login',
async ({ connection, username, password }, resolve, reject) => {
const url = (await clientService()).url
const client = createPbClient(url)
try {
await client.client
.collection('users')
.authWithPassword(username, password)
dbg(`Logged in`)
const fs = new PhFs(connection, client)
resolve({ fs })
} catch (e) {
reject(new Error(`Invalid username or password`))
}
}
)
ftpServer.listen().then(() => {
info('Ftp server is starting...')
})
const shutdown = () => {
info(`Closing FTP server`)
ftpServer.close()
}
return { shutdown }
})