🔀 Merge remote-tracking branch 'origin/main' into release/v1.0.5

This commit is contained in:
Eric Doughty-Papassideris
2024-11-18 15:13:02 +01:00
92 changed files with 18492 additions and 12036 deletions
-2
View File
@@ -1,5 +1,3 @@
version: "3.4"
services:
mongo:
container_name: mongo
@@ -70,12 +70,17 @@
}
},
"storage": {
"strategy": "STORAGE_STRATEGY",
"secret": "STORAGE_SECRET",
"iv": "STORAGE_SECRET_BASE_IV",
"type": "STORAGE_DRIVER",
"local": {
"path": "STORAGE_LOCAL_PATH"
},
"oneof": {
"__name": "STORAGE_ONEOF",
"__format": "json"
},
"S3": {
"homeDirectory": "STORAGE_S3_HOME_DIRECTORY",
"bucket": "STORAGE_S3_BUCKET",
+1
View File
@@ -93,6 +93,7 @@
"storage": {
"secret": "0ea28a329df23220fa814e005bfb671c",
"iv": "1234abcd00000000",
"strategy": "local",
"type": "local",
"S3": {
"endPoint": "play.min.io",
+52 -757
View File
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -29,7 +29,7 @@
"test:unit:watch": "npm run test:unit -- --watchAll --verbose false | pino-pretty",
"test:merge:json": "npx istanbul report --dir coverage/merged --include 'coverage/**/coverage-final.json' json-summary",
"test:merge:text": "npx istanbul report --dir coverage/merged --include 'coverage/**/coverage-final.json' text > coverage/merged/coverage-report.txt",
"test:all": "jest test --forceExit --coverage --detectOpenHandles --testTimeout=600000 --verbose false --runInBand",
"test:all": "jest test --forceExit --coverage --detectOpenHandles --testTimeout=600000 --verbose false",
"kill": "kill $(lsof -t -i:3000) | exit 0"
},
"jest": {
@@ -77,6 +77,7 @@
"@types/nodemailer": "^6.4.14",
"@types/pdf-image": "^2.0.1",
"@types/pg-pool": "^2.0.6",
"@types/pino": "^7.0.5",
"@types/pump": "^1.1.1",
"@types/random-useragent": "^0.3.1",
"@types/socket.io-client": "^1.4.36",
@@ -166,8 +167,7 @@
"pdf2pic": "^2.1.4",
"pg": "^8.11.3",
"pg-pool": "^3.6.1",
"pino": "^6.8.0",
"pino-std-serializers": "^6.1.0",
"pino": "^9.5.0",
"probe-image-size": "^7.2.3",
"pump": "^3.0.0",
"random-useragent": "^0.5.0",
@@ -182,7 +182,6 @@
"unzipper": "^0.11.6",
"uuid": "^8.3.2",
"uuid-time": "^1.0.0",
"v8-profiler-next": "^1.10.0",
"yargs": "^16.2.0"
}
}
@@ -12,6 +12,12 @@ export const logger = pino({
mixin() {
return executionStorage.getStore() ? executionStorage.getStore() : {};
},
formatters: {
level(label: string) {
return { level: label.toUpperCase() };
},
},
serializers: pino.stdSerializers,
});
export const getLogger = (name?: string): TdriveLogger =>
@@ -28,7 +28,7 @@ export async function buildDependenciesTree(
`The component dependency ${dependencyName} has not been found for component ${name}`,
);
} else {
console.warn(
logger.warn(
`(warning) The component dependency ${dependencyName} has not been found for component ${name} it will be imported asynchronously`,
);
@@ -209,7 +209,7 @@ export default class MongoSearch extends SearchAdapter implements SearchAdapterI
const { query, sort, project } = buildSearchQuery<EntityType>(entityType, filters, options);
logger.info(`Search query: ${JSON.stringify(query)}`);
console.log(query);
logger.info(query);
const sortMapped: any = sort
? Object.fromEntries(
@@ -1,6 +1,7 @@
import { buildSelectQuery } from "../../../database/services/orm/connectors/mongodb/query-builder";
import { EntityTarget, FindFilter, FindOptions, getEntityDefinition } from "../../api";
import { asciiFold } from "../utils";
import { logger } from "../../../../framework";
export function buildSearchQuery<Entity>(
entityType: EntityTarget<Entity>,
@@ -15,7 +16,7 @@ export function buildSearchQuery<Entity>(
try {
query = buildSelectQuery(entityType, filters, options);
} catch (e) {
console.log(e);
logger.info(e);
}
let sort: any = {};
@@ -2,8 +2,10 @@ import * as Minio from "minio";
import { logger } from "../../../../../../core/platform/framework";
import { Readable } from "stream";
import { StorageConnectorAPI, WriteMetadata } from "../../provider";
import { randomUUID } from "crypto";
export type S3Configuration = {
id: string;
bucket: string;
region: string;
endPoint: string;
@@ -17,10 +19,20 @@ export type S3Configuration = {
export default class S3ConnectorService implements StorageConnectorAPI {
client: Minio.Client;
minioConfiguration: S3Configuration;
id: string;
constructor(S3Configuration: S3Configuration) {
this.client = new Minio.Client(S3Configuration);
this.minioConfiguration = S3Configuration;
this.id = this.minioConfiguration.id;
if (!this.id) {
this.id = randomUUID();
logger.info(`Identifier for S3 storage haven't been set, initializing to '${this.id}'`);
}
}
getId() {
return this.id;
}
write(path: string, stream: Readable): Promise<WriteMetadata> {
@@ -101,7 +113,7 @@ export default class S3ConnectorService implements StorageConnectorAPI {
break;
}
} catch (e) {
logger.error("Error getting information from S3", e);
logger.error(e, `Error getting information from S3 for path: ${path}`);
}
if (i === tries) {
@@ -5,16 +5,28 @@ import { rm } from "fs/promises"; // Do not change the import, this is not the s
import { StorageConnectorAPI, WriteMetadata } from "../../provider";
import fs from "fs";
import { logger } from "../../../../framework/logger";
import { randomUUID } from "crypto";
export type LocalConfiguration = {
id: string;
path: string;
};
export default class LocalConnectorService implements StorageConnectorAPI {
id: string;
configuration: LocalConfiguration;
constructor(localConfiguration: LocalConfiguration) {
this.configuration = localConfiguration;
this.id = this.configuration.id;
if (!this.id) {
this.id = randomUUID();
logger.info(`Identifier for local storage haven't been set, initializing to '${this.id}'`);
}
}
getId() {
return this.id;
}
write(relativePath: string, stream: Readable): Promise<WriteMetadata> {
@@ -0,0 +1,46 @@
import { Readable, Stream } from "stream";
import { ExecutionContext } from "../../framework/api/crud-service";
import {
DeleteOptions,
ReadOptions,
StorageConnectorAPI,
WriteMetadata,
WriteOptions,
} from "../storage/provider";
export class DefaultStorageStrategy implements StorageConnectorAPI {
private connector: StorageConnectorAPI;
constructor(connector: StorageConnectorAPI) {
this.connector = connector;
}
getId() {
return this.connector.getId();
}
write = (
path: string,
stream: Stream,
options?: WriteOptions,
context?: ExecutionContext,
): Promise<WriteMetadata> => {
return this.connector.write(path, stream, options, context);
};
read = (path: string, options?: ReadOptions, context?: ExecutionContext): Promise<Readable> => {
return this.connector.read(path, options, context);
};
exists = (path: string, options?: ReadOptions, context?: ExecutionContext): Promise<boolean> => {
return this.connector.exists(path, options, context);
};
remove = (
path: string,
options?: DeleteOptions,
context?: ExecutionContext,
): Promise<boolean> => {
return this.connector.remove(path, options, context);
};
}
@@ -0,0 +1,2 @@
export class FileNotFountException extends Error {}
export class WriteFileException extends Error {}
@@ -0,0 +1,163 @@
import { Readable, Stream, PassThrough } from "stream";
import {
DeleteOptions,
ReadOptions,
StorageConnectorAPI,
WriteMetadata,
WriteOptions,
} from "../storage/provider";
import { logger } from "../../../platform/framework";
import { FileNotFountException, WriteFileException } from "./exceptions";
/**
* OneOfStorageStrategy is responsible for managing multiple storage backends.
* It writes to, reads from, removes, and checks for the existence of files
* across multiple storage systems.
*
* WARNING: Be careful using this strategy. There is a very big probability that
* all storages won't be synchronized. You need to use external synchronization
* tools like rclone to keep them in sync.
* As far as all the objects in a storage are immutable you won't have inconsistent
* state of the object except it could've been deleted, and it's still exists in one
* of the storages. So you will need to have external procedures to clean up storages
* and make "garbage" collection.
*/
export class OneOfStorageStrategy implements StorageConnectorAPI {
id: string;
constructor(private readonly storages: StorageConnectorAPI[]) {}
getId() {
if (!this.id) {
this.id = this.storages.map(s => s.getId()).join("_");
}
return this.id;
}
/**
* Writes a file to all configured storages.
* The write operation is considered successful if one of the storage succeed.
* @param path - The path where the file should be written.
* @param stream - The input stream to be written to storages.
* @param options - Write option: chunk number, encryption key, etc ...
* @throws Error if the write operation fails for one or more storages.
*/
write = async (path: string, stream: Stream, options?: WriteOptions): Promise<WriteMetadata> => {
logger.debug("Creating streams for all storages ...");
const passThroughStreams = this.storages.map(() => new PassThrough());
// destroy all streams if there is an error in the input stream
stream.on("error", err => {
logger.error(err, "Error in input stream, destroying all write streams");
passThroughStreams.forEach(stream => stream.destroy(err));
});
// Pipe the input stream to each PassThrough stream
stream
.pipe(new PassThrough())
.on("data", chunk => {
passThroughStreams.forEach(stream => stream.write(chunk));
})
.on("end", () => {
passThroughStreams.forEach(stream => stream.end());
});
// Write to all storages with error handling
const writeResults = await Promise.allSettled(
this.storages.map((storage, index) =>
storage.write(path, passThroughStreams[index], options),
),
);
// Log all errors and throw if all write operations fail
const errors = writeResults.filter(result => result.status === "rejected");
errors.forEach((error, index) => {
const storageId = this.storages[index].getId();
logger.error(
new OneOfStorageWriteOneFailedException(
storageId,
`Error writing to storage ${storageId}`,
(error as PromiseRejectedResult).reason,
),
);
});
if (errors.length === this.storages.length) {
throw new WriteFileException(`Write ${path} failed for all storages`);
}
const successResult = writeResults.filter(
result => result.status === "fulfilled",
)[0] as PromiseFulfilledResult<WriteMetadata>;
return successResult.value;
};
/**
* Reads a file from the primary storage. If it fails, attempts to read from other storages.
* @param path - The path of the file to be read.
* @param options
* @returns A readable stream of the file.
* @throws Error if all storage read attempts fail.
*/
read = async (path: string, options?: ReadOptions): Promise<Readable> => {
for (const storage of this.storages) {
try {
if (await storage.exists(path, options)) {
return await storage.read(path, options);
}
} catch (err) {
logger.error(
new OneOfStorageReadOneFailedException(
storage.getId(),
`Reading ${path} from storage ${storage} failed.`,
err,
),
);
}
}
throw new FileNotFountException(`Error reading ${path}`);
};
/**
* Checks if a file exists in any of the configured storages.
* @param path - The path of the file to check.
* @param options
* @returns A boolean indicating whether the file exists in any storage.
*/
exists = async (path: string, options?: ReadOptions): Promise<boolean> => {
for (const storage of this.storages) {
if (await storage.exists(path, options)) {
return true;
}
}
return false;
};
/**
* Removes a file from all configured storages.
* @param path - The path of the file to be removed.
* @param options
*/
remove = async (path: string, options?: DeleteOptions): Promise<boolean> => {
return Promise.all(this.storages.map(storage => storage.remove(path, options))).then(array => {
return array.reduce((a, b) => a && b);
});
};
}
/**
* Throw when read from one of the storages is filed.
*/
class StorageException extends Error {
constructor(readonly storageId: string, details: string, error: Error) {
super(details, error);
}
}
/**
* Throw when read from one of the storages is filed.
*/
class OneOfStorageReadOneFailedException extends StorageException {}
/**
* Thrown when write operation to one of the storages is failed.
*/
class OneOfStorageWriteOneFailedException extends StorageException {}
@@ -23,6 +23,12 @@ export type DeleteOptions = {
};
export interface StorageConnectorAPI {
/**
* Returns identifier of a storage that should've been set in configuration.
*
*/
getId(): string;
/**
* Write a stream to a path
*
@@ -9,7 +9,7 @@ import {
TdriveServiceOptions,
} from "../../framework";
import LocalConnectorService, { LocalConfiguration } from "./connectors/local/service";
import S3ConnectorService from "./connectors/S3/s3-service";
import S3ConnectorService, { S3Configuration } from "./connectors/S3/s3-service";
import StorageAPI, {
DeleteOptions,
ReadOptions,
@@ -18,6 +18,10 @@ import StorageAPI, {
WriteOptions,
} from "./provider";
import { OneOfStorageStrategy } from "./oneof-storage-strategy";
import { DefaultStorageStrategy } from "./default-storage-strategy";
import { FileNotFountException } from "./exceptions";
type EncryptionConfiguration = {
secret: string | null;
iv: string | null;
@@ -35,26 +39,14 @@ export default class StorageService extends TdriveService<StorageAPI> implements
* But for the local storage it's a default value
* @private
*/
private homeDir = "/tdrive";
private homeDir = "tdrive";
constructor(protected options?: TdriveServiceOptions<TdriveServiceConfiguration>) {
super(options);
const type = this.getConnectorType();
if (type === "S3") {
logger.info("Using 'S3' connector for storage.");
try {
this.homeDir = this.configuration.get<string>("S3.homeDirectory");
} catch (e) {
this.logger.warn("Home directory is not set, using S3.bucket instead");
}
if (!this.homeDir) {
this.homeDir = this.configuration.get<string>("S3.bucket");
}
if (this.homeDir && this.homeDir.startsWith("/")) {
this.logger.error("For S3 connector home directory MUST NOT start with '/'");
throw new Error("For S3 connector home directory MUST NOT start with '/'");
}
}
//init home directory variable
this.initHomeDirectory();
//init connector to storage
this.getConnector();
}
api(): StorageAPI {
@@ -67,31 +59,7 @@ export default class StorageService extends TdriveService<StorageAPI> implements
getConnector(): StorageConnectorAPI {
if (!this.connector) {
const type = this.getConnectorType();
if (type === "S3") {
logger.info("Using 'S3' connector for storage.");
this.connector = new S3ConnectorService({
bucket: this.configuration.get<string>("S3.bucket"),
region: this.configuration.get<string>("S3.region"),
endPoint: this.configuration.get<string>("S3.endPoint"),
port: Number(this.configuration.get<number>("S3.port")),
useSSL: Boolean(this.configuration.get<boolean>("S3.useSSL")),
accessKey: this.configuration.get<string>("S3.accessKey"),
secretKey: this.configuration.get<string>("S3.secretKey"),
disableRemove: this.configuration.get<boolean>("S3.disableRemove"),
});
} else {
logger.info(
`Using 'local' connector for storage${
type === "local" ? "" : " (no other connector recognized from configuration type: '%s')"
}.`,
type,
);
logger.trace(`Home directory for the storage: ${this.homeDir}`);
this.connector = new LocalConnectorService(
this.configuration.get<LocalConfiguration>("local"),
);
}
this.connector = this.getStorageStrategy();
}
return this.connector;
}
@@ -101,7 +69,8 @@ export default class StorageService extends TdriveService<StorageAPI> implements
}
exists(path: string, options?: ReadOptions): Promise<boolean> {
return this.getConnector().exists(path, options);
//TODO[ASH] check for all the file chunks
return this.getConnector().exists(path + "/chunk1", options);
}
async write(path: string, stream: Stream, options?: WriteOptions): Promise<WriteMetadata> {
@@ -134,15 +103,15 @@ export default class StorageService extends TdriveService<StorageAPI> implements
}
async read(path: string, options?: ReadOptions): Promise<Readable> {
if (!(await this.exists(path, options))) {
throw new FileNotFountException();
}
try {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const chunks = options?.totalChunks || 1;
let count = 1;
// Check if the first chunk or file exists
await self._read(options?.totalChunks ? `${path}/chunk${count}` : path);
async function factory(callback: (err?: Error, stream?: Stream) => unknown) {
if (count > chunks) {
callback();
@@ -216,4 +185,113 @@ export default class StorageService extends TdriveService<StorageAPI> implements
return this;
}
/**
* Instantiates storage strategy with configuration.
* "storage": {
* "secret": "",
* "iv": "",
* "strategy": "oneof",
* "type": "local",
* "S3": {
* "endPoint": "play.min.io",
* "port": 9000,
* "useSSL": false,
* "accessKey": "ABCD",
* "secretKey": "x1yz",
* "disableRemove": false
* },
* "local": {
* "path": "/tdrive"
* },
* "oneof": [
* {
* "type": "S3",
* "S3": {}
* },
* {
* "type": "local",
* "local": {}
* }]
* },
*/
getStorageStrategy(): StorageConnectorAPI {
const strategy = this.configuration.get<string>("strategy");
if (strategy == "oneof") {
logger.info("Creating storage with 'oneof' strategy");
const connectors = this.configuration
.get<Array<StorageConfiguration>>("oneof")
.map(c => this.createConnector(c));
return new OneOfStorageStrategy(connectors);
} else {
logger.info("Creating storage with 'default' strategy");
return new DefaultStorageStrategy(this.createConnectorFromConfiguration(this.configuration));
}
}
createConnector(config: StorageConfiguration) {
const type = config.type;
if (type === "S3") {
return this.newS3Connector(config.S3);
} else {
return this.newLocalConnector(config.local, type);
}
}
createConnectorFromConfiguration(config: TdriveServiceConfiguration) {
const type = config.get<string>("type");
if (type === "S3") {
return this.newS3Connector(config.get("S3"));
} else {
return this.newLocalConnector(this.configuration.get<LocalConfiguration>("local"), type);
}
}
newS3Connector(config: S3Configuration) {
logger.info("Using 'S3' connector for storage.");
return new S3ConnectorService(config);
}
newLocalConnector(config: LocalConfiguration, type: string) {
logger.info(
`Using 'local' connector for storage${
type === "local" ? "" : " (no other connector recognized from configuration type: '%s')"
}.`,
type,
);
logger.trace(`Home directory for the storage: ${this.homeDir}`);
return new LocalConnectorService(config);
}
initHomeDirectory() {
const type = this.getConnectorType();
if (type === "S3") {
logger.info("Using 'S3' connector for storage.");
try {
this.homeDir = this.configuration.get<string>("S3.homeDirectory");
} catch (e) {
this.logger.warn("Home directory is not set, using S3.bucket instead");
}
if (!this.homeDir) {
this.homeDir = this.configuration.get<string>("S3.bucket");
}
if (this.homeDir && this.homeDir.startsWith("/")) {
this.logger.error("For S3 connector home directory MUST NOT start with '/'");
throw new Error("For S3 connector home directory MUST NOT start with '/'");
}
} else {
this.logger.info("For 'local' connector setting home directory to '/tdrive'");
this.homeDir = "/tdrive";
}
}
getId() {
return this.connector.getId();
}
}
type StorageConfiguration = {
type: string;
S3?: S3Configuration;
local?: LocalConfiguration;
};
@@ -1,6 +1,7 @@
import Analytics, { IdentifyParams, TrackParams } from "@segment/analytics-node";
import { Analytics as AnalyticsAbtract } from "./types";
import axios, { AxiosInstance } from "axios";
import { logger } from "../../../framework/logger";
export default class Segment implements AnalyticsAbtract {
protected version = "5.0.0"; //Segment analytics lib version
@@ -33,7 +34,7 @@ export default class Segment implements AnalyticsAbtract {
try {
this.analytics.identify(message as IdentifyParams, callback);
} catch (err) {
console.error(err);
logger.error(err);
}
}
@@ -47,7 +48,7 @@ export default class Segment implements AnalyticsAbtract {
try {
this.analytics.track(message as TrackParams, callback);
} catch (err) {
console.error(err);
logger.error(err);
}
}
@@ -1,8 +1,9 @@
import { FastifyInstance } from "fastify";
import { logger } from "../../framework/logger";
function serverErrorHandler(server: FastifyInstance): void {
server.setErrorHandler(async (err, request, reply) => {
console.error(`Got ${reply.statusCode} error on request ${request.id} : `, err);
logger.error(`Got ${reply.statusCode} error on request ${request.id} : `, err);
server.log.debug(`Got ${reply.statusCode} error on request ${request.id} : ${err.toString()}`);
await reply.send(
reply.statusCode == 500
@@ -1,4 +1,4 @@
import { Initializable, TdriveServiceProvider } from "../../../core/platform/framework";
import { Initializable, logger, TdriveServiceProvider } from "../../../core/platform/framework";
import { ListResult, Paginable } from "../../../core/platform/framework/api/crud-service";
import gr from "../../global-resolver";
import {
@@ -39,7 +39,7 @@ export class CompanyApplicationServiceImpl implements TdriveServiceProvider, Ini
app.application.api = null;
return app;
} catch (err) {
console.error(err);
logger.error(err);
return null;
}
}
@@ -285,7 +285,7 @@ export const getAccessLevel = async (
);
if (matchingChannel) return matchingChannel.level;
} catch (e) {
console.log(e);
logger.info(e);
}
}
@@ -406,7 +406,7 @@ export const getSharedByUser = (
return getGrantorAndThrowIfEmpty(entity);
}
if (entity.type === "company" && entity.id === context.company.id && entity.level != "none") {
console.log("company", entity);
logger.info("company", entity);
return getGrantorAndThrowIfEmpty(entity);
}
}
@@ -47,7 +47,7 @@ export class DocumentsProcessor
);
content_keywords = await getKeywordsOfFile(storedFile.mime, storedFile.name, storedFile.file);
} catch (error) {
console.debug(error);
logger.debug(error);
logger.error({ error: `${error}` }, "Failed to generate content keywords");
}
return { content_keywords, item: message.item };
@@ -470,7 +470,7 @@ export class DocumentsService {
}
}
} catch (error) {
console.error("🚀🚀 error:", error);
logger.error(error, "🚀🚀 error:");
}
await this.repository.save(driveItem);
@@ -557,6 +557,16 @@ export class DocumentsService {
throw Error("Move operation not permitted");
} else {
oldParent = item.parent_id;
const newParentId = content.parent_id;
const needRenameTo = await getItemName(
newParentId,
item.id,
item.name,
item.is_directory,
this.repository,
context,
);
if (needRenameTo !== item.name) renamedTo = item.name = needRenameTo;
}
if (key === "access_info") {
// if manage access is disabled, we don't allow changing access level
@@ -1,7 +1,6 @@
import { FastifyInstance, FastifyPluginCallback } from "fastify";
import { DocumentsController } from "./controllers";
import { createDocumentSchema, createVersionSchema, beginEditingSchema } from "./schemas";
// import profilerPlugin from "../../../utils/profiler";
const baseUrl = "/companies/:company_id";
const serviceUrl = `${baseUrl}/item`;
@@ -10,11 +9,6 @@ const editingSessionBase = "/editing_session/:editing_session_key";
const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next) => {
const documentsController = new DocumentsController();
// fastify.register(profilerPlugin, {
// active: documentsController.profilingEnabled,
// outputDir: "profiles",
// });
fastify.route({
method: "GET",
url: `${serviceUrl}`,
@@ -431,7 +431,7 @@ export class FileServiceImpl {
id: externalId,
company_id: "00000000-0000-4000-0000-000000000000",
});
const exist = await gr.platformServices.storage.exists(getFilePath(file) + "/chunk1");
const exist = await gr.platformServices.storage.exists(getFilePath(file));
if (exist) {
return { exist: true, file };
} else {
@@ -6,6 +6,7 @@ import { UploadOptions } from "../../types";
import { PublicFile } from "../../entities/file";
import gr from "../../../global-resolver";
import { formatAttachmentContentDispositionHeader } from "../../utils";
import { logger } from "../../../../core/platform/framework";
export class FileController {
async save(
@@ -54,7 +55,7 @@ export class FileController {
response.type(data.mime);
return response.send(data.file);
} catch (e) {
console.log("!!!" + e);
logger.info(e);
throw e;
}
}
@@ -80,7 +81,7 @@ export class FileController {
response.send(data.file);
return response;
} catch (err) {
console.log(err);
logger.error(err);
response.statusCode = 500;
response.send("");
}
@@ -1,6 +1,9 @@
import { fromPath } from "pdf2pic";
import { mkdirSync } from "fs";
import { cleanFiles, getTmpFile } from "../../../../../utils/files";
import { logger } from "../../../../../core/platform/framework";
class PDFConversionError extends Error {}
export async function convertFromPdf(
inputPath: string,
@@ -33,11 +36,14 @@ export async function convertFromPdf(
//Just no more page to convert
}
} catch (error) {
console.error(error);
const pdfConversionError = new PDFConversionError("Can't convert file with pdf-image.", {
cause: error,
});
logger.error(pdfConversionError);
for (const file of pages) {
cleanFiles([file]);
}
throw Error("Can't convert file with pdf-image.");
throw pdfConversionError;
}
return { output: pages, done: true };
}
@@ -7,6 +7,7 @@ import {
CompanyStatsObject,
CompanyUserObject,
} from "./web/types";
import { logger } from "../../core/platform/framework";
export function formatCompany(
companyEntity: Company,
@@ -75,6 +76,8 @@ export function formatCompany(
},
);
logger.info("🚀🚀 res.plan.features: ", res.plan.features);
return res;
}
@@ -490,7 +490,7 @@ export class WorkspaceServiceImpl implements TdriveServiceProvider, Initializabl
}
}
} catch (err) {
console.log(err);
logger.warn(err);
}
}
}
@@ -35,6 +35,7 @@ import WorkspacePendingUser from "../../entities/workspace_pending_users";
import { ConsoleCompany, CreateConsoleUser } from "../../../console/types";
import { hasCompanyAdminLevel } from "../../../../utils/company";
import gr from "../../../global-resolver";
import { logger } from "../../../../core/platform/framework";
export class WorkspaceUsersCrudController
implements
@@ -525,7 +526,7 @@ export class WorkspaceUsersCrudController
return { status: "success" };
} catch (e) {
console.error(e);
logger.warn(e);
return {
status: "error",
};
+2 -1
View File
@@ -1,6 +1,7 @@
import { mkdirSync, existsSync, promises as fsPromise, createWriteStream, readFileSync } from "fs";
import { Readable } from "stream";
import { v4 as uuidv4 } from "uuid";
import { logger } from "../core/platform/framework";
const { unlink } = fsPromise;
@@ -50,7 +51,7 @@ export const writeToTemporaryFile = async (input: Readable, extension: string):
return temporaryFilePath;
} catch (error) {
console.debug(error);
logger.debug(error);
throw Error(error);
}
-61
View File
@@ -1,61 +0,0 @@
import fs from "fs";
import fp from "fastify-plugin";
import v8profiler from "v8-profiler-next";
export class Profiler {
title: string;
active: boolean;
outputDir: string;
constructor(options) {
const { title, active, outputDir } = options;
this.title = title;
this.active = active;
this.outputDir = outputDir;
}
start() {
if (this.active) {
v8profiler.startProfiling(this.title, true);
}
}
finish() {
if (this.active) {
const profile = v8profiler.stopProfiling(this.title);
if (profile === undefined) {
console.log("profile is undefined: ", this.title);
return;
}
profile.export((error, result) => {
if (error) {
console.log("Profiling error: ", error);
} else {
fs.writeFileSync(`${process.cwd()}/${this.outputDir}/${this.title}.cpuprofile`, result);
}
});
}
}
}
async function profilerPlugin(fastify, options) {
fastify.addHook("onRequest", async request => {
const profiler = new Profiler({
title: `${request.method}-${request.url}`,
active: options.active,
outputDir: options.outputDir || "profiles",
});
profiler.start();
// Attach profiler to request for access in routes or other hooks
request.profiler = profiler;
});
fastify.addHook("onResponse", async request => {
if (request.profiler) {
request.profiler.finish();
}
});
}
export default fp(profilerPlugin);
@@ -6,6 +6,9 @@ import UserApi from "../common/user-api";
describe("The Documents Browser Window and API", () => {
let platform: TestPlatform;
let currentUser: UserApi;
let sharedWIthMeFolder: string;
let myDriveId: string;
let files: any;
beforeEach(async () => {
platform = await init({
@@ -25,75 +28,63 @@ describe("The Documents Browser Window and API", () => {
],
});
currentUser = await UserApi.getInstance(platform);
sharedWIthMeFolder = "shared_with_me";
myDriveId = "user_" + currentUser.user.id;
files = await currentUser.uploadAllFilesOneByOne(myDriveId);
expect(files).toBeDefined();
expect(files.entries()).toBeDefined();
expect(Array.from(files.entries())).toHaveLength(UserApi.ALL_FILES.length);
});
afterAll(async () => {
await platform?.tearDown();
// @ts-ignore
platform = null;
});
describe("My Drive", () => {
it("Should successfully upload filed to the 'Shared Drive' and browse them", async () => {
const myDriveId = "user_" + currentUser.user.id;
const result = await currentUser.uploadAllFilesOneByOne(myDriveId);
expect(result).toBeDefined();
expect(result.entries()).toBeDefined();
expect(Array.from(result.entries())).toHaveLength(UserApi.ALL_FILES.length);
it("Should successfully upload filed to the 'My Drive' and browse them", async () => {
const docs = await currentUser.browseDocuments(myDriveId, {});
expect(docs).toBeDefined();
expect(docs.children).toBeDefined();
expect(docs.children.length).toEqual(UserApi.ALL_FILES.length)
expect(docs.children.length).toEqual(UserApi.ALL_FILES.length);
});
it("Should not be visible for other users", async () => {
const myDriveId = "user_" + currentUser.user.id;
const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
await currentUser.uploadAllFilesOneByOne(myDriveId);
await new Promise(r => setTimeout(r, 5000));
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const docs = await currentUser.browseDocuments(myDriveId, {});
expect(docs).toBeDefined();
expect(docs.children).toBeDefined();
expect(docs.children.length).toEqual(UserApi.ALL_FILES.length)
expect(docs.children.length).toEqual(UserApi.ALL_FILES.length);
const anotherUserDocs = await anotherUser.searchDocument({});
expect(anotherUserDocs).toBeDefined();
expect(anotherUserDocs.entities).toBeDefined();
expect(anotherUserDocs.entities.length).toEqual(0);
});
});
describe("Shared Drive", () => {
it("Should successfully upload filed to the 'Shared Drive' and browse them", async () => {
const result = await currentUser.uploadAllFilesOneByOne("root");
expect(result).toBeDefined();
expect(result.entries()).toBeDefined();
expect(Array.from(result.entries())).toHaveLength(UserApi.ALL_FILES.length);
const docs = await currentUser.browseDocuments("root", {});
expect(docs).toBeDefined();
expect(docs.children).toBeDefined();
expect(docs.children.length).toEqual(UserApi.ALL_FILES.length);
});
});
describe("Shared With Me", () => {
it("Shouldn't contain user personal files", async () => {
const sharedWIthMeFolder = "shared_with_me";
await currentUser.uploadAllFilesOneByOne("user_" + currentUser.user.id);
await new Promise(r => setTimeout(r, 5000));
let docs = await currentUser.browseDocuments(sharedWIthMeFolder, {});
expect(docs).toBeDefined();
expect(docs.children?.length).toEqual(0)
expect(docs.children?.length).toEqual(0);
await currentUser.uploadAllFilesOneByOne("root");
docs = await currentUser.browseDocuments("shared_with_me", {});
@@ -103,11 +94,7 @@ describe("The Documents Browser Window and API", () => {
});
it("Should contain files that were shared with the user", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
let files = await oneUser.uploadAllFilesOneByOne();
await new Promise(r => setTimeout(r, 5000));
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
//then:: files are not searchable for user without permissions
expect((await anotherUser.browseDocuments("shared_with_me", {})).children).toHaveLength(0);
@@ -119,48 +106,49 @@ describe("The Documents Browser Window and API", () => {
level: "read",
grantor: null,
});
await oneUser.updateDocument(files[0].id, files[0]);
await currentUser.updateDocument(files[0].id, files[0]);
await new Promise(r => setTimeout(r, 3000));
//then file become searchable
expect((await anotherUser.browseDocuments("shared_with_me", {pageSize: 1})).children).toHaveLength(1);
expect(
(await anotherUser.browseDocuments("shared_with_me", { pageSize: 1 })).children,
).toHaveLength(1);
});
it("Should return ALL the files that was share by user at one", async () => {
const oneUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
let files = await oneUser.uploadAllFilesOneByOne();
await anotherUser.uploadAllFilesOneByOne();
await new Promise(r => setTimeout(r, 5000));
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
//give permissions to the file
files[2].access_info.entities.push({
type: "user",
id: anotherUser.user.id,
level: "read",
// @ts-ignore
grantor: null,
});
await oneUser.updateDocument(files[2].id, files[2]);
await currentUser.updateDocument(files[2].id, files[2]);
await new Promise(r => setTimeout(r, 3000));
//then file become searchable
expect((await anotherUser.browseDocuments("shared_with_me", {pagination: {limitStr: 100}})).children).toHaveLength(1);
expect(
(await anotherUser.browseDocuments("shared_with_me", { pagination: { limitStr: 100 } }))
.children,
).toHaveLength(1);
});
it("User should be able to delete file that was shared with him with 'manage' permissions", async () => {
const oneUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne("user_" + oneUser.user.id);
await new Promise(r => setTimeout(r, 5000));
await new Promise(r => setTimeout(r, 3000));
let toDeleteDoc = files[2];
toDeleteDoc.access_info.entities.push({
type: "user",
id: anotherUser.user.id,
level: "manage",
// @ts-ignore
grantor: null,
});
await oneUser.updateDocument(toDeleteDoc.id, toDeleteDoc);
@@ -172,19 +160,20 @@ describe("The Documents Browser Window and API", () => {
it("User should be able to delete folder with the files that was shared with him with 'manage' permissions", async () => {
const oneUser = await UserApi.getInstance(platform, true);
const anotherUser = await UserApi.getInstance(platform, true);
const dir = await oneUser.createDirectory("user_" + oneUser.user.id);
const level2Dir = await oneUser.createDirectory(dir.id);
const level2Dir2 = await oneUser.createDirectory(dir.id);
await oneUser.uploadAllFilesOneByOne(level2Dir.id);
await oneUser.uploadAllFilesOneByOne(level2Dir2.id);
await oneUser.uploadAllFilesOneByOne(dir.id);
await new Promise(r => setTimeout(r, 5000));
await new Promise(r => setTimeout(r, 3000));
dir.access_info.entities.push({
type: "user",
id: anotherUser.user.id,
level: "manage",
// @ts-ignore
grantor: null,
});
await oneUser.updateDocument(dir.id, dir);
@@ -203,6 +192,7 @@ describe("The Documents Browser Window and API", () => {
type: "user",
id: anotherUser.user.id,
level: "manage",
// @ts-ignore
grantor: null,
});
await oneUser.updateDocument(dir.id, dir);
@@ -220,7 +210,5 @@ describe("The Documents Browser Window and API", () => {
sharedDocs = await anotherUser.browseDocuments("shared_with_me");
expect(sharedDocs.children.length).toBe(0);
});
});
});
@@ -64,7 +64,7 @@ describe("The Drive feature", () => {
const files = await oneUser.uploadAllFilesOneByOne();
// Wait for file processing
await new Promise(r => setTimeout(r, 5000));
await new Promise(r => setTimeout(r, 3000));
// Share the file with recipient user
await anotherUser.shareWithPermissions(files[1], anotherUser.user.id, "read");
@@ -3,8 +3,12 @@ import { init, TestPlatform } from "../setup";
import UserApi from "../common/user-api";
describe("The Documents Browser Window and API", () => {
let sharedWIthMeFolder: string;
let platform: TestPlatform;
let currentUser: UserApi;
let anotherUser: UserApi;
let myDriveId: string;
let files: any;
beforeAll(async () => {
platform = await init({
@@ -23,19 +27,24 @@ describe("The Documents Browser Window and API", () => {
"documents",
],
});
currentUser = await UserApi.getInstance(platform);
currentUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
myDriveId = "user_" + currentUser.user.id;
sharedWIthMeFolder = "shared_with_me";
files = await currentUser.uploadAllFilesOneByOne(myDriveId);
for (const file of files) {
await currentUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
});
afterAll(async () => {
await platform?.tearDown();
// @ts-ignore
platform = null;
});
describe("Pagination and Sorting", () => {
it("Should paginate documents correctly", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
let page_token = "1";
const limitStr = "2";
let docs = await currentUser.browseDocuments(myDriveId, {
@@ -60,9 +69,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by name in ascending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "name";
const sortOrder = "asc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -75,9 +81,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by name in descending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "name";
const sortOrder = "desc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -90,9 +93,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by date in ascending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "date";
const sortOrder = "asc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -107,9 +107,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by date in descending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "date";
const sortOrder = "desc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -124,9 +121,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by size in ascending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "size";
const sortOrder = "asc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -139,9 +133,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort documents by size in descending order", async () => {
const myDriveId = "user_" + currentUser.user.id;
await currentUser.uploadAllFilesOneByOne(myDriveId);
const sortBy = "size";
const sortOrder = "desc";
const docs = await currentUser.browseDocuments(myDriveId, {
@@ -154,17 +145,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should paginate shared with me ", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
// wait for files to be indexed
await new Promise(r => setTimeout(r, 5000));
let page_token: any = "1";
const limitStr = "2";
let docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -189,13 +169,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by name in ascending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "name";
const sortOrder = "asc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -208,13 +181,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by name in descending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "name";
const sortOrder = "desc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -227,13 +193,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by size in ascending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "size";
const sortOrder = "asc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -246,13 +205,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by size in descending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "size";
const sortOrder = "desc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -265,13 +217,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by date in ascending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "date";
const sortOrder = "asc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -286,13 +231,6 @@ describe("The Documents Browser Window and API", () => {
});
it("Should sort shared with me by date in descending order", async () => {
const sharedWIthMeFolder = "shared_with_me";
const oneUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
const anotherUser = await UserApi.getInstance(platform, true, { companyRole: "admin" });
let files = await oneUser.uploadAllFilesOneByOne();
for (const file of files) {
await oneUser.shareWithPermissions(file, anotherUser.user.id, "read");
}
const sortBy = "date";
const sortOrder = "desc";
const docs = await anotherUser.browseDocuments(sharedWIthMeFolder, {
@@ -0,0 +1,8 @@
// @ts-ignore
import path from "path";
// @ts-ignore
import config from "config";
const ourConfigDir = path.join(__dirname, 'oneof-storage');
let configs = config.util.loadFileConfigs(ourConfigDir);
config.util.extendDeep(config, configs);
@@ -0,0 +1,21 @@
{
"storage": {
"secret": "0ea28a329df23220fa814e005bfb671c",
"iv": "1234abcd00000000",
"strategy": "oneof",
"oneof": [
{
"type": "local",
"local": {
"path": "-tmp/storage1"
}
},
{
"type": "local",
"local": {
"path": "/tmp/storage2"
}
}]
}
}
@@ -0,0 +1,71 @@
import "./load_test_config"
import "reflect-metadata";
import { afterAll, beforeAll, afterEach, describe, expect, it } from "@jest/globals";
import { init, TestPlatform } from "../setup";
import UserApi from "../common/user-api";
import LocalConnectorService from "../../../src/core/platform/services/storage/connectors/local/service"
import { StorageConnectorAPI } from "../../../src/core/platform/services/storage/provider";
describe("The OneOf Storage feature", () => {
const url = "/internal/services/files/v1";
let platform: TestPlatform;
let helpers: UserApi;
beforeAll(async () => {
platform = await init({
services: ["webserver", "database", "storage", "files", "previews"],
});
helpers = await UserApi.getInstance(platform);
}, 300000000);
afterEach(async () => {
jest.restoreAllMocks();
});
afterAll(async () => {
await platform?.tearDown();
platform = null;
});
describe("On file upload", () => {
it("should fail an upload POST when ALL backend storage fails", async () => {
const thrower = () => {
throw new Error("<Mock error done on purpose on upload to storage (for the E2E test)>");
};
const writeLocalMock = jest.spyOn(LocalConnectorService.prototype, "write").mockRejectedValue("Error");
// expect(response.statusCode).toBe(500);
await expect(helpers.uploadRandomFile()).rejects.toThrow("Error code: 500");
// expect(response.statusCode).toBe(500);
expect(writeLocalMock.mock.calls.length).toEqual(2);
});
it("should successfully upload file when one backend storage fails", async () => {
const thrower = () => {
throw new Error("<Mock error done on purpose on upload to storage (for the E2E test)>");
};
const connectors = (platform.storage.getConnector() as any).storages as Array<StorageConnectorAPI>;
const writeLocalMock = jest.spyOn(connectors[0], "write").mockRejectedValue("Error");
// expect(response.statusCode).toBe(200);
const filesUpload = await helpers.uploadRandomFile();
expect(filesUpload.id).toBeTruthy();
// expect failed upload
expect(writeLocalMock.mock.calls.length).toBeGreaterThan(0);
//expect that file can be downloaded
const fileDownloadResponse = await platform.app.inject({
method: "GET",
url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.id}/download`,
});
expect(fileDownloadResponse).toBeTruthy();
expect(fileDownloadResponse.statusCode).toBe(200);
});
});
});
@@ -0,0 +1,185 @@
import { Readable, PassThrough } from 'stream';
import { OneOfStorageStrategy } from "../../../../../src/core/platform/services/storage/oneof-storage-strategy"; // Adjust the import path as necessary
import {StorageConnectorAPI } from "../../../../../src/core/platform/services/storage/provider"; // Adjust the import path as necessary
function mockStream() {
const stream = new Readable();
stream.push("Test data");
stream.push(null);
return stream;
}
function mockStorage(size: number) {
return {
write: jest.fn().mockResolvedValue({ size: size }),
read: jest.fn(),
exists: jest.fn(),
remove: jest.fn().mockResolvedValue(true),
getId: jest.fn().mockResolvedValue("Mock Storage ID")
} as unknown as StorageConnectorAPI;
}
describe('OneOfStorageStrategy', () => {
let storage1: StorageConnectorAPI;
let storage2: StorageConnectorAPI;
let storage3: StorageConnectorAPI;
let oneOfStorageStrategy: OneOfStorageStrategy;
beforeEach(() => {
// Mocking two storage backends
storage1 = mockStorage(100);
storage2 = mockStorage(200);
storage3 = mockStorage(300);
oneOfStorageStrategy = new OneOfStorageStrategy([storage1, storage2, storage3]);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('write', () => {
it('should write to all configured storages', async () => {
const path = 'test/path';
const stream = mockStream();
const result = await oneOfStorageStrategy.write(path, stream);
expect(storage1.write).toHaveBeenCalled();
expect(storage2.write).toHaveBeenCalled();
expect(result.size).toBe(100); // Assuming the first storage returned this size
});
it('should throw an error if all storage writes fail', async () => {
storage1.write = jest.fn().mockRejectedValue(new Error('Write failed'));
storage2.write = jest.fn().mockRejectedValue(new Error('Write failed'));
storage3.write = jest.fn().mockRejectedValue(new Error('Write failed'));
const path = 'test/path';
const stream = mockStream();
await expect(oneOfStorageStrategy.write(path, stream)).rejects.toThrow('Write test/path failed for all storages');
});
it('should succeed if one storage write fails but the other succeeds', async () => {
//given
storage1.write = jest.fn().mockRejectedValue(new Error('Write failed'));
storage2.write = jest.fn().mockResolvedValue({ size: 200 });
storage3.write = jest.fn().mockRejectedValue(new Error('Write failed'));
const path = 'test/path';
const stream = mockStream();
//when
const result = await oneOfStorageStrategy.write(path, stream);
//then
expect(result.size).toBe(200); // Assuming the second storage returned this size
});
it('should verify that all data was written to the storage stream', async () => {
const data = "Test data";
const stream = new Readable();
stream.push(data);
stream.push(null);
storage1.write = jest.fn().mockImplementation((_, stream) => {
return new Promise((resolve, reject) => {
let writtenData = '';
stream.on('data', chunk => {
writtenData += chunk;
});
stream.on('end', () => {
if (writtenData === data) {
resolve({ size: writtenData.length });
} else {
reject(new Error('Data mismatch'));
}
});
});
});
const result = await oneOfStorageStrategy.write("test/path", stream);
expect(result.size).toBe(data.length);
});
});
describe('read', () => {
it('should read from the first storage that has the file', async () => {
const path = 'test/path';
const mockStream = new Readable();
mockStream.push('Test data');
mockStream.push(null);
storage1.read = jest.fn().mockResolvedValue(mockStream);
storage1.exists = jest.fn().mockResolvedValue(true);
const result = await oneOfStorageStrategy.read(path);
expect(storage1.exists).toHaveBeenCalledWith(path, undefined);
expect(storage1.read).toHaveBeenCalledWith(path, undefined);
expect(result).toBe(mockStream);
});
it('should fallback to the next storage if the first fails', async () => {
const path = 'test/path';
storage1.read = jest.fn().mockRejectedValue(new Error('Read failed'));
storage1.exists = jest.fn().mockResolvedValue(true);
const mockStream = new Readable();
mockStream.push('Test data');
mockStream.push(null);
storage2.read = jest.fn().mockResolvedValue(mockStream);
storage2.exists = jest.fn().mockResolvedValue(true);
const result = await oneOfStorageStrategy.read(path);
expect(storage1.exists).toHaveBeenCalled();
expect(storage1.read).toHaveBeenCalled();
expect(storage2.exists).toHaveBeenCalled();
expect(storage2.read).toHaveBeenCalled();
expect(result).toBe(mockStream);
});
});
describe('exists', () => {
it('should return true if the file exists in any storage', async () => {
storage1.exists = jest.fn().mockResolvedValue(false);
storage2.exists = jest.fn().mockResolvedValue(true);
const result = await oneOfStorageStrategy.exists('test/path');
expect(result).toBe(true);
expect(storage1.exists).toHaveBeenCalled();
expect(storage2.exists).toHaveBeenCalled();
});
it('should return false if the file does not exist in any storage', async () => {
storage1.exists = jest.fn().mockResolvedValue(false);
storage2.exists = jest.fn().mockResolvedValue(false);
const result = await oneOfStorageStrategy.exists('test/path');
expect(result).toBe(false);
expect(storage1.exists).toHaveBeenCalled();
expect(storage2.exists).toHaveBeenCalled();
});
});
describe('remove', () => {
it('should remove the file from all storages', async () => {
const result = await oneOfStorageStrategy.remove('test/path');
expect(storage1.remove).toHaveBeenCalledWith('test/path', undefined);
expect(storage2.remove).toHaveBeenCalledWith('test/path', undefined);
expect(result).toBe(true);
});
it('should return false if any storage fails to remove the file', async () => {
storage1.remove = jest.fn().mockResolvedValue(true);
storage2.remove = jest.fn().mockResolvedValue(false);
const result = await oneOfStorageStrategy.remove('test/path');
expect(result).toBe(false);
});
});
});
+72 -440
View File
@@ -493,11 +493,6 @@
rfdc "^1.3.0"
yaml "^2.2.2"
"@ffprobe-installer/darwin-arm64@5.0.1":
version "5.0.1"
resolved "https://registry.npmjs.org/@ffprobe-installer/darwin-arm64/-/darwin-arm64-5.0.1.tgz"
integrity sha512-vwNCNjokH8hfkbl6m95zICHwkSzhEvDC3GVBcUp5HX8+4wsX10SP3B+bGur7XUzTIZ4cQpgJmEIAx6TUwRepMg==
"@ffprobe-installer/ffprobe@^1.4.1":
version "1.4.1"
resolved "https://registry.npmjs.org/@ffprobe-installer/ffprobe/-/ffprobe-1.4.1.tgz"
@@ -512,10 +507,10 @@
"@ffprobe-installer/win32-ia32" "5.0.0"
"@ffprobe-installer/win32-x64" "5.0.0"
"@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@ffprobe-installer/linux-x64@5.0.0":
version "5.0.0"
resolved "https://registry.npmjs.org/@ffprobe-installer/linux-x64/-/linux-x64-5.0.0.tgz"
integrity sha512-zgLnWJFvMGCaw1txGtz84sMEQt6mQUzdw86ih9S/kZOWnp06Gj/ams/EXxEkAxgAACCVM6/O0mkDe/6biY5tgA==
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
@@ -536,17 +531,29 @@
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
"@img/sharp-darwin-arm64@0.33.3":
version "0.33.3"
resolved "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz"
integrity sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==
optionalDependencies:
"@img/sharp-libvips-darwin-arm64" "1.0.2"
"@img/sharp-libvips-darwin-arm64@1.0.2":
"@img/sharp-libvips-linux-x64@1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz"
integrity sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==
resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz"
integrity sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==
"@img/sharp-libvips-linuxmusl-x64@1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz"
integrity sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==
"@img/sharp-linux-x64@0.33.3":
version "0.33.3"
resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz"
integrity sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==
optionalDependencies:
"@img/sharp-libvips-linux-x64" "1.0.2"
"@img/sharp-linuxmusl-x64@0.33.3":
version "0.33.3"
resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz"
integrity sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-x64" "1.0.2"
"@isaacs/cliui@^8.0.2":
version "8.0.2"
@@ -904,22 +911,6 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@npmcli/fs@^2.1.0":
version "2.1.2"
resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz"
integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==
dependencies:
"@gar/promisify" "^1.1.3"
semver "^7.3.5"
"@npmcli/move-file@^2.0.0":
version "2.0.1"
resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz"
integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==
dependencies:
mkdirp "^1.0.4"
rimraf "^3.0.2"
"@opensearch-project/opensearch@^2.4.0":
version "2.7.0"
resolved "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-2.7.0.tgz"
@@ -1087,11 +1078,6 @@
dependencies:
defer-to-connect "^2.0.0"
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
"@tsconfig/node10@^1.0.7":
version "1.0.11"
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz"
@@ -1430,6 +1416,13 @@
pg-protocol "*"
pg-types "^4.0.1"
"@types/pino@^7.0.5":
version "7.0.5"
resolved "https://registry.npmjs.org/@types/pino/-/pino-7.0.5.tgz"
integrity sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg==
dependencies:
pino "*"
"@types/pump@^1.1.1":
version "1.1.3"
resolved "https://registry.npmjs.org/@types/pump/-/pump-1.1.3.tgz"
@@ -1648,28 +1641,12 @@
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@xprofiler/node-pre-gyp@^1.0.9":
version "1.0.11"
resolved "https://registry.npmjs.org/@xprofiler/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz"
integrity sha512-kNFT4XscrA+Hjh+jSHs49PiG/YGf08a6eNDo16qjSnCaT4B5ngrKDcNtEJ6CnS0sDP/1oZmHCBYECB6wGKP7lg==
dependencies:
detect-libc "^1.0.3"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.5"
node-gyp "9.3.1"
nopt "^5.0.0"
npmlog "^5.0.1"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.11"
"@zxing/text-encoding@0.9.0":
version "0.9.0"
resolved "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz"
integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==
abbrev@^1.0.0, abbrev@1:
abbrev@1:
version "1.1.1"
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
@@ -1718,28 +1695,13 @@ acorn-walk@^8.1.1:
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
agent-base@^6.0.2, agent-base@6:
agent-base@6:
version "6.0.2"
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
agentkeepalive@^4.2.1:
version "4.5.0"
resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz"
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
dependencies:
humanize-ms "^1.2.1"
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz"
integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
dependencies:
clean-stack "^2.0.0"
indent-string "^4.0.0"
aggregate-error@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz"
@@ -1928,14 +1890,6 @@ are-we-there-yet@^2.0.0:
delegates "^1.0.0"
readable-stream "^3.6.0"
are-we-there-yet@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz"
integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
@@ -2274,30 +2228,6 @@ buffer@^6.0.3:
base64-js "^1.3.1"
ieee754 "^1.2.1"
cacache@^16.1.0:
version "16.1.3"
resolved "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz"
integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==
dependencies:
"@npmcli/fs" "^2.1.0"
"@npmcli/move-file" "^2.0.0"
chownr "^2.0.0"
fs-minipass "^2.1.0"
glob "^8.0.1"
infer-owner "^1.0.4"
lru-cache "^7.7.1"
minipass "^3.1.6"
minipass-collect "^1.0.2"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
mkdirp "^1.0.4"
p-map "^4.0.0"
promise-inflight "^1.0.1"
rimraf "^3.0.2"
ssri "^9.0.0"
tar "^6.1.11"
unique-filename "^2.0.0"
cacheable-lookup@^5.0.3:
version "5.0.4"
resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz"
@@ -2447,11 +2377,6 @@ class-transformer@^0.3.1:
resolved "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.1.tgz"
integrity sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA==
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
clean-stack@^4.0.0:
version "4.2.0"
resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz"
@@ -2555,7 +2480,7 @@ color-string@^1.9.0:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color-support@^1.1.2, color-support@^1.1.3:
color-support@^1.1.2:
version "1.1.3"
resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
@@ -2880,11 +2805,6 @@ detect-indent@^6.0.0:
resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz"
integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
detect-libc@^2.0.0, detect-libc@^2.0.3:
version "2.0.3"
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz"
@@ -3024,7 +2944,7 @@ emojilib@^2.0.2:
resolved "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz"
integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==
encoding@^0.1.0, encoding@^0.1.13:
encoding@^0.1.0:
version "0.1.13"
resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
@@ -3075,16 +2995,6 @@ entities@^2.0.0:
resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
err-code@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz"
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz"
@@ -3413,12 +3323,12 @@ fast-querystring@^1.0.0:
dependencies:
fast-decode-uri-component "^1.0.1"
fast-redact@^3.0.0, fast-redact@^3.1.1:
fast-redact@^3.1.1:
version "3.5.0"
resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz"
integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==
fast-safe-stringify@^2.0.8, fast-safe-stringify@^2.1.1, fast-safe-stringify@~2.1.0:
fast-safe-stringify@^2.1.1, fast-safe-stringify@~2.1.0:
version "2.1.1"
resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
@@ -3583,11 +3493,6 @@ flat-cache@^3.0.4:
keyv "^4.5.3"
rimraf "^3.0.2"
flatstr@^1.0.12:
version "1.0.12"
resolved "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz"
integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==
flatted@^3.2.9:
version "3.3.1"
resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz"
@@ -3683,7 +3588,7 @@ fs-extra@^10.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-minipass@^2.0.0, fs-minipass@^2.1.0:
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
@@ -3695,11 +3600,6 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
fstream@^1.0.12:
version "1.0.12"
resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz"
@@ -3730,20 +3630,6 @@ gauge@^3.0.0:
strip-ansi "^6.0.1"
wide-align "^1.1.2"
gauge@^4.0.3:
version "4.0.4"
resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz"
integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==
dependencies:
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.3"
console-control-strings "^1.1.0"
has-unicode "^2.0.1"
signal-exit "^3.0.7"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.5"
generate-password@^1.6.0:
version "1.7.1"
resolved "https://registry.npmjs.org/generate-password/-/generate-password-1.7.1.tgz"
@@ -3860,17 +3746,6 @@ glob@^7.2.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^8.0.1:
version "8.1.0"
resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
@@ -3940,7 +3815,7 @@ got@~11.8.5:
p-cancelable "^2.0.0"
responselike "^2.0.0"
graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -4055,7 +3930,7 @@ htmlparser2@^6.1.0:
domutils "^2.5.2"
entities "^2.0.0"
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
http-cache-semantics@^4.0.0:
version "4.1.1"
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
@@ -4071,15 +3946,6 @@ http-errors@^2.0.0, http-errors@2.0.0:
statuses "2.0.1"
toidentifier "1.0.1"
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
dependencies:
"@tootallnate/once" "2"
agent-base "6"
debug "4"
http2-wrapper@^1.0.0-beta.5.2:
version "1.0.3"
resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz"
@@ -4101,13 +3967,6 @@ human-signals@^2.1.0:
resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz"
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"
i18n@^0.15.1:
version "0.15.1"
resolved "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz"
@@ -4170,21 +4029,11 @@ imurmurhash@^0.1.4:
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
indent-string@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz"
integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==
infer-owner@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz"
integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
@@ -4287,11 +4136,6 @@ is-interactive@^1.0.0:
resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
is-lambda@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz"
integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
@@ -5175,11 +5019,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lru-cache@^7.7.1:
version "7.18.3"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
lru-cache@~4.0.0:
version "4.0.2"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz"
@@ -5215,28 +5054,6 @@ make-error@^1.1.1, make-error@1.x:
resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
make-fetch-happen@^10.0.3:
version "10.2.1"
resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz"
integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==
dependencies:
agentkeepalive "^4.2.1"
cacache "^16.1.0"
http-cache-semantics "^4.1.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-lambda "^1.0.1"
lru-cache "^7.7.1"
minipass "^3.1.6"
minipass-collect "^1.0.2"
minipass-fetch "^2.0.3"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
negotiator "^0.6.3"
promise-retry "^2.0.1"
socks-proxy-agent "^7.0.0"
ssri "^9.0.0"
make-plural@^7.0.0:
version "7.4.0"
resolved "https://registry.npmjs.org/make-plural/-/make-plural-7.4.0.tgz"
@@ -5388,13 +5205,6 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.6"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
minimatch@^5.1.0:
version "5.1.6"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
@@ -5443,45 +5253,6 @@ minio@^7.1.3:
xml "^1.0.1"
xml2js "^0.5.0"
minipass-collect@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz"
integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
dependencies:
minipass "^3.0.0"
minipass-fetch@^2.0.3:
version "2.1.2"
resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz"
integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==
dependencies:
minipass "^3.1.6"
minipass-sized "^1.0.3"
minizlib "^2.1.2"
optionalDependencies:
encoding "^0.1.13"
minipass-flush@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz"
integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
dependencies:
minipass "^3.0.0"
minipass-pipeline@^1.2.4:
version "1.2.4"
resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz"
integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
dependencies:
minipass "^3.0.0"
minipass-sized@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz"
integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
dependencies:
minipass "^3.0.0"
minipass@^3.0.0:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
@@ -5489,20 +5260,6 @@ minipass@^3.0.0:
dependencies:
yallist "^4.0.0"
minipass@^3.1.1:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^3.1.6:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4:
version "7.1.0"
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz"
@@ -5513,7 +5270,7 @@ minipass@^5.0.0:
resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
minizlib@^2.1.1, minizlib@^2.1.2:
minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
@@ -5569,7 +5326,7 @@ moo@^0.5.0, moo@^0.5.1:
resolved "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz"
integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -5597,11 +5354,6 @@ mustache@^4.2.0:
resolved "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz"
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
nan@^2.18.0:
version "2.19.0"
resolved "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz"
integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==
natural-compare-lite@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz"
@@ -5631,7 +5383,7 @@ needle@^2.5.2:
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@^0.6.3, negotiator@0.6.3:
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
@@ -5684,29 +5436,13 @@ node-ensure@^0.0.0:
resolved "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz"
integrity sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==
node-fetch@^2.6.5, node-fetch@^2.6.7:
node-fetch@^2.6.7:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
node-gyp@9.3.1:
version "9.3.1"
resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.1.tgz"
integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==
dependencies:
env-paths "^2.2.0"
glob "^7.1.4"
graceful-fs "^4.2.6"
make-fetch-happen "^10.0.3"
nopt "^6.0.0"
npmlog "^6.0.0"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.2"
which "^2.0.2"
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz"
@@ -5753,13 +5489,6 @@ nopt@^5.0.0:
dependencies:
abbrev "1"
nopt@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz"
integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==
dependencies:
abbrev "^1.0.0"
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz"
@@ -5809,16 +5538,6 @@ npmlog@^5.0.1:
gauge "^3.0.0"
set-blocking "^2.0.0"
npmlog@^6.0.0:
version "6.0.2"
resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz"
integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==
dependencies:
are-we-there-yet "^3.0.0"
console-control-strings "^1.1.0"
gauge "^4.0.3"
set-blocking "^2.0.0"
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
@@ -5957,13 +5676,6 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz"
integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
dependencies:
aggregate-error "^3.0.0"
p-map@^5.1.0, p-map@^5.3.0:
version "5.5.0"
resolved "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz"
@@ -6153,7 +5865,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0:
pino-abstract-transport@^1.0.0:
version "1.2.0"
resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz"
integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==
@@ -6161,6 +5873,13 @@ pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0:
readable-stream "^4.0.0"
split2 "^4.0.0"
pino-abstract-transport@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz"
integrity sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==
dependencies:
split2 "^4.0.0"
pino-pretty@^10.0.0:
version "10.3.1"
resolved "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz"
@@ -6181,45 +5900,22 @@ pino-pretty@^10.0.0:
sonic-boom "^3.0.0"
strip-json-comments "^3.1.1"
pino-std-serializers@^3.1.0:
version "3.2.0"
resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz"
integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==
pino-std-serializers@^6.1.0:
version "6.2.2"
resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz"
integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==
pino-std-serializers@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz"
integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==
pino@^6.8.0:
version "6.14.0"
resolved "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
dependencies:
fast-redact "^3.0.0"
fast-safe-stringify "^2.0.8"
flatstr "^1.0.12"
pino-std-serializers "^3.1.0"
process-warning "^1.0.0"
quick-format-unescaped "^4.0.3"
sonic-boom "^1.0.2"
pino@^9.0.0:
version "9.1.0"
resolved "https://registry.npmjs.org/pino/-/pino-9.1.0.tgz"
integrity sha512-qUcgfrlyOtjwhNLdbhoL7NR4NkHjzykAPw0V2QLFbvu/zss29h4NkRnibyFzBrNCbzCOY3WZ9hhKSwfOkNggYA==
pino@*, pino@^9.0.0, pino@^9.5.0:
version "9.5.0"
resolved "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz"
integrity sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==
dependencies:
atomic-sleep "^1.0.0"
fast-redact "^3.1.1"
on-exit-leak-free "^2.1.0"
pino-abstract-transport "^1.2.0"
pino-abstract-transport "^2.0.0"
pino-std-serializers "^7.0.0"
process-warning "^3.0.0"
process-warning "^4.0.0"
quick-format-unescaped "^4.0.3"
real-require "^0.2.0"
safe-stable-stringify "^2.3.1"
@@ -6332,16 +6028,16 @@ process-nextick-args@~2.0.0:
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process-warning@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz"
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
process-warning@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz"
integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==
process-warning@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz"
integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
@@ -6352,19 +6048,6 @@ promise-breaker@^5.0.0:
resolved "https://registry.npmjs.org/promise-breaker/-/promise-breaker-5.0.0.tgz"
integrity sha512-mgsWQuG4kJ1dtO6e/QlNDLFtMkMzzecsC69aI5hlLEjGHFNpHrvGhFi4LiK5jg2SMQj74/diH+wZliL9LpGsyA==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz"
integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
promise-retry@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz"
integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
dependencies:
err-code "^2.0.2"
retry "^0.12.0"
promise@~1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/promise/-/promise-1.3.0.tgz"
@@ -6745,11 +6428,6 @@ ret@~0.4.0:
resolved "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz"
integrity sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz"
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
reusify@^1.0.0, reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
@@ -7050,16 +6728,7 @@ socket.io@^4.6.2:
socket.io-adapter "~2.5.2"
socket.io-parser "~4.2.4"
socks-proxy-agent@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz"
integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==
dependencies:
agent-base "^6.0.2"
debug "^4.3.3"
socks "^2.6.2"
socks@^2.6.2, socks@^2.7.1:
socks@^2.7.1:
version "2.8.3"
resolved "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz"
integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
@@ -7067,14 +6736,6 @@ socks@^2.6.2, socks@^2.7.1:
ip-address "^9.0.5"
smart-buffer "^4.2.0"
sonic-boom@^1.0.2:
version "1.4.1"
resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz"
integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==
dependencies:
atomic-sleep "^1.0.0"
flatstr "^1.0.12"
sonic-boom@^3.0.0:
version "3.8.1"
resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz"
@@ -7083,9 +6744,9 @@ sonic-boom@^3.0.0:
atomic-sleep "^1.0.0"
sonic-boom@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.0.1.tgz"
integrity sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==
version "4.2.0"
resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz"
integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==
dependencies:
atomic-sleep "^1.0.0"
@@ -7169,13 +6830,6 @@ sprintf-js@~1.0.2:
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
ssri@^9.0.0:
version "9.0.1"
resolved "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz"
integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==
dependencies:
minipass "^3.1.1"
stack-utils@^2.0.3:
version "2.0.6"
resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz"
@@ -7400,7 +7054,7 @@ tar-stream@^2.2.0:
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^6.1.11, tar@^6.1.2:
tar@^6.1.11:
version "6.2.1"
resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
@@ -7691,20 +7345,6 @@ undici-types@~5.26.4:
resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
unique-filename@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz"
integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==
dependencies:
unique-slug "^3.0.0"
unique-slug@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz"
integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==
dependencies:
imurmurhash "^0.1.4"
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
@@ -7784,14 +7424,6 @@ v8-compile-cache-lib@^3.0.1:
resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-profiler-next@^1.10.0:
version "1.10.0"
resolved "https://registry.npmjs.org/v8-profiler-next/-/v8-profiler-next-1.10.0.tgz"
integrity sha512-HME7CR3V8gkBEAutcMyGS0vK0XH2hFQhF5SvSdrF/mdjWIGoaiY+WH3RpY7ePY7J7vNDbQfP+Ikefdi1z/mJXg==
dependencies:
"@xprofiler/node-pre-gyp" "^1.0.9"
nan "^2.18.0"
v8-to-istanbul@^9.0.1:
version "9.2.0"
resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz"
@@ -7888,14 +7520,14 @@ which@^1.2.9:
dependencies:
isexe "^2.0.0"
which@^2.0.1, which@^2.0.2:
which@^2.0.1:
version "2.0.2"
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wide-align@^1.1.2, wide-align@^1.1.5:
wide-align@^1.1.2:
version "1.1.5"
resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+1 -1
View File
@@ -1,4 +1,4 @@
version: "2"
version: "3"
services:
# rabbitmq:
+1
View File
@@ -31,6 +31,7 @@ module.exports = {
'@typescript-eslint/no-this-alias': 'warn',
'react/prop-types': 'warn',
'no-useless-escape': 'warn',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-empty-function': 'warn',
'@typescript-eslint/ban-types': 'warn',
'@typescript-eslint/ban-ts-comment': 'warn',
+10858 -4142
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -232,7 +232,7 @@
"react-transition-group": "^4.4.1",
"react-virtualized": "^9.22.2",
"react-virtuoso": "2.10.2",
"react-zoom-pan-pinch": "^2.1.3",
"react-zoom-pan-pinch": "^2.6.1",
"recoil": "^0.7.6",
"redux": "^4.0.5",
"resize-observer-polyfill": "^1.5.1",
+2 -19
View File
@@ -54,13 +54,13 @@
left: calc(50% - 25px);
position: absolute;
top: calc(50% - 25px);
width: 50px;
width: 50px;
}
.path {
stroke-dasharray: 1,200;
stroke-dashoffset: 0;
animation:
animation:
dash 1.5s ease-in-out infinite,
color 6s ease-in-out infinite
;
@@ -136,22 +136,5 @@
document.getElementById('body').className = 'electron';
}
</script>
<script async src="https://cdn.headwayapp.co/widget.js"></script>
<script>
if(location.origin.indexOf("tdrive.app") >= 0){
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e<analytics.methods.length;e++){var key=analytics.methods[e];analytics[key]=analytics.factory(key)}analytics.load=function(key,e){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._loadOptions=e};analytics.SNIPPET_VERSION="4.13.1";
analytics.load("Vl5xpgcpGRe4BmSYp9xPlAeQdqLP5lLe");
analytics.page();
}}();
}
</script>
<script>
if(location.origin.indexOf("canary.tdrive.app") >= 0){
const canaryTag = document.createElement("div");
canaryTag.id = "canary_tag";
document.body.appendChild(canaryTag);
window.canary = true;
}
</script>
</body>
</html>
+1
View File
@@ -197,6 +197,7 @@
"hooks.use-drive-actions.unable_remove_file": "Unable to remove this file.",
"hooks.use-drive-actions.unable_restore_file": "Unable to restore this item.",
"hooks.use-drive-actions.unable_update_file": "Unable to update this file.",
"hooks.use-drive-actions.update_caused_a_rename": "Item was renamed to '{{$2}}'.",
"login.create_account": "Create account",
"login.login_error": "Error during login",
"molecules.download_banner.download_button": "Download desktop app",
+1
View File
@@ -190,6 +190,7 @@
"hooks.use-drive-actions.unable_remove_file": "Impossible de supprimer ce fichier",
"hooks.use-drive-actions.unable_restore_file": "Impossible de restaurer cet élément.",
"hooks.use-drive-actions.unable_update_file": "Impossible de mettre à jour ce fichier",
"hooks.use-drive-actions.update_caused_a_rename": "Renommé en '{{$2}}'.",
"login.create_account": "Créer un compte",
"login.login_error": "Erreur lors de la connexion",
"molecules.download_banner.download_button": "Télécharger l'application de bureau",
+1
View File
@@ -197,6 +197,7 @@
"hooks.use-drive-actions.unable_remove_file": "Невозможно удалить эти файлы.",
"hooks.use-drive-actions.unable_restore_file": "Невозможно восстановить эти файлы.",
"hooks.use-drive-actions.unable_update_file": "Невозможно обновить эти файлы.",
"hooks.use-drive-actions.update_caused_a_rename": "Элемент был переименован в «{{$2}}».",
"login.create_account": "Создать учетную запись",
"login.login_error": "Ошибка при входе в систему",
"molecules.download_banner.download_button": "Скачать настольное приложение",
+1
View File
@@ -179,6 +179,7 @@
"hooks.use-drive-actions.unable_remove_file": "Không thể xóa tệp này.",
"hooks.use-drive-actions.unable_restore_file": "Không thể khôi phục mục này.",
"hooks.use-drive-actions.unable_update_file": "Không thể cập nhật tệp này.",
"hooks.use-drive-actions.update_caused_a_rename": "Mục đã được đổi tên thành '{{$2}}'.",
"login.create_account": "Tạo tài khoản",
"login.login_error": "Lỗi trong khi đăng nhập",
"molecules.download_banner.download_button": "Tải xuống ứng dụng dành cho máy tính",
@@ -8,7 +8,7 @@ import {
useRef,
} from "react";
let interval: any = null;
let interval: ReturnType<typeof setInterval> | null = null;
export const AnimatedHeight = memo(
(props: { children: ReactNode } & InputHTMLAttributes<HTMLDivElement>) => {
@@ -28,7 +28,8 @@ export const AnimatedHeight = memo(
updateSize();
}, 200);
return () => {
clearInterval(interval);
if (interval)
clearInterval(interval);
};
}, []);
@@ -36,7 +36,7 @@ const Template: ComponentStory<any> = (props: { label: string; disabled: boolean
<Title className='my-5'>CheckboxSlider</Title>
<CheckboxSlider
onClick={e => setChecked(!checked)}
onClick={() => setChecked(!checked)}
checked={checked}
disabled={props.disabled}
/>
@@ -4,7 +4,6 @@ import { action } from '@storybook/addon-actions';
import { RecoilRoot } from 'recoil';
import { ComponentStory } from '@storybook/react';
import { CheckCircleIcon, ExclamationCircleIcon } from '@heroicons/react/outline';
import { Title } from '../text';
import { ConfirmModal, ConfirmModalProps } from './confirm';
const icons = {
@@ -36,6 +35,7 @@ export default {
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Template: ComponentStory<any> = (props: ConfirmModalProps) => {
const [open, setOpen] = useState(true);
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import './connection-indicator.scss';
import ErrorOutlinedIcon from '@material-ui/icons/ErrorOutlined';
import HourglassEmpty from '@material-ui/icons/HourglassEmpty';
@@ -9,7 +9,7 @@ import { useRecoilState } from 'recoil';
// import WebSocket, { WebsocketEvents } from '@features/global/types/websocket-types';
export default () => {
const [{ connected, reconnecting }, setState] = useRecoilState(ConnectedState);
const [{ connected, reconnecting }] = useRecoilState(ConnectedState);
return (
<div className={'connection_indicator ' + (connected === false ? 'visible' : '')}>
@@ -1,24 +1,21 @@
import React, { useEffect, useState } from 'react';
import InitService from '../../features/global/services/init-service';
// import InitService from '../../features/global/services/init-service';
import LocalStorage from '../../features/global/framework/local-storage-service';
import DownloadBanner from '@molecules/download-banner';
import { detectDesktopAppPresence } from '../../../utils/browser-detect';
export default (): React.ReactElement => {
const [showBanner, setShowBanner] = useState(false);
const [ , setShowBanner] = useState(false);
const download = (): void => {
const appDownloadUrl = InitService?.server_infos?.configuration?.app_download_url;
// const download = (): void => {
// const appDownloadUrl = InitService?.server_infos?.configuration?.app_download_url;
// if (appDownloadUrl)
// window.open(appDownloadUrl, '_blank');
// };
if (appDownloadUrl) {
window.open(appDownloadUrl, '_blank');
}
};
const removeBanner = (): void => {
LocalStorage.setItem('show_app_banner', 'false');
setShowBanner(false);
};
// const removeBanner = (): void => {
// LocalStorage.setItem('show_app_banner', 'false');
// setShowBanner(false);
// };
useEffect(() => {
if (LocalStorage.getItem('show_app_banner') === 'false') {
@@ -1,5 +1,4 @@
import React, { ReactNode } from 'react';
import { getDevice } from '../../features/global/utils/device';
import configuration from '../../environment/environment';
import { Smartphone, X } from 'react-feather';
import './style.scss';
@@ -29,7 +29,7 @@ export default class PopupComponent extends React.Component {
{this.state.popupManager.canClose() && (
<div className="header">
<div className="close" onClick={() => this.state.popupManager.close()}>
<CloseIcon class="m-icon" />
<CloseIcon className="m-icon" />
</div>
</div>
)}
@@ -1,6 +1,6 @@
import { FolderIcon } from '@heroicons/react/solid';
import Highlighter from 'react-highlight-words';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilValue } from 'recoil';
import { onDriveItemDownloadClick } from '../common';
import ResultContext from './result-context';
import { Button } from '@atoms/button/button';
@@ -15,18 +15,14 @@ import { SearchInputState } from '@features/search/state/search-input';
import { UserType } from '@features/users/types/user';
import { useDrivePreview } from '@features/drive/hooks/use-drive-preview';
import Media from '@molecules/media';
import { DriveCurrentFolderAtom } from '@views/client/body/drive/browser';
import { useHistory } from 'react-router-dom';
import RouterServices from '@features/router/services/router-service';
import { useCurrentUser } from 'app/features/users/hooks/use-current-user';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import { DocumentIcon } from '@views/client/body/drive/documents/document-icon';
export default (props: { driveItem: DriveItem & { user?: UserType }}) => {
const history = useHistory();
const input = useRecoilValue(SearchInputState);
const { user } = useCurrentUser();
const [_, setParentId] = useRecoilState(DriveCurrentFolderAtom({ initialFolderId: 'user_'+user?.id }));
const file = props.driveItem;
const name = file?.name;
const extension = name?.split('.').pop();
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
type TreeItem = { [key: string]: File | TreeItem };
export type FileTreeObject = {
@@ -10,7 +10,6 @@ import AlertManager from '@features/global/services/alert-manager-service';
import Languages from '@features/global/services/languages-service';
import JWTStorage from '@features/auth/jwt-storage-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
import { useCurrentUser } from '@features/users/hooks/use-current-user';
import UserAPIClient from '../../features/users/api/user-api-client';
import { getUser } from '@features/users/hooks/use-user-list';
@@ -68,6 +67,7 @@ class CurrentUser extends Observable {
const data = {
status: user.tutorial_status,
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
Api.post('/ajax/users/account/set_tutorial_status', data, () => {});
}
@@ -8,7 +8,6 @@ import {
onChangeCompanyApplications,
} from '../state/company-applications';
import CompanyApplicationsAPIClient from 'app/features/applications/api/company-applications-api-client';
import { useCurrentCompany } from '../../companies/hooks/use-companies';
import { Application } from 'app/features/applications/types/application';
import { LoadingState } from 'app/features/global/state/atoms/Loading';
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
@@ -1,7 +1,6 @@
import { atomFamily, selectorFamily } from 'recoil';
import { Application } from 'app/features/applications/types/application';
import Collections from 'app/deprecated/CollectionsV1/Collections/Collections';
import _ from 'lodash';
//Retro compatibility
@@ -1,9 +1,7 @@
import { atomFamily } from 'recoil';
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
import { CompanyType } from '@features/companies/types/company';
import CompanyAPIClient from '@features/companies/api/company-api-client';
import _ from 'lodash';
const companies: { [key: string]: CompanyType } = {};
@@ -5,7 +5,6 @@ import Languages from '@features/global/services/languages-service';
import { ToasterService as Toaster } from '@features/global/services/toaster-service';
import { ConsoleMemberRole } from '@features/console/types/types';
import Logger from '@features/global/framework/logger-service';
import { JWTDataType } from '@features/auth/jwt-storage-service';
class ConsoleService {
logger: Logger.Logger;
@@ -1,4 +1,4 @@
import React, { MouseEventHandler } from 'react';
import React from 'react';
import {useDraggable} from '@dnd-kit/core';
type DraggableProps={
@@ -7,7 +7,7 @@ type DraggableProps={
}
export function Draggable(props:DraggableProps) {
const {attributes, listeners, setNodeRef, transform} = useDraggable({
const { attributes, listeners, setNodeRef } = useDraggable({
id: `draggable-${props.id+1}`,
data: {
child: props.children
@@ -17,7 +17,7 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
const companyId = useRouterCompany();
const sharedFilter = useRecoilValue(SharedWithMeFilterState);
const sortItem = useRecoilValue(DriveItemSort);
const [paginateItem, _] = useRecoilState(DriveItemPagination);
const [ paginateItem ] = useRecoilState(DriveItemPagination);
const { getQuota } = useUserQuota();
const refresh = useRecoilCallback(
@@ -138,9 +138,11 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
);
const update = useCallback(
async (update: Partial<DriveItem>, id: string, parentId: string) => {
async (update: Partial<DriveItem>, id: string, parentId: string, previousName?: string) => {
try {
await DriveApiClient.update(companyId, id, update);
const newItem = await DriveApiClient.update(companyId, id, update);
if (previousName && previousName !== newItem.name && !update.name)
ToasterService.warn(Languages.t('hooks.use-drive-actions.update_caused_a_rename', [previousName, newItem.name]));
await refresh(id || '', true);
if (!inPublicSharing) await refresh(parentId || '', true);
if (update?.parent_id !== parentId) await refresh(update?.parent_id || '', true);
@@ -1,7 +1,7 @@
import { ToasterService } from '@features/global/services/toaster-service';
import { LoadingStateInitTrue } from '@features/global/state/atoms/Loading';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useCallback, useState } from 'react';
import { useCallback } from 'react';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { DriveItemAtom, DriveItemChildrenAtom, DriveItemPagination } from '../state/store';
import { DriveItem } from '../types';
@@ -20,7 +20,7 @@ export const useDriveItem = (id: string) => {
// const children = useRecoilValue(DriveItemChildrenAtom(id));
const [children, setChildren] = useRecoilState(DriveItemChildrenAtom(id));
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
const [_, setPaginateItem] = useRecoilState(DriveItemPagination);
const [, setPaginateItem] = useRecoilState(DriveItemPagination);
const {
refresh: refreshItem,
create,
@@ -171,6 +171,7 @@ export const useDriveItem = (id: string) => {
updateLevel,
remove,
refresh,
restore,
loadNextPage,
};
};
@@ -6,18 +6,15 @@ import { useRecoilState } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import { DriveViewerState } from '../state/viewer';
import { DriveItem } from '../types';
import { useHistory } from 'react-router-dom';
import RouterServices from '@features/router/services/router-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { DriveCurrentFolderAtom } from 'app/views/client/body/drive/browser';
import { useCurrentUser } from 'app/features/users/hooks/use-current-user';
export const useDrivePreviewModal = () => {
const history = useHistory();
const company = useRouterCompany();
const [status, setStatus] = useRecoilState(DriveViewerState);
const { user } = useCurrentUser();
const [ parentId, setParentId ] = useRecoilState(
const [ , setParentId ] = useRecoilState(
DriveCurrentFolderAtom({ initialFolderId: 'user_'+user?.id }),
);
@@ -1,4 +1,3 @@
import { string } from 'prop-types';
import { atom } from 'recoil';
export type SharedWithMeFilter = {
@@ -23,7 +23,7 @@ export const useSearchDriveItems = () => {
const [loading, setLoading] = useRecoilState(LoadingState('useSearchDriveItems'));
const [searched, setSearched] = useRecoilState(SearchDriveItemsResultsState(companyId));
const [recent, setRecent] = useRecoilState(RecentDriveItemsState(companyId));
const [, setRecent] = useRecoilState(RecentDriveItemsState(companyId));
const opt = _.omitBy(
{
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef } from "react";
import { useEffect } from "react";
import LoginService from '@features/auth/login-service';
import UserAPIClient from '@features/users/api/user-api-client';
import { useRecoilState } from 'recoil';
import { CurrentUserState } from '../state/atoms/current-user';
import Languages from '@features/global/services/languages-service';
@@ -4,8 +4,6 @@ import { atomFamily, selectorFamily, useSetRecoilState } from 'recoil';
import { WorkspaceType } from '@features/workspaces/types/workspace';
import WorkspaceAPIClient from '@features/workspaces/api/workspace-api-client';
import Logger from '@features/global/framework/logger-service';
import _ from 'lodash';
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
const logger = Logger.getLogger('WorkspaceListState');
@@ -10,9 +10,9 @@ import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store';
import { formatBytes } from '@features/drive/utils';
import useRouterCompany from '@features/router/hooks/use-router-company';
import _, { set } from 'lodash';
import _ from 'lodash';
import { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { atomFamily, useRecoilState, useSetRecoilState, useRecoilValue } from 'recoil';
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
import { DrivePreview } from '../../viewer/drive-preview';
import {
useOnBuildContextMenu,
@@ -40,7 +40,7 @@ import useRouteState from 'app/features/router/hooks/use-route-state';
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
import MenusManager from '@components/menus/menus-manager.jsx';
import Languages from 'features/global/services/languages-service';
import {DndContext, useSensors, useSensor, PointerSensor, DragOverlay} from '@dnd-kit/core';
import { DndContext, useSensors, useSensor, PointerSensor, DragOverlay } from '@dnd-kit/core';
import { Droppable } from 'app/features/dragndrop/hook/droppable';
import { Draggable } from 'app/features/dragndrop/hook/draggable';
import { useDriveActions } from '@features/drive/hooks/use-drive-actions';
@@ -49,7 +49,6 @@ import { useCurrentUser } from 'app/features/users/hooks/use-current-user';
import { ConfirmModal } from './modals/confirm-move';
import { useHistory } from 'react-router-dom';
import { SortIcon } from 'app/atoms/icons-agnostic';
import { useDrivePreview, useDrivePreviewLoading } from 'app/features/drive/hooks/use-drive-preview';
export const DriveCurrentFolderAtom = atomFamily<
string,
@@ -78,12 +77,9 @@ export default memo(
? (user?.companies || []).find(company => company?.company.id === companyId)?.role
: 'member';
setTdriveTabToken(tdriveTabContextToken || null);
const [filter, __] = useRecoilState(SharedWithMeFilterState);
const { viewId, dirId, itemId } = useRouteState();
const { status } = useDrivePreview();
const { openWithId, close } = useDrivePreview();
const [ filter ] = useRecoilState(SharedWithMeFilterState);
const { viewId, dirId } = useRouteState();
const [sortLabel] = useRecoilState(DriveItemSort)
const { loading: isModalLoading } = useDrivePreviewLoading();
const [parentId, _setParentId] = useRecoilState(
DriveCurrentFolderAtom({
context: context,
@@ -22,9 +22,7 @@ import { copyToClipboard } from '@features/global/utils/CopyClipboard';
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
import { getCurrentUserList } from '@features/users/hooks/use-user-list';
import useRouteState from 'app/features/router/hooks/use-route-state';
import RouterServices from '@features/router/services/router-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
import _, { set } from 'lodash';
import _ from 'lodash';
import Languages from 'features/global/services/languages-service';
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
import FeatureTogglesService, {
@@ -42,7 +40,7 @@ export const useOnBuildContextMenu = (
const [checkedIds, setChecked] = useRecoilState(DriveItemSelectedList);
const checked = children.filter(c => checkedIds[c.id]);
const [_, setParentId] = useRecoilState(
const [setParentId] = useRecoilState(
DriveCurrentFolderAtom({ initialFolderId: initialParentId || 'root' }),
);
@@ -59,11 +57,6 @@ export const useOnBuildContextMenu = (
const setUsersModalState = useSetRecoilState(UsersModalAtom);
const { open: preview } = useDrivePreview();
const { viewId } = useRouteState();
const company = useRouterCompany();
function getIdsFromArray(arr: DriveItem[]): string[] {
return arr.map(obj => obj.id);
}
return useCallback(
async (parent?: Partial<DriveItemDetails> | null, item?: DriveItem) => {
@@ -148,6 +141,7 @@ export const useOnBuildContextMenu = (
},
item.id,
item.parent_id,
item.name,
);
},
}),
@@ -377,7 +371,7 @@ export const useOnBuildContextMenu = (
};
export const useOnBuildFileTypeContextMenu = () => {
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
const [, setFilter] = useRecoilState(SharedWithMeFilterState);
const mimeTypes = [
{ key: Languages.t('components.item_context_menu.all'), value: '' },
{ key: 'CSV', value: 'text/csv' },
@@ -413,8 +407,8 @@ export const useOnBuildFileTypeContextMenu = () => {
};
export const useOnBuildPeopleContextMenu = () => {
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
const [_userList, setUserList] = useState(getCurrentUserList());
const [, setFilter] = useRecoilState(SharedWithMeFilterState);
const [_userList] = useState(getCurrentUserList());
let userList = _userList;
userList = _.uniqBy(userList, 'id');
return useCallback(() => {
@@ -438,7 +432,7 @@ export const useOnBuildPeopleContextMenu = () => {
};
export const useOnBuildDateContextMenu = () => {
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
const [, setFilter] = useRecoilState(SharedWithMeFilterState);
return useCallback(() => {
const menuItems = [
{
@@ -9,14 +9,12 @@ export type DriveItemProps = {
checked: boolean;
onClick?: () => void;
onBuildContextMenu: () => Promise<any[]>;
inPublicSharing?: boolean;
};
export type DriveItemOverlayProps = {
item: DriveItem|null;
className: string;
};
export const menuBuilder = async () => {};
export const CheckableIcon = ({
show,
@@ -5,13 +5,12 @@ import Menu from '@components/menus/menu';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useDrivePreview } from '@features/drive/hooks/use-drive-preview';
import { formatBytes } from '@features/drive/utils';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { PublicIcon } from '../components/public-icon';
import { CheckableIcon, DriveItemOverlayProps, DriveItemProps } from './common';
import './style.scss';
import { useHistory } from 'react-router-dom';
import RouterServices from '@features/router/services/router-service';
import useRouteState from 'app/features/router/hooks/use-route-state';
import { DocumentIcon } from './document-icon';
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
import { formatDateShort } from 'app/features/global/utils/Numbers';
@@ -23,11 +22,10 @@ export const DocumentRow = ({
checked,
onClick,
onBuildContextMenu,
inPublicSharing,
}: DriveItemProps) => {
const history = useHistory();
const [hover, setHover] = useState(false);
const { open, close, isOpen } = useDrivePreview();
const {open} = useDrivePreview();
const company = useRouterCompany();
const preview = () => {
@@ -1,7 +1,7 @@
import { DesktopComputerIcon } from '@heroicons/react/solid';
import { Base } from '@atoms/text';
export const DriveItem = ({ className, onClick }: { className?: string; onClick: Function }) => {
export const DriveItem = ({ className, onClick }: { className?: string; onClick: () => void }) => {
return (
<div
className={
@@ -1,4 +1,3 @@
import { useCompanyApplications } from 'app/features/applications/hooks/use-company-applications';
import Browser from './browser';
import { SelectorModal } from './modals/selector';
@@ -11,7 +11,7 @@ export type ConfirmModalType = {
parent_id: string;
mode: 'move' | 'select-file' | 'select-files';
title: string;
onSelected: (ids: string[]) => Promise<void>;
onSelected?: (ids: string[]) => Promise<void>;
};
export const ConfirmModalAtom = atom<ConfirmModalType>({
@@ -21,7 +21,6 @@ export const ConfirmModalAtom = atom<ConfirmModalType>({
parent_id: '',
mode: 'move',
title: '',
onSelected: async () => {},
},
});
@@ -40,7 +39,7 @@ const ConfirmModalContent = () => {
const handleClose = async () => {
setLoading(true);
await state.onSelected(selected.map((i) => i.id));
state.onSelected && await state.onSelected(selected.map((i) => i.id));
setState({ ...state, open: false });
setLoading(false);
};
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { useRecoilState } from 'recoil';
import { CreateModalAtom, CreateModalAtomType } from '.';
import { CreateModalAtom } from '.';
import { Button } from '@atoms/button/button';
import { Input } from '@atoms/input/input-text';
import { Info } from '@atoms/text';
@@ -9,7 +9,7 @@ import Languages from "features/global/services/languages-service";
export const CreateFolder = () => {
const [name, setName] = useState<string>('');
const [loading, _] = useState<boolean>(false);
const [loading] = useState<boolean>(false);
const [state, setState] = useRecoilState(CreateModalAtom);
const { create } = useDriveActions();
const inputRef = useRef<HTMLDivElement>(null);
@@ -1,6 +1,5 @@
import { Button } from '@atoms/button/button';
import { Input } from '@atoms/input/input-text';
import { Info } from '@atoms/text';
import { useDriveActions } from '@features/drive/hooks/use-drive-actions';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
@@ -7,9 +7,7 @@ import { Application } from '@features/applications/types/application';
import { Transition } from '@headlessui/react';
import {
ChevronLeftIcon,
DocumentDownloadIcon,
FolderAddIcon,
FolderDownloadIcon,
LinkIcon,
} from '@heroicons/react/outline';
import { ReactNode } from 'react';
@@ -35,8 +33,6 @@ export const CreateModalAtom = atom<CreateModalAtomType>({
});
export const CreateModal = ({
selectFromDevice,
selectFolderFromDevice,
addFromUrl,
}: {
selectFromDevice: () => void;
@@ -5,14 +5,12 @@ import Languages from 'features/global/services/languages-service';
export const AccessLevel = ({
level,
onChange,
canRemove,
hiddenLevels,
className,
}: {
disabled?: boolean;
level: DriveFileAccessLevel | null;
onChange: (level: DriveFileAccessLevel & 'remove') => void;
canRemove?: boolean;
className?: string;
hiddenLevels?: string[];
}) => {
@@ -4,7 +4,6 @@ import Avatar from '@atoms/avatar';
import { Base, Info } from '@atoms/text';
import { atom, useRecoilState } from 'recoil';
import { useDriveItem } from '@features/drive/hooks/use-drive-item';
import AlertManager from '@features/global/services/alert-manager-service';
import { useCurrentUser } from '@features/users/hooks/use-current-user';
import { useUser } from '@features/users/hooks/use-user';
import currentUserService from '@features/users/services/current-user-service';
@@ -55,7 +54,7 @@ const UserAccessLevel = ({
}) => {
const user = useUser(userId);
const { user: currentUser } = useCurrentUser();
const { item, loading, updateLevel } = useDriveItem(id);
const { updateLevel } = useDriveItem(id);
const [level, setLevel] = useState<DriveFileAccessLevel>(role == "admin" ? "manage" : "read");
//const level = role == "admin" ? "manage" : "read";
@@ -78,7 +77,6 @@ const UserAccessLevel = ({
<AccessLevel
disabled={userId === currentUser?.id}
level={level}
canRemove
onChange={level => {
setLevel(level);
updateLevel(userId || '', level);
@@ -15,7 +15,7 @@ export type SelectorModalType = {
parent_id: string;
mode: 'move' | 'select-file' | 'select-files';
title: string;
onSelected: (ids: string[]) => Promise<void>;
onSelected?: (ids: string[]) => Promise<void>;
};
export const SelectorModalAtom = atom<SelectorModalType>({
@@ -25,7 +25,6 @@ export const SelectorModalAtom = atom<SelectorModalType>({
parent_id: '',
mode: 'move',
title: '',
onSelected: async () => {},
},
});
@@ -39,7 +38,7 @@ export const SelectorModal = () => {
);
};
const SelectorModalContent = (key:any,showfiles:boolean) => {
const SelectorModalContent = (key: any) => {
const [state, setState] = useRecoilState(SelectorModalAtom);
const [selected, setSelected] = useState<DriveItem[]>([]);
const [loading, setLoading] = useState(false);
@@ -91,8 +90,8 @@ const SelectorModalContent = (key:any,showfiles:boolean) => {
</div>
</div>
))}
{key.showfiles && (
{key.showfiles && (
<>
{files.map(file => (
<div
@@ -135,7 +134,7 @@ const SelectorModalContent = (key:any,showfiles:boolean) => {
className="float-right"
onClick={async () => {
setLoading(true);
await state.onSelected(selected.map(i => i.id));
state.onSelected && await state.onSelected(selected.map(i => i.id));
setState({ ...state, open: false });
setLoading(false);
}}
@@ -41,7 +41,7 @@ const AccessModalContent = (props: {
onCloseModal: () => void,
}) => {
const { id } = props;
const { item, access, loading, update, refresh } = useDriveItem(id);
const { item, access, loading, refresh } = useDriveItem(id);
const { refresh: refreshCompany } = useCurrentCompany();
useEffect(() => {
refresh(id);
@@ -1,16 +1,11 @@
import Avatar from '@atoms/avatar';
import A from '@atoms/link';
import { Modal, ModalContent } from '@atoms/modal';
import { Base } from '@atoms/text';
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
import { Application } from '@features/applications/types/application';
import { Transition } from '@headlessui/react';
import {
ChevronLeftIcon,
DocumentDownloadIcon,
FolderAddIcon,
FolderDownloadIcon,
LinkIcon,
} from '@heroicons/react/outline';
import { ReactNode } from 'react';
import { atom, useRecoilState } from 'recoil';
@@ -34,14 +29,12 @@ export const UploadModelAtom = atom<UploadModalAtomType>({
export const UploadModal = ({
selectFromDevice,
selectFolderFromDevice,
addFromUrl,
}: {
selectFromDevice: () => void;
selectFolderFromDevice: () => void;
addFromUrl: (url: string, name: string) => void;
}) => {
const [state, setState] = useRecoilState(UploadModelAtom);
const { applications } = useCompanyApplications();
return (
<Modal
@@ -10,7 +10,7 @@ import FeatureTogglesService, {
FeatureNames,
} from '@features/global/services/feature-toggles-service';
export default ({ sidebar }: { sidebar?: boolean }): JSX.Element => {
export default (): JSX.Element => {
const { user } = useCurrentUser();
if (!user) return <></>;
@@ -61,25 +61,22 @@ export default class UserParameter extends Component {
if (this.state.page === 1) {
return (
<form className="" autoComplete="off">
<div className="title">{this.state.i18n.t('scenes.apps.account.title')}</div>
<div className="group_section">
<Attribute
label={this.state.i18n.t('scenes.apps.account.languages.menu_title')}
description={this.state.i18n.t('scenes.apps.account.languages.text')}
>
<div className="parameters_form">
<select
value={this.state.i18n.language}
onChange={ev => currentUserService.updateLanguage(ev.target.value)}
>
<option value="en">English</option>
<option value="fr">Français</option>
<option value="ru">Русский</option>
<option value="vi">Tiếng Việt</option>
</select>
</div>
</Attribute>
</div>
<Attribute
label={this.state.i18n.t('scenes.apps.account.languages.menu_title')}
description={this.state.i18n.t('scenes.apps.account.languages.text')}
>
<div className="parameters_form">
<select
value={this.state.i18n.language}
onChange={ev => currentUserService.updateLanguage(ev.target.value)}
>
<option value="en">English</option>
<option value="fr">Français</option>
<option value="ru">Русский</option>
<option value="vi">Tiếng Việt</option>
</select>
</div>
</Attribute>
</form>
);
}
@@ -24,7 +24,7 @@ export const CreateModalWithUploadZones = ({ initialParentId }: { initialParentI
const setUploadModalState = useSetRecoilState(UploadModelAtom);
const { uploadTree, uploadFromUrl } = useDriveUpload();
const { user } = useCurrentUser();
const [parentId, _] = useRecoilState(
const [ parentId ] = useRecoilState(
DriveCurrentFolderAtom({ initialFolderId: initialParentId || 'user_'+user?.id }),
);
@@ -96,7 +96,7 @@ export const CreateModalWithUploadZones = ({ initialParentId }: { initialParentI
export default () => {
const { user } = useCurrentUser();
const { viewId, dirId } = useRouteState();
const [parentId, _] = useRecoilState(DriveCurrentFolderAtom({ initialFolderId: dirId || viewId || 'user_'+user?.id }));
const [ parentId ] = useRecoilState(DriveCurrentFolderAtom({ initialFolderId: dirId || viewId || 'user_'+user?.id }));
const { access, item } = useDriveItem(parentId);
const { children: trashChildren } = useDriveItem(viewId === 'trash' ? 'trash' : 'trash_'+user?.id);
const uploadZoneRef = useRef<UploadZone | null>(null);
@@ -53,7 +53,7 @@ export default () => {
<div className="sm:hidden block mb-2">
<div className="flex flex-row space-between w-full">
<div className="grow">
<Account sidebar />
<Account />
</div>
<AppGrid />
</div>
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import { Transition } from '@headlessui/react';
import { fadeTransition } from 'src/utils/transitions';
@@ -24,7 +25,6 @@ import Controls from './controls';
interface DrivePreviewProps {
items: DriveItem[];
}
export const DrivePreview: React.FC<DrivePreviewProps> = ({ items }) => {
const history = useHistory();
const company = useRouterCompany();
@@ -64,6 +64,7 @@ export const DrivePreview: React.FC<DrivePreviewProps> = ({ items }) => {
useEffect(() => {
if (items.length < 2)
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
addShortcut({ shortcut: 'Right', handler: handleSwitchRight });
addShortcut({ shortcut: 'Left', handler: handleSwitchLeft });
@@ -183,4 +184,7 @@ export const DrivePreview: React.FC<DrivePreviewProps> = ({ items }) => {
</Transition>
</Modal>
);
};
};
DrivePreview.propTypes = {
items: PropTypes.any.isRequired,
}
@@ -12,7 +12,6 @@ import { Modal } from '@atoms/modal';
import * as Text from '@atoms/text';
import { addShortcut, removeShortcut } from '@features/global/services/shortcut-service';
import { formatSize } from '@features/global/utils/format-file-size';
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
import currentUserService from '@features/users/services/current-user-service';
import { UserType } from '@features/users/types/user';
import {
@@ -125,13 +124,12 @@ const Navigation = () => {
};
const Footer = () => {
const { status, close } = useFileViewer();
const { status } = useFileViewer();
const { download } = useViewerDisplayData();
const { type = '' } = useViewerDisplayData();
const user = status?.details?.user as UserType;
const name = status.details?.metadata?.name;
const extension = name?.split('.').pop();
const workspaceId = useRouterWorkspace();
return (
<>
+6651 -6274
View File
File diff suppressed because it is too large Load Diff