feat(sync): make basic new sync work

This commit is contained in:
Johannes Millan 2025-07-18 16:05:43 +02:00
parent ddfa4ddb15
commit 9bee38597c
5 changed files with 47 additions and 36 deletions

3
.gitignore vendored
View file

@ -105,5 +105,8 @@ perf-metrics-initial-load.json
# bundled plugins (generated by build process)
src/assets/bundled-plugins/**/*.*
# testing webdav server
e2e-webdav-data
electron-builder-appx.yaml

View file

@ -18,6 +18,7 @@ module.exports = {
baseUrlInput: '.e2e-baseUrl input',
userNameInput: '.e2e-userName input',
passwordInput: '.e2e-password input',
syncFolder: '.e2e-syncFolderPath input',
saveBtn: 'mat-dialog-actions button[mat-stroked-button]',
};
@ -31,6 +32,7 @@ module.exports = {
.setValue(sel.baseUrlInput, 'http://localhost:2345')
.setValue(sel.userNameInput, 'alice')
.setValue(sel.passwordInput, 'alice')
.setValue(sel.syncFolder, '/')
.pause(100)
.click(sel.saveBtn);

View file

@ -128,6 +128,7 @@ export const SYNC_FORM: ConfigFormSection<SyncConfig> = {
{
key: 'syncFolderPath',
type: 'input',
className: 'e2e-syncFolderPath',
templateOptions: {
required: true,
label: T.F.SYNC.FORM.WEB_DAV.L_SYNC_FOLDER_PATH,

View file

@ -49,9 +49,9 @@ export class WebdavApi {
}
// If PROPFIND fails or returns no data, try HEAD request as fallback
if (useGetFallback) {
return await this._getFileMetaViaHead(fullPath);
}
// if (useGetFallback) {
// return await this._getFileMetaViaHead(fullPath);
// }
throw new RemoteFileNotFoundAPIError(path);
} catch (e) {
@ -244,38 +244,6 @@ export class WebdavApi {
});
}
private async _getFileMetaViaHead(fullPath: string): Promise<FileMeta> {
const response = await this._makeRequest({
url: fullPath,
method: 'HEAD',
});
const etag = response.headers['etag'] || response.headers['ETag'];
const lastModified =
response.headers['last-modified'] || response.headers['Last-Modified'];
const contentLength =
response.headers['content-length'] || response.headers['Content-Length'];
const contentType =
response.headers['content-type'] || response.headers['Content-Type'];
if (!lastModified) {
throw new InvalidDataSPError('No Last-Modified header in HEAD response');
}
// Extract filename from path
const filename = fullPath.split('/').pop() || '';
return {
filename,
basename: filename,
lastmod: lastModified,
size: parseInt(contentLength || '0', 10),
type: contentType || 'application/octet-stream',
etag: etag ? this._cleanRev(etag) : '',
data: {},
};
}
private async _ensureParentDirectory(fullPath: string): Promise<void> {
const pathParts = fullPath.split('/');
pathParts.pop(); // Remove filename
@ -319,4 +287,36 @@ export class WebdavApi {
PFLog.verbose(`${WebdavApi.L}.cleanRev() "${rev}" -> "${result}"`);
return result;
}
private async _getFileMetaViaHead(fullPath: string): Promise<FileMeta> {
const response = await this._makeRequest({
url: fullPath,
method: 'HEAD',
});
const etag = response.headers['etag'] || response.headers['ETag'];
const lastModified =
response.headers['last-modified'] || response.headers['Last-Modified'];
const contentLength =
response.headers['content-length'] || response.headers['Content-Length'];
const contentType =
response.headers['content-type'] || response.headers['Content-Type'];
if (!lastModified) {
throw new InvalidDataSPError('No Last-Modified header in HEAD response');
}
// Extract filename from path
const filename = fullPath.split('/').pop() || '';
return {
filename,
basename: filename,
lastmod: lastModified,
size: parseInt(contentLength || '0', 10),
type: contentType || 'application/octet-stream',
etag: etag ? this._cleanRev(etag) : '',
data: {},
};
}
}

View file

@ -59,7 +59,12 @@ export class WebDavHttpAdapter {
return response;
} catch (e) {
if (e instanceof AuthFailSPError || e instanceof HttpNotOkAPIError) {
if (
e instanceof AuthFailSPError ||
e instanceof HttpNotOkAPIError ||
e instanceof RemoteFileNotFoundAPIError ||
e instanceof TooManyRequestsAPIError
) {
throw e;
}