mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Fixes #4426: Improved handling of empty paths for Joplin Server sync target
This commit is contained in:
parent
a419e1eb7c
commit
b1b5069a23
@ -552,7 +552,7 @@ async function initFileApi(suiteName: string) {
|
||||
username: () => 'admin@localhost',
|
||||
password: () => 'admin',
|
||||
});
|
||||
fileApi = new FileApi(`root:/Apps/Joplin-${suiteName}`, new FileApiDriverJoplinServer(api));
|
||||
fileApi = new FileApi(`Apps/Joplin-${suiteName}`, new FileApiDriverJoplinServer(api));
|
||||
}
|
||||
|
||||
fileApi.setLogger(logger);
|
||||
|
@ -31,6 +31,7 @@ export default class JoplinServerApi {
|
||||
|
||||
private options_: Options;
|
||||
private session_: any;
|
||||
private debugRequests_: boolean = false;
|
||||
|
||||
public constructor(options: Options) {
|
||||
this.options_ = options;
|
||||
@ -73,22 +74,22 @@ export default class JoplinServerApi {
|
||||
return `${this.baseUrl()}/shares/${share.id}`;
|
||||
}
|
||||
|
||||
// private requestToCurl_(url: string, options: any) {
|
||||
// const output = [];
|
||||
// output.push('curl');
|
||||
// output.push('-v');
|
||||
// if (options.method) output.push(`-X ${options.method}`);
|
||||
// if (options.headers) {
|
||||
// for (const n in options.headers) {
|
||||
// if (!options.headers.hasOwnProperty(n)) continue;
|
||||
// output.push(`${'-H ' + '"'}${n}: ${options.headers[n]}"`);
|
||||
// }
|
||||
// }
|
||||
// if (options.body) output.push(`${'--data ' + '\''}${JSON.stringify(options.body)}'`);
|
||||
// output.push(url);
|
||||
private requestToCurl_(url: string, options: any) {
|
||||
const output = [];
|
||||
output.push('curl');
|
||||
output.push('-v');
|
||||
if (options.method) output.push(`-X ${options.method}`);
|
||||
if (options.headers) {
|
||||
for (const n in options.headers) {
|
||||
if (!options.headers.hasOwnProperty(n)) continue;
|
||||
output.push(`${'-H ' + '"'}${n}: ${options.headers[n]}"`);
|
||||
}
|
||||
}
|
||||
if (options.body) output.push(`${'--data ' + '\''}${JSON.stringify(options.body)}'`);
|
||||
output.push(url);
|
||||
|
||||
// return output.join(' ');
|
||||
// }
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
public async exec(method: string, path: string = '', query: Record<string, any> = null, body: any = null, headers: any = null, options: ExecOptions = null) {
|
||||
if (headers === null) headers = {};
|
||||
@ -128,8 +129,10 @@ export default class JoplinServerApi {
|
||||
|
||||
let response: any = null;
|
||||
|
||||
// console.info('Joplin API Call', `${method} ${url}`, headers, options);
|
||||
// console.info(this.requestToCurl_(url, fetchOptions));
|
||||
if (this.debugRequests_) {
|
||||
console.info('Joplin API Call', `${method} ${url}`, headers, options);
|
||||
console.info(this.requestToCurl_(url, fetchOptions));
|
||||
}
|
||||
|
||||
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
|
||||
if (fetchOptions.path) {
|
||||
|
@ -48,7 +48,7 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||
|
||||
const api = new JoplinServerApi(apiOptions);
|
||||
const driver = new FileApiDriverJoplinServer(api);
|
||||
const fileApi = new FileApi(() => `root:/${options.directory()}`, driver);
|
||||
const fileApi = new FileApi(options.directory, driver);
|
||||
fileApi.setSyncTargetId(this.id());
|
||||
await fileApi.initialize();
|
||||
return fileApi;
|
||||
|
@ -1,16 +1,8 @@
|
||||
import JoplinServerApi from './JoplinServerApi';
|
||||
const { dirname, basename } = require('./path-utils');
|
||||
import { trimSlashes, dirname, basename } from './path-utils';
|
||||
|
||||
function removeTrailingColon(path: string) {
|
||||
if (!path || !path.length) return '';
|
||||
if (path[path.length - 1] === ':') return path.substr(0, path.length - 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
// All input paths should be in the format: "SPECIAL_DIR:/path/to/file"
|
||||
// The trailing colon must not be included as it's automatically added
|
||||
// when doing the API call.
|
||||
// Only supported special dir at the moment is "root"
|
||||
// All input paths should be in the format: "path/to/file". This is converted to
|
||||
// "root:/path/to/file:" when doing the API call.
|
||||
|
||||
export default class FileApiDriverJoplinServer {
|
||||
|
||||
@ -21,19 +13,16 @@ export default class FileApiDriverJoplinServer {
|
||||
}
|
||||
|
||||
public async initialize(basePath: string) {
|
||||
const pieces = removeTrailingColon(basePath).split('/');
|
||||
const pieces = trimSlashes(basePath).split('/');
|
||||
if (!pieces.length) return;
|
||||
|
||||
let parent = pieces.splice(0, 1)[0];
|
||||
const parent: string[] = [];
|
||||
|
||||
for (const p of pieces) {
|
||||
// Syncing with the root, which is ok, and in that
|
||||
// case there's no sub-dir to create.
|
||||
if (!p && pieces.length === 1) return;
|
||||
|
||||
const subPath = `${parent}/${p}`;
|
||||
for (let i = 0; i < pieces.length; i++) {
|
||||
const p = pieces[i];
|
||||
const subPath = parent.concat(p).join('/');
|
||||
parent.push(p);
|
||||
await this.mkdir(subPath);
|
||||
parent = subPath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,9 +56,10 @@ export default class FileApiDriverJoplinServer {
|
||||
return output;
|
||||
}
|
||||
|
||||
// Transforms a path such as "Apps/Joplin/file.txt" to a complete a complete
|
||||
// API URL path: "api/files/root:/Apps/Joplin/file.txt:"
|
||||
private apiFilePath_(p: string) {
|
||||
if (p !== 'root') p += ':';
|
||||
return `api/files/${p}`;
|
||||
return `api/files/root:/${trimSlashes(p)}:`;
|
||||
}
|
||||
|
||||
public async stat(path: string) {
|
||||
@ -145,14 +135,7 @@ export default class FileApiDriverJoplinServer {
|
||||
}
|
||||
|
||||
private parentPath_(path: string) {
|
||||
let output = dirname(path);
|
||||
|
||||
// This is the root or a special folder
|
||||
if (output.split('/').length === 1) {
|
||||
output = output.substr(0, output.length - 1);
|
||||
}
|
||||
|
||||
return output;
|
||||
return dirname(path);
|
||||
}
|
||||
|
||||
private basename_(path: string) {
|
||||
|
@ -138,6 +138,10 @@ export function ltrimSlashes(path: string) {
|
||||
return path.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
export function trimSlashes(path: string): string {
|
||||
return ltrimSlashes(rtrimSlashes(path));
|
||||
}
|
||||
|
||||
export function quotePath(path: string) {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
||||
|
@ -269,7 +269,7 @@ export default class FileModel extends BaseModel<File> {
|
||||
}
|
||||
|
||||
if ('name' in file) {
|
||||
if (this.includesReservedCharacter(file.name)) throw new ErrorUnprocessableEntity(`File name may not contain any of these characters: ${this.reservedCharacters.join('')}`);
|
||||
if (this.includesReservedCharacter(file.name)) throw new ErrorUnprocessableEntity(`File name may not contain any of these characters: ${this.reservedCharacters.join('')}: ${file.name}`);
|
||||
}
|
||||
|
||||
return file;
|
||||
|
Loading…
Reference in New Issue
Block a user