Skip to content

Commit b7ea52a

Browse files
authored
Zero-db support
1 parent 236c963 commit b7ea52a

File tree

1 file changed

+104
-62
lines changed

1 file changed

+104
-62
lines changed

mega.ts

Lines changed: 104 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { Mutex } from 'async-mutex';
2-
import * as Mega from 'megajs';
3-
import { Config, MegaAccount, UploadMode, UploadQuery } from './types';
4-
import database from './database';
5-
import { Readable } from 'stream';
1+
import { Mutex } from "async-mutex";
2+
import * as Mega from "megajs";
3+
import { Config, MegaAccount, UploadMode, UploadQuery } from "./types";
4+
import database from "./database";
5+
import { Readable } from "stream";
66

77
function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
88
return new Promise((resolve, reject) => {
99
const chunks: Buffer[] = [];
10-
stream.on('data', (chunk) => chunks.push(chunk));
11-
stream.on('end', () => resolve(Buffer.concat(chunks)));
12-
stream.on('error', (err) => {
13-
console.error('Error stream:', err);
10+
stream.on("data", (chunk) => chunks.push(chunk));
11+
stream.on("end", () => resolve(Buffer.concat(chunks)));
12+
stream.on("error", (err) => {
13+
console.error("Error stream:", err);
1414
reject(err);
1515
});
1616
});
@@ -19,24 +19,27 @@ function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
1919
async function fullUpload(uploadStream: any): Promise<any> {
2020
return new Promise((resolve, reject) => {
2121
// Timeout for lag uploads
22-
const timeout = setTimeout(() => {
23-
reject(new Error('Upload timeout after 5 mins'));
24-
}, 5 * 60 * 1000);
25-
26-
uploadStream.on('complete', (file: any) => {
22+
const timeout = setTimeout(
23+
() => {
24+
reject(new Error("Upload timeout after 5 mins"));
25+
},
26+
5 * 60 * 1000,
27+
);
28+
29+
uploadStream.on("complete", (file: any) => {
2730
clearTimeout(timeout);
2831
file.link((err: any, url: string) => {
2932
if (err) {
30-
console.error('Error linking file:', err);
33+
console.error("Error linking file:", err);
3134
return reject(err);
3235
}
3336
resolve({ name: file.name, size: file.size, mime: file.mime, url });
3437
});
3538
});
3639

37-
uploadStream.on('error', (err: any) => {
40+
uploadStream.on("error", (err: any) => {
3841
clearTimeout(timeout);
39-
console.error('Could not upload media:', err);
42+
console.error("Could not upload media:", err);
4043
reject(err);
4144
});
4245
});
@@ -51,22 +54,25 @@ class MegaClient {
5154
constructor(config: Config) {
5255
this.config = config;
5356
}
54-
57+
5558
async initialize() {
56-
await database.initialize(this.config.DATABASE_URL);
59+
if (this.config.FILENAMES || this.config.autoDelete?.enable) {
60+
await database.initialize(this.config.DATABASE_URL);
61+
}
62+
5763
const creds = this.config.mega.accounts;
58-
if (!creds) throw new Error('No MEGA accounts found');
64+
if (!creds) throw new Error("No MEGA accounts found");
5965

60-
for (const entry of creds.split(';')) {
61-
const [email, password] = entry.split(':');
66+
for (const entry of creds.split(";")) {
67+
const [email, password] = entry.split(":");
6268
if (email && password) {
6369
try {
6470
const storage = new (Mega as any).Storage({ email, password });
6571
await storage.ready;
66-
this.accounts.push({
67-
email,
68-
password,
69-
storage
72+
this.accounts.push({
73+
email,
74+
password,
75+
storage,
7076
});
7177

7278
this.accountMutexes.set(email, new Mutex());
@@ -77,68 +83,71 @@ class MegaClient {
7783
}
7884
}
7985

80-
if (!this.accounts.length) throw new Error('No valid MEGA accounts');
86+
if (!this.accounts.length) throw new Error("No valid MEGA accounts");
8187
}
82-
8388
private selectAccount(mode: UploadMode, query?: UploadQuery): MegaAccount {
84-
if (mode === 'dual' && query?.email) {
85-
const account = this.accounts.find(a => a.email === query.email);
89+
if (mode === "dual" && query?.email) {
90+
const account = this.accounts.find((a) => a.email === query.email);
8691
if (!account) throw new Error(`No account for ${query.email}`);
8792
return account;
8893
}
89-
94+
9095
const account = this.accounts[this.currentAccountIndex];
91-
this.currentAccountIndex = (this.currentAccountIndex + 1) % this.accounts.length;
96+
this.currentAccountIndex =
97+
(this.currentAccountIndex + 1) % this.accounts.length;
9298
return account;
9399
}
94100

95101
getAccountByEmail(email: string): MegaAccount | null {
96-
return this.accounts.find(acc => acc.email === email) || null;
102+
return this.accounts.find((acc) => acc.email === email) || null;
97103
}
98104

99105
getZeroAcc(): MegaAccount {
100-
if (!this.accounts.length) throw new Error('No accounts available');
106+
if (!this.accounts.length) throw new Error("No accounts available");
101107
return this.accounts[0];
102108
}
103109

104110
async uploadFile(
105111
filename: string,
106112
input: NodeJS.ReadableStream,
107-
mode: UploadMode = 'single',
108-
query?: UploadQuery
113+
mode: UploadMode = "single",
114+
query?: UploadQuery,
109115
) {
110116
const account = this.selectAccount(mode, query);
111117

112118
if (!account || !account.storage) {
113-
throw new Error('Storage not available for this account - payment required or banned');
119+
throw new Error(
120+
"Storage not available for this account - payment required or banned",
121+
);
114122
}
115123

116124
const mutex = this.accountMutexes.get(account.email);
117-
if (!mutex) throw new Error('Account mutex not found');
125+
if (!mutex) throw new Error("Account mutex not found");
118126

119127
const release = await mutex.acquire();
120128

121129
try {
122-
//console.log(`Uploading ${filename} to ${account.email}`);
123130
const buffer = await streamToBuffer(input);
124131
const size = buffer.length;
125132

126133
if (size === 0) {
127-
throw new Error('File is empty');
134+
throw new Error("File is empty");
128135
}
129136

130-
const uploadStream = account.storage.upload({
131-
name: filename,
132-
size: size,
133-
allowUploadBuffering: true
137+
const uploadStream = account.storage.upload({
138+
name: filename,
139+
size: size,
140+
allowUploadBuffering: true,
134141
});
135142

136143
Readable.from(buffer).pipe(uploadStream);
137144
const result = await fullUpload(uploadStream);
138-
// console.log(`Successfully uploaded ${filename} to ${account.email}`);
139145
return result;
140146
} catch (error) {
141-
throw new Error('Upload failed: ' + (error instanceof Error ? error.message : 'Unknown error'));
147+
throw new Error(
148+
"Upload failed: " +
149+
(error instanceof Error ? error.message : "Unknown error"),
150+
);
142151
} finally {
143152
release();
144153
}
@@ -147,37 +156,38 @@ class MegaClient {
147156
async uploadBuffer(
148157
filename: string,
149158
buffer: Buffer,
150-
mode: UploadMode = 'single',
151-
query?: UploadQuery
159+
mode: UploadMode = "single",
160+
query?: UploadQuery,
152161
) {
153162
if (!buffer || buffer.length === 0) {
154-
throw new Error('Buffer is empty or null');
163+
throw new Error("Buffer is empty or null");
155164
}
156165

157166
return this.uploadFile(filename, Readable.from(buffer), mode, query);
158167
}
159168

160169
async getFile(filePath: string) {
161170
const primary = this.getZeroAcc();
162-
const fileName = filePath.split('/').pop() || filePath;
163-
const file = Object.values(primary.storage.files).find((f: any) => f.name === fileName);
164-
if (!file) throw new Error('File not found');
171+
const fileName = filePath.split("/").pop() || filePath;
172+
const file = Object.values(primary.storage.files).find(
173+
(f: any) => f.name === fileName,
174+
);
175+
if (!file) throw new Error("File not found");
165176
return file;
166177
}
167178

168179
async scheduleDelete(name: string, mins: number) {
169180
const deleteTime = Date.now() + mins * 60_000;
170181
await database.save({ fileName: name, deleteTime });
171-
// console.log(`Scheduled ${name} for deletion in ${mins} minutes`);
172182
}
173183

174184
async processExpired() {
175185
const now = Date.now();
176186
const expired = await database.findExpired(now);
177-
187+
178188
for (const record of expired) {
179189
let fileDeleted = false;
180-
190+
181191
for (const account of this.accounts) {
182192
try {
183193
const files = account.storage.root.children;
@@ -190,7 +200,10 @@ class MegaClient {
190200
break;
191201
}
192202
} catch (error) {
193-
console.error(`Failed to delete ${record.fileName} from ${account.email}:`, error);
203+
console.error(
204+
`Failed to delete ${record.fileName} from ${account.email}:`,
205+
error,
206+
);
194207
}
195208
}
196209

@@ -208,20 +221,49 @@ class MegaClient {
208221

209222
async cleanup() {
210223
try {
211-
await this.processExpired();
224+
if (this.config.autoDelete?.enable) {
225+
await this.processExpired();
226+
}
212227
await database.disconnect();
213-
console.log('Cleanup completed');
228+
console.log("Cleanup completed");
214229
} catch (error) {
215-
console.error('Cleanup failed:', error);
230+
console.error("Cleanup failed:", error);
216231
}
217232
}
218-
219233
getAccountCount(): number {
220234
return this.accounts.length;
221235
}
222236

223237
getAccountEmails(): string[] {
224-
return this.accounts.map(acc => acc.email);
238+
return this.accounts.map((acc) => acc.email);
239+
}
240+
241+
public getAccounts(): MegaAccount[] {
242+
return this.accounts;
243+
}
244+
245+
public async deleteFileByName(fileName: string): Promise<boolean> {
246+
for (const account of this.accounts) {
247+
try {
248+
await account.storage.reload();
249+
const file = account.storage.find(fileName);
250+
if (file && !file.directory) {
251+
await file.delete(true);
252+
return true;
253+
}
254+
} catch (error) {
255+
console.error(
256+
`Failed to delete ${fileName} from ${account.email}:`,
257+
error,
258+
);
259+
}
260+
}
261+
return false;
262+
}
263+
public async getFileNameFromUrl(url: string): Promise<string> {
264+
const file = Mega.File.fromURL(url);
265+
await file.loadAttributes();
266+
return file.name || "unknown";
225267
}
226268

227269
async getStorageInfo(): Promise<any[]> {
@@ -239,7 +281,7 @@ class MegaClient {
239281
console.error(`Failed to get acc info for ${account.email}:`, error);
240282
info.push({
241283
email: account.email,
242-
error: 'Failed to get acc info',
284+
error: "Failed to get acc info",
243285
});
244286
}
245287
}

0 commit comments

Comments
 (0)