mirror of
https://github.com/laurent22/joplin.git
synced 2024-11-27 08:21:03 +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',
|
username: () => 'admin@localhost',
|
||||||
password: () => 'admin',
|
password: () => 'admin',
|
||||||
});
|
});
|
||||||
fileApi = new FileApi(`root:/Apps/Joplin-${suiteName}`, new FileApiDriverJoplinServer(api));
|
fileApi = new FileApi(`Apps/Joplin-${suiteName}`, new FileApiDriverJoplinServer(api));
|
||||||
}
|
}
|
||||||
|
|
||||||
fileApi.setLogger(logger);
|
fileApi.setLogger(logger);
|
||||||
|
@ -31,6 +31,7 @@ export default class JoplinServerApi {
|
|||||||
|
|
||||||
private options_: Options;
|
private options_: Options;
|
||||||
private session_: any;
|
private session_: any;
|
||||||
|
private debugRequests_: boolean = false;
|
||||||
|
|
||||||
public constructor(options: Options) {
|
public constructor(options: Options) {
|
||||||
this.options_ = options;
|
this.options_ = options;
|
||||||
@ -73,22 +74,22 @@ export default class JoplinServerApi {
|
|||||||
return `${this.baseUrl()}/shares/${share.id}`;
|
return `${this.baseUrl()}/shares/${share.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private requestToCurl_(url: string, options: any) {
|
private requestToCurl_(url: string, options: any) {
|
||||||
// const output = [];
|
const output = [];
|
||||||
// output.push('curl');
|
output.push('curl');
|
||||||
// output.push('-v');
|
output.push('-v');
|
||||||
// if (options.method) output.push(`-X ${options.method}`);
|
if (options.method) output.push(`-X ${options.method}`);
|
||||||
// if (options.headers) {
|
if (options.headers) {
|
||||||
// for (const n in options.headers) {
|
for (const n in options.headers) {
|
||||||
// if (!options.headers.hasOwnProperty(n)) continue;
|
if (!options.headers.hasOwnProperty(n)) continue;
|
||||||
// output.push(`${'-H ' + '"'}${n}: ${options.headers[n]}"`);
|
output.push(`${'-H ' + '"'}${n}: ${options.headers[n]}"`);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// if (options.body) output.push(`${'--data ' + '\''}${JSON.stringify(options.body)}'`);
|
if (options.body) output.push(`${'--data ' + '\''}${JSON.stringify(options.body)}'`);
|
||||||
// output.push(url);
|
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) {
|
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 = {};
|
if (headers === null) headers = {};
|
||||||
@ -128,8 +129,10 @@ export default class JoplinServerApi {
|
|||||||
|
|
||||||
let response: any = null;
|
let response: any = null;
|
||||||
|
|
||||||
// console.info('Joplin API Call', `${method} ${url}`, headers, options);
|
if (this.debugRequests_) {
|
||||||
// console.info(this.requestToCurl_(url, fetchOptions));
|
console.info('Joplin API Call', `${method} ${url}`, headers, options);
|
||||||
|
console.info(this.requestToCurl_(url, fetchOptions));
|
||||||
|
}
|
||||||
|
|
||||||
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
|
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
|
||||||
if (fetchOptions.path) {
|
if (fetchOptions.path) {
|
||||||
|
@ -48,7 +48,7 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
|||||||
|
|
||||||
const api = new JoplinServerApi(apiOptions);
|
const api = new JoplinServerApi(apiOptions);
|
||||||
const driver = new FileApiDriverJoplinServer(api);
|
const driver = new FileApiDriverJoplinServer(api);
|
||||||
const fileApi = new FileApi(() => `root:/${options.directory()}`, driver);
|
const fileApi = new FileApi(options.directory, driver);
|
||||||
fileApi.setSyncTargetId(this.id());
|
fileApi.setSyncTargetId(this.id());
|
||||||
await fileApi.initialize();
|
await fileApi.initialize();
|
||||||
return fileApi;
|
return fileApi;
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
import JoplinServerApi from './JoplinServerApi';
|
import JoplinServerApi from './JoplinServerApi';
|
||||||
const { dirname, basename } = require('./path-utils');
|
import { trimSlashes, dirname, basename } from './path-utils';
|
||||||
|
|
||||||
function removeTrailingColon(path: string) {
|
// All input paths should be in the format: "path/to/file". This is converted to
|
||||||
if (!path || !path.length) return '';
|
// "root:/path/to/file:" when doing the API call.
|
||||||
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"
|
|
||||||
|
|
||||||
export default class FileApiDriverJoplinServer {
|
export default class FileApiDriverJoplinServer {
|
||||||
|
|
||||||
@ -21,19 +13,16 @@ export default class FileApiDriverJoplinServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async initialize(basePath: string) {
|
public async initialize(basePath: string) {
|
||||||
const pieces = removeTrailingColon(basePath).split('/');
|
const pieces = trimSlashes(basePath).split('/');
|
||||||
if (!pieces.length) return;
|
if (!pieces.length) return;
|
||||||
|
|
||||||
let parent = pieces.splice(0, 1)[0];
|
const parent: string[] = [];
|
||||||
|
|
||||||
for (const p of pieces) {
|
for (let i = 0; i < pieces.length; i++) {
|
||||||
// Syncing with the root, which is ok, and in that
|
const p = pieces[i];
|
||||||
// case there's no sub-dir to create.
|
const subPath = parent.concat(p).join('/');
|
||||||
if (!p && pieces.length === 1) return;
|
parent.push(p);
|
||||||
|
|
||||||
const subPath = `${parent}/${p}`;
|
|
||||||
await this.mkdir(subPath);
|
await this.mkdir(subPath);
|
||||||
parent = subPath;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,9 +56,10 @@ export default class FileApiDriverJoplinServer {
|
|||||||
return output;
|
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) {
|
private apiFilePath_(p: string) {
|
||||||
if (p !== 'root') p += ':';
|
return `api/files/root:/${trimSlashes(p)}:`;
|
||||||
return `api/files/${p}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stat(path: string) {
|
public async stat(path: string) {
|
||||||
@ -145,14 +135,7 @@ export default class FileApiDriverJoplinServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parentPath_(path: string) {
|
private parentPath_(path: string) {
|
||||||
let output = dirname(path);
|
return dirname(path);
|
||||||
|
|
||||||
// This is the root or a special folder
|
|
||||||
if (output.split('/').length === 1) {
|
|
||||||
output = output.substr(0, output.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private basename_(path: string) {
|
private basename_(path: string) {
|
||||||
|
@ -138,6 +138,10 @@ export function ltrimSlashes(path: string) {
|
|||||||
return path.replace(/^\/+/, '');
|
return path.replace(/^\/+/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trimSlashes(path: string): string {
|
||||||
|
return ltrimSlashes(rtrimSlashes(path));
|
||||||
|
}
|
||||||
|
|
||||||
export function quotePath(path: string) {
|
export function quotePath(path: string) {
|
||||||
if (!path) return '';
|
if (!path) return '';
|
||||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
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 ('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;
|
return file;
|
||||||
|
Loading…
Reference in New Issue
Block a user