Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import fs from 'node:fs';
|
|
import { unlink } from 'node:fs/promises';
|
|
import { isNativeError } from 'node:util/types';
|
|
|
|
import type { IUpload } from '@rocket.chat/core-typings';
|
|
import mkdirp from 'mkdirp';
|
|
|
|
import { UploadFS } from './ufs';
|
|
import type { StoreOptions } from './ufs-store';
|
|
import { Store } from './ufs-store';
|
|
|
|
type LocalStoreOptions = StoreOptions & {
|
|
mode?: string;
|
|
path?: string;
|
|
writeMode?: number;
|
|
};
|
|
|
|
export class LocalStore extends Store {
|
|
protected getPath: (file: string) => string;
|
|
|
|
constructor(options: LocalStoreOptions) {
|
|
// Default options
|
|
options = {
|
|
mode: '0744',
|
|
path: 'ufs/uploads',
|
|
writeMode: 0o744,
|
|
...options,
|
|
};
|
|
|
|
// Check options
|
|
if (typeof options.mode !== 'string') {
|
|
throw new TypeError('LocalStore: mode is not a string');
|
|
}
|
|
if (typeof options.path !== 'string') {
|
|
throw new TypeError('LocalStore: path is not a string');
|
|
}
|
|
if (typeof options.writeMode !== 'number') {
|
|
throw new TypeError('LocalStore: writeMode is not a string');
|
|
}
|
|
|
|
super(options);
|
|
|
|
// Private attributes
|
|
const { mode } = options;
|
|
const { path } = options;
|
|
const { writeMode } = options;
|
|
|
|
fs.stat(path, (err) => {
|
|
if (err) {
|
|
// Create the directory
|
|
mkdirp(path, { mode })
|
|
.then(() => {
|
|
console.info(`LocalStore: store created at ${path}`);
|
|
})
|
|
.catch((err) => {
|
|
console.error(`LocalStore: cannot create store at ${path} (${err.message})`);
|
|
});
|
|
} else {
|
|
// Set directory permissions
|
|
fs.chmod(path, mode, (err) => {
|
|
err && console.error(`LocalStore: cannot set store permissions ${mode} (${err.message})`);
|
|
});
|
|
}
|
|
});
|
|
|
|
this.getPath = function (file) {
|
|
return path + (file ? `/${file}` : '');
|
|
};
|
|
|
|
this.delete = async (fileId, options) => {
|
|
const path = await this.getFilePath(fileId);
|
|
try {
|
|
await unlink(path);
|
|
} catch (error) {
|
|
if (!isNativeError(error) || !('code' in error) || !(error.code === 'ENOENT')) {
|
|
throw error;
|
|
}
|
|
}
|
|
await this.removeById(fileId, { session: options?.session }, true);
|
|
};
|
|
|
|
this.getReadStream = async (fileId: string, file: IUpload, options?: { start?: number; end?: number }) => {
|
|
options = Object.assign({}, options);
|
|
return fs.createReadStream(await this.getFilePath(fileId, file), {
|
|
flags: 'r',
|
|
encoding: undefined,
|
|
autoClose: true,
|
|
start: options.start,
|
|
end: options.end,
|
|
});
|
|
};
|
|
|
|
this.getWriteStream = async (fileId: string, file: IUpload, options?: { start?: number }) => {
|
|
options = Object.assign({}, options);
|
|
return fs.createWriteStream(await this.getFilePath(fileId, file), {
|
|
flags: 'a',
|
|
encoding: undefined,
|
|
mode: writeMode,
|
|
start: options.start,
|
|
});
|
|
};
|
|
}
|
|
|
|
override async getFilePath(fileId: string, fileParam?: IUpload): Promise<string> {
|
|
const file = fileParam || (await this.getCollection().findOne(fileId, { projection: { extension: 1 } }));
|
|
return (file && this.getPath(fileId + (file.extension ? `.${file.extension}` : ''))) || '';
|
|
}
|
|
}
|
|
|
|
// Add store to UFS namespace
|
|
UploadFS.store.Local = LocalStore;
|