@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
123 lines (93 loc) • 3.27 kB
text/typescript
import path from 'node:path';
import fs, {ReadStream} from 'node:fs';
import os from 'node:os';
import {StreamOptions} from 'node:stream';
import Transport, {TransportStreamOptions} from 'winston-transport';
import {JobLogFileProcessor} from './JobLogFileProcessor.ts';
interface JobLogStreamOptions extends TransportStreamOptions {
file_options?: StreamOptions<ReadStream>;
zippedArchive?: boolean;
extension?: string;
dirname: string;
filename: string;
eol?: string;
}
function isValidFileName(filename) {
// eslint-disable-next-line no-control-regex
return !/["<>|:*?\\/\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]/g.test(filename);
}
function isValidDirName(dirname) {
// eslint-disable-next-line no-control-regex
return !/["<>|\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]/g.test(dirname);
}
const loggerDefaults: { file_options: any, eol: string } = {
file_options: { flags: 'a' },
eol: os.EOL,
};
export class JobLogFile extends Transport {
name = 'jobLogFile';
private options: JobLogStreamOptions;
private dirname: string;
private filename: string;
constructor(options: JobLogStreamOptions) {
super(options);
this.options = { ...loggerDefaults, ...options };
this.filename = options.filename;
this.dirname = options.dirname;
if (!isValidFileName(this.filename) || !isValidDirName(this.dirname)) {
throw new Error('Your path or filename contain an invalid character.');
}
}
log(info, callback) {
if (info?.jobId) {
const logStream = this.getLogStream(info.driveId, info.jobId);
logStream.write(JSON.stringify(info) + this.options.eol);
logStream.close();
this.emit('logged', info);
}
if (callback) {
callback(null, true);
}
}
async close() {
this.emit('finish');
}
getLogStream(driveId: string, jobId: string) {
driveId = driveId || '';
const dirname = this.dirname
.replace('%driveId%', driveId)
.replace('%jobId%', jobId)
.replace('//', '/');
fs.mkdirSync(dirname, { recursive: true });
const filename = path.join(dirname, this.filename)
.replace(/%JOB_ID%/g, jobId);
return fs.createWriteStream(filename, this.options.file_options);
}
async query(options, callback) {
if (!this.filename) {
throw new Error('query() may not be used when initializing with a stream');
}
options = options || {};
if (!options.jobId || !options.driveId) {
callback(null, []);
return [];
}
options.order = options.order || 'desc';
const dirname = this.dirname
.replace('%driveId%', options.driveId)
.replace('%jobId%', options.jobId)
.replace('//', '/');
fs.mkdirSync(dirname, { recursive: true });
const filename = path.join(dirname, this.filename)
.replace(/%JOB_ID%/g, options.jobId);
try {
const processor = new JobLogFileProcessor(filename, options);
const results = await processor.query();
callback(null, results);
return results;
} catch (err) {
callback(err);
throw err;
}
}
}