configure meta-fuse to connect to meta-core
This commit is contained in:
@@ -242,8 +242,8 @@ WEBDAV_USERNAME=metamesh # WebDAV username
|
||||
WEBDAV_PASSWORD=metamesh # WebDAV password
|
||||
WEBDAV_READONLY=true # Read-only mode
|
||||
|
||||
# meta-sort WebDAV Access (reading files from meta-sort)
|
||||
META_SORT_WEBDAV_URL=http://meta-sort-dev/webdav # URL to access files via meta-sort's WebDAV
|
||||
# meta-core WebDAV Access (reading files from meta-core)
|
||||
META_CORE_WEBDAV_URL=http://meta-core/webdav # URL to access files via meta-core's WebDAV
|
||||
# Enables access to SMB/rclone mounts
|
||||
```
|
||||
|
||||
@@ -276,8 +276,8 @@ services:
|
||||
- FILES_VOLUME=/files
|
||||
- FUSE_MOUNT_POINT=/mnt/virtual
|
||||
- REDIS_PREFIX=meta-sort:
|
||||
# Optional: Read files via meta-sort WebDAV (enables SMB/rclone mount access)
|
||||
- META_SORT_WEBDAV_URL=http://meta-sort/webdav
|
||||
# Optional: Read files via meta-core WebDAV (enables SMB/rclone mount access)
|
||||
- META_CORE_WEBDAV_URL=http://meta-core/webdav
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
@@ -57,13 +57,13 @@ async function main(): Promise<void> {
|
||||
vfsRefreshInterval: parseInt(process.env.VFS_REFRESH_INTERVAL ?? '30000', 10),
|
||||
baseUrl: process.env.BASE_URL,
|
||||
serviceVersion: process.env.SERVICE_VERSION ?? '1.0.0',
|
||||
// WebDAV URL for meta-sort file access (e.g., http://meta-sort-dev/webdav)
|
||||
metaSortWebdavUrl: process.env.META_SORT_WEBDAV_URL,
|
||||
// WebDAV URL for meta-core file access (e.g., http://meta-core/webdav)
|
||||
metaCoreWebdavUrl: process.env.META_CORE_WEBDAV_URL,
|
||||
};
|
||||
|
||||
logger.info(`Config: META_CORE_PATH=${config.metaCorePath}, FILES_VOLUME=${config.filesPath}`);
|
||||
if (config.metaSortWebdavUrl) {
|
||||
logger.info(`WebDAV: Using meta-sort WebDAV at ${config.metaSortWebdavUrl}`);
|
||||
if (config.metaCoreWebdavUrl) {
|
||||
logger.info(`WebDAV: Using meta-core WebDAV at ${config.metaCoreWebdavUrl}`);
|
||||
} else {
|
||||
logger.info(`WebDAV: Not configured, FUSE driver will use local filesystem`);
|
||||
}
|
||||
@@ -125,7 +125,7 @@ async function main(): Promise<void> {
|
||||
gid: parseInt(process.env.PGID ?? '1000', 10),
|
||||
refreshInterval: config.vfsRefreshInterval,
|
||||
configDir: process.env.CONFIG_DIR ?? '/meta-fuse/config',
|
||||
webdavBaseUrl: config.metaSortWebdavUrl,
|
||||
webdavBaseUrl: config.metaCoreWebdavUrl,
|
||||
filesPath: config.filesPath,
|
||||
});
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ export class VirtualFileSystem {
|
||||
// Compute virtual path using rules or fallback
|
||||
const virtualPath = this.computeVirtualPath(metadata, sourcePath);
|
||||
if (virtualPath) {
|
||||
this.addFile(virtualPath, metadata);
|
||||
await this.addFile(virtualPath, metadata);
|
||||
this.sourcePathToVirtualPath.set(sourcePath, virtualPath);
|
||||
}
|
||||
}
|
||||
@@ -325,7 +325,7 @@ export class VirtualFileSystem {
|
||||
}
|
||||
|
||||
// Add/update file
|
||||
this.addFile(virtualPath, metadata);
|
||||
await this.addFile(virtualPath, metadata);
|
||||
this.sourcePathToVirtualPath.set(metadata.sourcePath, virtualPath);
|
||||
|
||||
logger.debug(`File ${action}ed: ${virtualPath}`);
|
||||
@@ -334,7 +334,7 @@ export class VirtualFileSystem {
|
||||
/**
|
||||
* Add a file to the VFS
|
||||
*/
|
||||
private addFile(virtualPath: string, metadata: FileMetadata): void {
|
||||
private async addFile(virtualPath: string, metadata: FileMetadata): Promise<void> {
|
||||
virtualPath = this.normalizePath(virtualPath);
|
||||
|
||||
// Skip if file already exists at this path
|
||||
@@ -363,8 +363,20 @@ export class VirtualFileSystem {
|
||||
mtime = stats.mtime;
|
||||
ctime = stats.ctime;
|
||||
} catch {
|
||||
logger.warn(`Cannot stat source file: ${metadata.sourcePath}`);
|
||||
return;
|
||||
// Local stat failed, try WebDAV if configured
|
||||
if (this.config.webdavBaseUrl) {
|
||||
const webdavStats = await this.statViaWebDAV(metadata.sourcePath);
|
||||
if (webdavStats) {
|
||||
size = webdavStats.size;
|
||||
mtime = webdavStats.mtime || mtime;
|
||||
} else {
|
||||
logger.warn(`Cannot stat source file via WebDAV: ${metadata.sourcePath}`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.warn(`Cannot stat source file: ${metadata.sourcePath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +404,49 @@ export class VirtualFileSystem {
|
||||
this.cachedStats.totalSize += size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file stats via WebDAV HEAD request
|
||||
*/
|
||||
private async statViaWebDAV(sourcePath: string): Promise<{ size: number; mtime?: Date } | null> {
|
||||
if (!this.config.webdavBaseUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert source path to WebDAV URL
|
||||
let relativePath = sourcePath;
|
||||
if (this.config.filesPath && sourcePath.startsWith(this.config.filesPath)) {
|
||||
relativePath = sourcePath.slice(this.config.filesPath.length);
|
||||
}
|
||||
if (!relativePath.startsWith('/')) {
|
||||
relativePath = '/' + relativePath;
|
||||
}
|
||||
|
||||
// URL-encode path segments (but not slashes)
|
||||
const encodedPath = relativePath.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
||||
const webdavUrl = this.config.webdavBaseUrl + encodedPath;
|
||||
|
||||
// Make HEAD request
|
||||
const response = await fetch(webdavUrl, { method: 'HEAD' });
|
||||
|
||||
if (response.ok) {
|
||||
const contentLength = response.headers.get('Content-Length');
|
||||
const lastModified = response.headers.get('Last-Modified');
|
||||
|
||||
return {
|
||||
size: contentLength ? parseInt(contentLength, 10) : 0,
|
||||
mtime: lastModified ? new Date(lastModified) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug(`WebDAV HEAD failed for ${webdavUrl}: ${response.status}`);
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
logger.debug(`WebDAV stat error for ${sourcePath}: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file from the VFS
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user