mirror of
https://github.com/laurent22/joplin.git
synced 2024-11-27 08:21:03 +02:00
Merge branch 'dev' of github.com:laurent22/joplin into dev
This commit is contained in:
commit
6632d67a64
5245
packages/app-cli/package-lock.json
generated
5245
packages/app-cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -42,7 +42,6 @@
|
||||
"dependencies": {
|
||||
"@joplin/lib": "2.1",
|
||||
"@joplin/renderer": "2.1",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"chalk": "^4.1.0",
|
||||
"compare-version": "^0.1.2",
|
||||
"fs-extra": "^5.0.0",
|
||||
|
@ -191,9 +191,11 @@ export default class NoteListUtils {
|
||||
const location = info.view.location;
|
||||
if (location !== MenuItemLocation.Context && location !== MenuItemLocation.NoteListContextMenu) continue;
|
||||
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName, noteIds))
|
||||
);
|
||||
if (cmdService.isEnabled(info.view.commandName)) {
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName, noteIds))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
// So there's basically still a one way flux: React => SQLite => Redux => React
|
||||
|
||||
// For aws-sdk-js-v3
|
||||
import 'react-native-get-random-values';
|
||||
import 'react-native-url-polyfill/auto';
|
||||
|
||||
import { LogBox, AppRegistry } from 'react-native';
|
||||
const Root = require('./root').default;
|
||||
|
||||
|
@ -263,6 +263,8 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-geolocation (2.0.2):
|
||||
- React
|
||||
- react-native-get-random-values (1.7.0):
|
||||
- React-Core
|
||||
- react-native-image-picker (2.3.4):
|
||||
- React-Core
|
||||
- react-native-image-resizer (1.3.0):
|
||||
@ -404,6 +406,7 @@ DEPENDENCIES:
|
||||
- react-native-camera (from `../node_modules/react-native-camera`)
|
||||
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
|
||||
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
- react-native-image-resizer (from `../node_modules/react-native-image-resizer`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
@ -491,6 +494,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-document-picker"
|
||||
react-native-geolocation:
|
||||
:path: "../node_modules/@react-native-community/geolocation"
|
||||
react-native-get-random-values:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-image-resizer:
|
||||
@ -581,6 +586,7 @@ SPEC CHECKSUMS:
|
||||
react-native-camera: 5c1fbfecf63b802b8ca4a71c60d30a71550fb348
|
||||
react-native-document-picker: b3e78a8f7fef98b5cb069f20fc35797d55e68e28
|
||||
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
|
||||
react-native-get-random-values: 237bffb1c7e05fb142092681531810a29ba53015
|
||||
react-native-image-picker: 32d1ad2c0024ca36161ae0d5c2117e2d6c441f11
|
||||
react-native-image-resizer: b53bf95ad880100e20262687e41f76fdbc9df255
|
||||
react-native-netinfo: 34f4d7a42f49157f3b45c14217d256bce7dc9682
|
||||
|
57
packages/app-mobile/package-lock.json
generated
57
packages/app-mobile/package-lock.json
generated
@ -3872,6 +3872,11 @@
|
||||
"time-stamp": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fast-base64-decode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
|
||||
"integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="
|
||||
},
|
||||
"fb-watchman": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
|
||||
@ -7835,6 +7840,11 @@
|
||||
"escape-goat": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@ -8106,6 +8116,14 @@
|
||||
"utf8": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"react-native-get-random-values": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.7.0.tgz",
|
||||
"integrity": "sha512-zDhmpWUekGRFb9I+MQkxllHcqXN9HBSsgPwBQfrZ1KZYpzDspWLZ6/yLMMZrtq4pVqNR7C7N96L3SuLpXv1nhQ==",
|
||||
"requires": {
|
||||
"fast-base64-decode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"react-native-image-picker": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-2.3.4.tgz",
|
||||
@ -8169,6 +8187,14 @@
|
||||
"resolved": "https://registry.npmjs.org/react-native-sqlite-storage/-/react-native-sqlite-storage-5.0.0.tgz",
|
||||
"integrity": "sha512-c1Joq3/tO1nmIcP8SkRZNolPSbfvY8uZg5lXse0TmjIPC0qHVbk96IMvWGyly1TmYCIpxpuDRc0/xCffDbYIvg=="
|
||||
},
|
||||
"react-native-url-polyfill": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz",
|
||||
"integrity": "sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ==",
|
||||
"requires": {
|
||||
"whatwg-url-without-unicode": "8.0.0-3"
|
||||
}
|
||||
},
|
||||
"react-native-vector-icons": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-7.1.0.tgz",
|
||||
@ -9799,6 +9825,22 @@
|
||||
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
|
||||
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
|
||||
},
|
||||
"url": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
||||
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
||||
"requires": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@ -9965,11 +10007,26 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
|
||||
"integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz",
|
||||
"integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ=="
|
||||
},
|
||||
"whatwg-url-without-unicode": {
|
||||
"version": "8.0.0-3",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz",
|
||||
"integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==",
|
||||
"requires": {
|
||||
"buffer": "^5.4.3",
|
||||
"punycode": "^2.1.1",
|
||||
"webidl-conversions": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
|
@ -39,6 +39,7 @@
|
||||
"react-native-dropdownalert": "^3.1.2",
|
||||
"react-native-file-viewer": "^2.1.4",
|
||||
"react-native-fs": "^2.16.6",
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-image-picker": "^2.3.4",
|
||||
"react-native-image-resizer": "^1.3.0",
|
||||
"react-native-modal-datetime-picker": "^9.0.0",
|
||||
@ -49,6 +50,7 @@
|
||||
"react-native-share": "^5.1.5",
|
||||
"react-native-side-menu": "^1.1.3",
|
||||
"react-native-sqlite-storage": "^5.0.0",
|
||||
"react-native-url-polyfill": "^1.3.0",
|
||||
"react-native-vector-icons": "^7.1.0",
|
||||
"react-native-version-info": "^1.1.0",
|
||||
"react-native-webview": "^10.9.2",
|
||||
@ -59,6 +61,7 @@
|
||||
"stream-browserify": "^3.0.0",
|
||||
"string-natural-compare": "^2.0.2",
|
||||
"timers": "^0.1.1",
|
||||
"url": "^0.11.0",
|
||||
"valid-url": "^1.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,8 +1,6 @@
|
||||
import FsDriverBase from '@joplin/lib/fs-driver-base';
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
const RNFS = require('react-native-fs');
|
||||
const { Writable } = require('stream-browserify');
|
||||
const { Buffer } = require('buffer');
|
||||
|
||||
export default class FsDriverRN extends FsDriverBase {
|
||||
public appendFileSync() {
|
||||
@ -26,27 +24,6 @@ export default class FsDriverRN extends FsDriverBase {
|
||||
return await this.unlink(path);
|
||||
}
|
||||
|
||||
public writeBinaryFile(path: string, content: any) {
|
||||
const buffer = Buffer.from(content);
|
||||
return RNFetchBlob.fs.writeStream(path, 'base64').then((stream: any) => {
|
||||
const fileStream = new Writable({
|
||||
write(chunk: any, _encoding: any, callback: Function) {
|
||||
this.stream.write(chunk.toString('base64'));
|
||||
callback();
|
||||
},
|
||||
final(callback: Function) {
|
||||
this.stream.close();
|
||||
callback();
|
||||
},
|
||||
});
|
||||
// using options.construct is not implemented in readable-stream so lets
|
||||
// pass the stream from RNFetchBlob to the Writable instance here
|
||||
fileStream.stream = stream;
|
||||
fileStream.write(buffer);
|
||||
fileStream.end();
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a format compatible with Node.js format
|
||||
private rnfsStatToStd_(stat: any, path: string) {
|
||||
return {
|
||||
|
@ -4,7 +4,7 @@ const Setting = require('./models/Setting').default;
|
||||
const { FileApi } = require('./file-api.js');
|
||||
const Synchronizer = require('./Synchronizer').default;
|
||||
const { FileApiDriverAmazonS3 } = require('./file-api-driver-amazon-s3.js');
|
||||
const S3 = require('aws-sdk/clients/s3');
|
||||
const { S3Client, HeadBucketCommand } = require('@aws-sdk/client-s3');
|
||||
|
||||
class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
static id() {
|
||||
@ -38,10 +38,14 @@ class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
|
||||
s3AuthParameters() {
|
||||
return {
|
||||
accessKeyId: Setting.value('sync.8.username'),
|
||||
secretAccessKey: Setting.value('sync.8.password'),
|
||||
s3UseArnRegion: true, // override the request region with the region inferred from requested resource's ARN
|
||||
s3ForcePathStyle: true,
|
||||
// We need to set a region. See https://github.com/aws/aws-sdk-js-v3/issues/1845#issuecomment-754832210
|
||||
region: 'us-east-1',
|
||||
credentials: {
|
||||
accessKeyId: Setting.value('sync.8.username'),
|
||||
secretAccessKey: Setting.value('sync.8.password'),
|
||||
},
|
||||
UseArnRegion: true, // override the request region with the region inferred from requested resource's ARN
|
||||
forcePathStyle: true,
|
||||
endpoint: Setting.value('sync.8.url'),
|
||||
};
|
||||
}
|
||||
@ -49,20 +53,31 @@ class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
api() {
|
||||
if (this.api_) return this.api_;
|
||||
|
||||
this.api_ = new S3(this.s3AuthParameters());
|
||||
this.api_ = new S3Client(this.s3AuthParameters());
|
||||
|
||||
// There is a bug with auto skew correction in aws-sdk-js-v3
|
||||
// and this attempts to remove the skew correction for all calls.
|
||||
// There are some additional spots in the app where we reset this
|
||||
// to zero as well as it appears the skew logic gets triggered
|
||||
// which makes "RequestTimeTooSkewed" errors...
|
||||
// See https://github.com/aws/aws-sdk-js-v3/issues/2208
|
||||
this.api_.config.systemClockOffset = 0;
|
||||
return this.api_;
|
||||
}
|
||||
|
||||
static async newFileApi_(syncTargetId, options) {
|
||||
const apiOptions = {
|
||||
accessKeyId: options.username(),
|
||||
secretAccessKey: options.password(),
|
||||
s3UseArnRegion: true,
|
||||
s3ForcePathStyle: true,
|
||||
region: 'us-east-1',
|
||||
credentials: {
|
||||
accessKeyId: options.username(),
|
||||
secretAccessKey: options.password(),
|
||||
},
|
||||
UseArnRegion: true, // override the request region with the region inferred from requested resource's ARN
|
||||
forcePathStyle: true,
|
||||
endpoint: options.url(),
|
||||
};
|
||||
|
||||
const api = new S3(apiOptions);
|
||||
const api = new S3Client(apiOptions);
|
||||
const driver = new FileApiDriverAmazonS3(api, SyncTargetAmazonS3.s3BucketName());
|
||||
const fileApi = new FileApi('', driver);
|
||||
fileApi.setSyncTargetId(syncTargetId);
|
||||
@ -80,12 +95,14 @@ class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
|
||||
try {
|
||||
const headBucketReq = new Promise((resolve, reject) => {
|
||||
fileApi.driver().api().headBucket({
|
||||
Bucket: options.path(),
|
||||
},(err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
fileApi.driver().api().send(
|
||||
|
||||
new HeadBucketCommand({
|
||||
Bucket: options.path(),
|
||||
}),(err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
});
|
||||
const result = await headBucketReq;
|
||||
if (!result) throw new Error(`AWS S3 bucket not found: ${SyncTargetAmazonS3.s3BucketName()}`);
|
||||
|
@ -85,7 +85,6 @@ shared.saveSettings = function(comp) {
|
||||
for (const key in comp.state.settings) {
|
||||
if (!comp.state.settings.hasOwnProperty(key)) continue;
|
||||
if (comp.state.changedSettingKeys.indexOf(key) < 0) continue;
|
||||
console.info('Saving', key, comp.state.settings[key]);
|
||||
Setting.setValue(key, comp.state.settings[key]);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ const { basename } = require('./path-utils');
|
||||
const shim = require('./shim').default;
|
||||
const JoplinError = require('./JoplinError').default;
|
||||
const { Buffer } = require('buffer');
|
||||
const { GetObjectCommand, ListObjectsV2Command, HeadObjectCommand, PutObjectCommand, DeleteObjectCommand, DeleteObjectsCommand, CopyObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
|
||||
|
||||
const S3_MAX_DELETES = 1000;
|
||||
|
||||
@ -26,31 +28,33 @@ class FileApiDriverAmazonS3 {
|
||||
}
|
||||
|
||||
hasErrorCode_(error, errorCode) {
|
||||
if (!error || typeof error.code !== 'string') return false;
|
||||
return error.code.indexOf(errorCode) >= 0;
|
||||
if (!error || typeof error.name !== 'string') return false;
|
||||
return error.name.indexOf(errorCode) >= 0;
|
||||
}
|
||||
|
||||
// Need to make a custom promise, built-in promise is broken: https://github.com/aws/aws-sdk-js/issues/1436
|
||||
async s3GetObject(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().getObject({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
}, (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
// Because of the way AWS-SDK-v3 works for getting data from a bucket we will
|
||||
// use a pre-signed URL to avoid https://github.com/aws/aws-sdk-js-v3/issues/1877
|
||||
async s3GenerateGetURL(key) {
|
||||
const signedUrl = await getSignedUrl(this.api(), new GetObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
}), {
|
||||
expiresIn: 3600,
|
||||
});
|
||||
return signedUrl;
|
||||
}
|
||||
|
||||
// We've now moved to aws-sdk-v3 and this note is outdated, but explains the promise structure.
|
||||
// Need to make a custom promise, built-in promise is broken: https://github.com/aws/aws-sdk-js/issues/1436
|
||||
// TODO: Re-factor to https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3#asyncawait
|
||||
async s3ListObjects(key, cursor) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().listObjectsV2({
|
||||
this.api().send(new ListObjectsV2Command({
|
||||
Bucket: this.s3_bucket_,
|
||||
Prefix: key,
|
||||
Delimiter: '/',
|
||||
ContinuationToken: cursor,
|
||||
}, (err, response) => {
|
||||
}), (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -59,10 +63,10 @@ class FileApiDriverAmazonS3 {
|
||||
|
||||
async s3HeadObject(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().headObject({
|
||||
this.api().send(new HeadObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
}, (err, response) => {
|
||||
}), (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -71,11 +75,11 @@ class FileApiDriverAmazonS3 {
|
||||
|
||||
async s3PutObject(key, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().putObject({
|
||||
this.api().send(new PutObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
Body: body,
|
||||
}, (err, response) => {
|
||||
}), (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -87,12 +91,12 @@ class FileApiDriverAmazonS3 {
|
||||
const body = await shim.fsDriver().readFile(path, 'base64');
|
||||
const fileStat = await shim.fsDriver().stat(path);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().putObject({
|
||||
this.api().send(new PutObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
Body: Buffer.from(body, 'base64'),
|
||||
ContentLength: `${fileStat.size}`,
|
||||
}, (err, response) => {
|
||||
}), (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -101,10 +105,10 @@ class FileApiDriverAmazonS3 {
|
||||
|
||||
async s3DeleteObject(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().deleteObject({
|
||||
this.api().send(new DeleteObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Key: key,
|
||||
},
|
||||
}),
|
||||
(err, response) => {
|
||||
if (err) {
|
||||
console.log(err.code);
|
||||
@ -118,10 +122,10 @@ class FileApiDriverAmazonS3 {
|
||||
// Assumes key is formatted, like `{Key: 's3 path'}`
|
||||
async s3DeleteObjects(keys) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api().deleteObjects({
|
||||
this.api().send(new DeleteObjectsCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
Delete: { Objects: keys },
|
||||
},
|
||||
}),
|
||||
(err, response) => {
|
||||
if (err) {
|
||||
console.log(err.code);
|
||||
@ -188,8 +192,20 @@ class FileApiDriverAmazonS3 {
|
||||
prefixPath = `${prefixPath}/`;
|
||||
}
|
||||
|
||||
// There is a bug/quirk of aws-sdk-js-v3 which causes the
|
||||
// S3Client systemClockOffset to be wildly inaccurate. This
|
||||
// effectively removes the offset and sets it to system time.
|
||||
// See https://github.com/aws/aws-sdk-js-v3/issues/2208 for more.
|
||||
// If the user's time actaully off, then this should correctly
|
||||
// result in a RequestTimeTooSkewed error from s3ListObjects.
|
||||
this.api().config.systemClockOffset = 0;
|
||||
|
||||
let response = await this.s3ListObjects(prefixPath);
|
||||
|
||||
// In aws-sdk-js-v3 if there are no contents it no longer returns
|
||||
// an empty array. This creates an Empty array to pass onward.
|
||||
if (response.Contents === undefined) response.Contents = [];
|
||||
|
||||
let output = this.metadataToStats_(response.Contents, prefixPath);
|
||||
|
||||
while (response.IsTruncated) {
|
||||
@ -212,31 +228,17 @@ class FileApiDriverAmazonS3 {
|
||||
|
||||
try {
|
||||
let output = null;
|
||||
const response = await this.s3GetObject(remotePath);
|
||||
output = response.Body;
|
||||
let response = null;
|
||||
|
||||
const s3Url = await this.s3GenerateGetURL(remotePath);
|
||||
|
||||
if (options.target === 'file') {
|
||||
const filePath = options.path;
|
||||
if (!filePath) throw new Error('get: target options.path is missing');
|
||||
|
||||
// TODO: check if this ever hits on RN
|
||||
await shim.fsDriver().writeBinaryFile(filePath, output);
|
||||
return {
|
||||
ok: true,
|
||||
path: filePath,
|
||||
text: () => {
|
||||
return response.statusMessage;
|
||||
},
|
||||
json: () => {
|
||||
return { message: `${response.statusCode}: ${response.statusMessage}` };
|
||||
},
|
||||
status: response.statusCode,
|
||||
headers: response.headers,
|
||||
};
|
||||
output = await shim.fetchBlob(s3Url, options);
|
||||
}
|
||||
|
||||
if (responseFormat === 'text') {
|
||||
output = output.toString();
|
||||
response = await shim.fetch(s3Url, options);
|
||||
output = await response.text();
|
||||
}
|
||||
|
||||
return output;
|
||||
@ -310,11 +312,11 @@ class FileApiDriverAmazonS3 {
|
||||
|
||||
async move(oldPath, newPath) {
|
||||
const req = new Promise((resolve, reject) => {
|
||||
this.api().copyObject({
|
||||
this.api().send(new CopyObjectCommand({
|
||||
Bucket: this.s3_bucket_,
|
||||
CopySource: this.makePath_(oldPath),
|
||||
Key: newPath,
|
||||
},(err, response) => {
|
||||
}),(err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -340,10 +342,10 @@ class FileApiDriverAmazonS3 {
|
||||
async clearRoot() {
|
||||
const listRecursive = async (cursor) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.api().listObjectsV2({
|
||||
return this.api().send(new ListObjectsV2Command({
|
||||
Bucket: this.s3_bucket_,
|
||||
ContinuationToken: cursor,
|
||||
}, (err, response) => {
|
||||
}), (err, response) => {
|
||||
if (err) reject(err);
|
||||
else resolve(response);
|
||||
});
|
||||
@ -351,6 +353,9 @@ class FileApiDriverAmazonS3 {
|
||||
};
|
||||
|
||||
let response = await listRecursive();
|
||||
// In aws-sdk-js-v3 if there are no contents it no longer returns
|
||||
// an empty array. This creates an Empty array to pass onward.
|
||||
if (response.Contents === undefined) response.Contents = [];
|
||||
let keys = response.Contents.map((content) => content.Key);
|
||||
|
||||
while (response.IsTruncated) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
class FsDriverDummy {
|
||||
constructor() {}
|
||||
appendFileSync() {}
|
||||
writeBinaryFile() {}
|
||||
readFile() {}
|
||||
}
|
||||
|
||||
|
@ -26,16 +26,6 @@ export default class FsDriverNode extends FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
public async writeBinaryFile(path: string, content: any) {
|
||||
try {
|
||||
// let buffer = new Buffer(content);
|
||||
const buffer = Buffer.from(content);
|
||||
return await fs.writeFile(path, buffer);
|
||||
} catch (error) {
|
||||
throw this.fsErrorToJsError_(error, path);
|
||||
}
|
||||
}
|
||||
|
||||
public async writeFile(path: string, string: string, encoding: string = 'base64') {
|
||||
try {
|
||||
if (encoding === 'buffer') {
|
||||
|
@ -245,10 +245,6 @@ export default class Resource extends BaseItem {
|
||||
return this.fsDriver().readFile(this.fullPath(resource), 'Buffer');
|
||||
}
|
||||
|
||||
static setContent(resource: ResourceEntity, content: any) {
|
||||
return this.fsDriver().writeBinaryFile(this.fullPath(resource), content);
|
||||
}
|
||||
|
||||
static isResourceUrl(url: string) {
|
||||
return url && url.length === 34 && url[0] === ':' && url[1] === '/';
|
||||
}
|
||||
|
1148
packages/lib/package-lock.json
generated
1148
packages/lib/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,8 @@
|
||||
"@joplin/turndown": "^4.0.50",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.32",
|
||||
"async-mutex": "^0.1.3",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"@aws-sdk/client-s3": "^3.22.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.23.0",
|
||||
"base-64": "^0.1.0",
|
||||
"base64-stream": "^1.0.0",
|
||||
"builtin-modules": "^3.1.0",
|
||||
|
@ -55,7 +55,7 @@ import SyncTargetJoplinCloud from '../SyncTargetJoplinCloud';
|
||||
import KeychainService from '../services/keychain/KeychainService';
|
||||
import { loadKeychainServiceAndSettings } from '../services/SettingUtils';
|
||||
const md5 = require('md5');
|
||||
const S3 = require('aws-sdk/clients/s3');
|
||||
const { S3Client } = require('@aws-sdk/client-s3');
|
||||
const { Dirnames } = require('../services/synchronizer/utils/types');
|
||||
|
||||
// Each suite has its own separate data and temp directory so that multiple
|
||||
@ -569,10 +569,16 @@ async function initFileApi() {
|
||||
const appDir = await api.appDirectory();
|
||||
fileApi = new FileApi(appDir, new FileApiDriverOneDrive(api));
|
||||
} else if (syncTargetId_ == SyncTargetRegistry.nameToId('amazon_s3')) {
|
||||
|
||||
// We make sure for S3 tests run in band because tests
|
||||
// share the same directory which will cause locking errors.
|
||||
|
||||
mustRunInBand();
|
||||
|
||||
const amazonS3CredsPath = `${oldTestDir}/support/amazon-s3-auth.json`;
|
||||
const amazonS3Creds = require(amazonS3CredsPath);
|
||||
if (!amazonS3Creds || !amazonS3Creds.accessKeyId) throw new Error(`AWS auth JSON missing in ${amazonS3CredsPath} format should be: { "accessKeyId": "", "secretAccessKey": "", "bucket": "mybucket"}`);
|
||||
const api = new S3({ accessKeyId: amazonS3Creds.accessKeyId, secretAccessKey: amazonS3Creds.secretAccessKey, s3UseArnRegion: true });
|
||||
const api = new S3Client({ region: 'us-east-1', accessKeyId: amazonS3Creds.accessKeyId, secretAccessKey: amazonS3Creds.secretAccessKey, s3UseArnRegion: true });
|
||||
fileApi = new FileApi('', new FileApiDriverAmazonS3(api, amazonS3Creds.bucket));
|
||||
} else if (syncTargetId_ == SyncTargetRegistry.nameToId('joplinServer')) {
|
||||
mustRunInBand();
|
||||
|
Loading…
Reference in New Issue
Block a user