@lacrous/omnicloud 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +181 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +181 -7
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/server/index.js +220 -10
- package/server/web/assets/index-BjgDY1Om.js +346 -0
- package/server/web/assets/index-DZrY56Sp.css +1 -0
- package/server/web/index.html +2 -2
- package/server/web/assets/index-Caa-mAUw.js +0 -339
- package/server/web/assets/index-kc0MNEkT.css +0 -1
package/dist/index.cjs
CHANGED
|
@@ -3158,10 +3158,13 @@ var TelegramStorageProvider = class {
|
|
|
3158
3158
|
};
|
|
3159
3159
|
|
|
3160
3160
|
// ../telegram/src/connection.ts
|
|
3161
|
+
var import_node_crypto6 = require("crypto");
|
|
3161
3162
|
var import_telegram3 = require("telegram");
|
|
3162
3163
|
var import_Password = require("telegram/Password.js");
|
|
3163
3164
|
var TELEGRAM_PROVIDER = "telegram";
|
|
3164
3165
|
var LOGIN_FLOW_TTL_MS = 5 * 60 * 1e3;
|
|
3166
|
+
var QR_FLOW_TTL_MS = 5 * 60 * 1e3;
|
|
3167
|
+
var QR_APPROVAL_POLL_MS = 2e3;
|
|
3165
3168
|
var TelegramConnectionService = class {
|
|
3166
3169
|
constructor(repos, clientManager, credentials) {
|
|
3167
3170
|
this.repos = repos;
|
|
@@ -3172,6 +3175,7 @@ var TelegramConnectionService = class {
|
|
|
3172
3175
|
clientManager;
|
|
3173
3176
|
credentials;
|
|
3174
3177
|
flows = /* @__PURE__ */ new Map();
|
|
3178
|
+
qrFlows = /* @__PURE__ */ new Map();
|
|
3175
3179
|
/** Sends the Telegram confirmation code for the phone number. */
|
|
3176
3180
|
async startLogin(phone) {
|
|
3177
3181
|
this.sweepExpiredFlows();
|
|
@@ -3220,6 +3224,167 @@ var TelegramConnectionService = class {
|
|
|
3220
3224
|
}
|
|
3221
3225
|
return this.finalizeLogin(flow);
|
|
3222
3226
|
}
|
|
3227
|
+
/**
|
|
3228
|
+
* Starts a QR sign-in. Returns a flow id and the first login token. The phone
|
|
3229
|
+
* approves the token in the Telegram app; `pollQrLogin` reports the result.
|
|
3230
|
+
*/
|
|
3231
|
+
async startQrLogin() {
|
|
3232
|
+
this.sweepExpiredQrFlows();
|
|
3233
|
+
const client = newTelegramClient(this.credentials);
|
|
3234
|
+
try {
|
|
3235
|
+
await client.connect();
|
|
3236
|
+
const flow = {
|
|
3237
|
+
client,
|
|
3238
|
+
createdAt: Date.now(),
|
|
3239
|
+
approved: false,
|
|
3240
|
+
needsPassword: false,
|
|
3241
|
+
failure: null
|
|
3242
|
+
};
|
|
3243
|
+
const flowId = (0, import_node_crypto6.randomUUID)();
|
|
3244
|
+
const token = await this.exportQrToken(client);
|
|
3245
|
+
this.qrFlows.set(flowId, flow);
|
|
3246
|
+
this.watchQrApproval(flowId, flow);
|
|
3247
|
+
return { flowId, token };
|
|
3248
|
+
} catch (error) {
|
|
3249
|
+
await this.disconnectQuietly(client);
|
|
3250
|
+
throw mapTelegramError(error, "Could not start Telegram QR sign-in");
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
/**
|
|
3254
|
+
* Reports where a QR sign-in stands. While waiting, returns a fresh token
|
|
3255
|
+
* (Telegram rotates them every few seconds). On approval, completes the login.
|
|
3256
|
+
*/
|
|
3257
|
+
async pollQrLogin(flowId) {
|
|
3258
|
+
const flow = this.requireQrFlow(flowId);
|
|
3259
|
+
if (flow.failure) {
|
|
3260
|
+
this.qrFlows.delete(flowId);
|
|
3261
|
+
await this.disconnectQuietly(flow.client);
|
|
3262
|
+
throw new TelegramConnectionError(flow.failure);
|
|
3263
|
+
}
|
|
3264
|
+
if (flow.needsPassword) return { status: "password_required" };
|
|
3265
|
+
if (!flow.approved) {
|
|
3266
|
+
const token = await this.exportQrToken(flow.client).catch((error) => {
|
|
3267
|
+
this.qrFlows.delete(flowId);
|
|
3268
|
+
void this.disconnectQuietly(flow.client);
|
|
3269
|
+
throw mapTelegramError(error, "Could not refresh the Telegram QR code");
|
|
3270
|
+
});
|
|
3271
|
+
return { status: "waiting", token };
|
|
3272
|
+
}
|
|
3273
|
+
return this.completeQrLogin(flowId, flow);
|
|
3274
|
+
}
|
|
3275
|
+
/** Completes a QR sign-in for an account with two-factor authentication. */
|
|
3276
|
+
async submitQrPassword(flowId, password) {
|
|
3277
|
+
const flow = this.requireQrFlow(flowId);
|
|
3278
|
+
try {
|
|
3279
|
+
const passwordInfo = await flow.client.invoke(new import_telegram3.Api.account.GetPassword());
|
|
3280
|
+
const check = await (0, import_Password.computeCheck)(passwordInfo, password);
|
|
3281
|
+
await flow.client.invoke(new import_telegram3.Api.auth.CheckPassword({ password: check }));
|
|
3282
|
+
} catch (error) {
|
|
3283
|
+
throw mapTelegramError(error, "Telegram sign-in failed");
|
|
3284
|
+
}
|
|
3285
|
+
return this.completeQrLogin(flowId, flow);
|
|
3286
|
+
}
|
|
3287
|
+
async exportQrToken(client) {
|
|
3288
|
+
const result = await client.invoke(
|
|
3289
|
+
new import_telegram3.Api.auth.ExportLoginToken({
|
|
3290
|
+
apiId: this.credentials.apiId,
|
|
3291
|
+
apiHash: this.credentials.apiHash,
|
|
3292
|
+
exceptIds: []
|
|
3293
|
+
})
|
|
3294
|
+
);
|
|
3295
|
+
if (!(result instanceof import_telegram3.Api.auth.LoginToken)) {
|
|
3296
|
+
throw new TelegramConnectionError("Telegram did not issue a QR login token");
|
|
3297
|
+
}
|
|
3298
|
+
const tokenBase64 = Buffer.from(result.token).toString("base64url");
|
|
3299
|
+
return {
|
|
3300
|
+
url: `tg://login?token=${tokenBase64}`,
|
|
3301
|
+
expiresAt: result.expires * 1e3
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
/** Waits for the phone to approve the token, then marks the flow approved. */
|
|
3305
|
+
watchQrApproval(flowId, flow) {
|
|
3306
|
+
const timer = setInterval(() => {
|
|
3307
|
+
if (Date.now() - flow.createdAt > QR_FLOW_TTL_MS) {
|
|
3308
|
+
clearInterval(timer);
|
|
3309
|
+
this.qrFlows.delete(flowId);
|
|
3310
|
+
void this.disconnectQuietly(flow.client);
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
if (flow.approved || flow.needsPassword || flow.failure) {
|
|
3314
|
+
clearInterval(timer);
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
this.probeQrApproval(flow).then(
|
|
3318
|
+
(state) => {
|
|
3319
|
+
if (state === "approved") {
|
|
3320
|
+
flow.approved = true;
|
|
3321
|
+
clearInterval(timer);
|
|
3322
|
+
} else if (state === "password") {
|
|
3323
|
+
flow.needsPassword = true;
|
|
3324
|
+
clearInterval(timer);
|
|
3325
|
+
}
|
|
3326
|
+
},
|
|
3327
|
+
(error) => {
|
|
3328
|
+
flow.failure = mapTelegramError(error, "Telegram QR sign-in failed").message;
|
|
3329
|
+
clearInterval(timer);
|
|
3330
|
+
}
|
|
3331
|
+
);
|
|
3332
|
+
}, QR_APPROVAL_POLL_MS);
|
|
3333
|
+
timer.unref();
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Asks Telegram whether the token was approved. Approval is observed by
|
|
3337
|
+
* `ExportLoginToken` returning `LoginTokenSuccess` (or a DC migration).
|
|
3338
|
+
*/
|
|
3339
|
+
async probeQrApproval(flow) {
|
|
3340
|
+
try {
|
|
3341
|
+
const result = await flow.client.invoke(
|
|
3342
|
+
new import_telegram3.Api.auth.ExportLoginToken({
|
|
3343
|
+
apiId: this.credentials.apiId,
|
|
3344
|
+
apiHash: this.credentials.apiHash,
|
|
3345
|
+
exceptIds: []
|
|
3346
|
+
})
|
|
3347
|
+
);
|
|
3348
|
+
if (result instanceof import_telegram3.Api.auth.LoginTokenSuccess) return "approved";
|
|
3349
|
+
if (result instanceof import_telegram3.Api.auth.LoginTokenMigrateTo) {
|
|
3350
|
+
await flow.client._switchDC(result.dcId);
|
|
3351
|
+
const migrated = await flow.client.invoke(
|
|
3352
|
+
new import_telegram3.Api.auth.ImportLoginToken({ token: result.token })
|
|
3353
|
+
);
|
|
3354
|
+
if (migrated instanceof import_telegram3.Api.auth.LoginTokenSuccess) return "approved";
|
|
3355
|
+
}
|
|
3356
|
+
return "pending";
|
|
3357
|
+
} catch (error) {
|
|
3358
|
+
if (isPasswordRequiredError(error)) return "password";
|
|
3359
|
+
throw error;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
async completeQrLogin(flowId, flow) {
|
|
3363
|
+
this.qrFlows.delete(flowId);
|
|
3364
|
+
const user = await this.finalizeConnectedClient(flow.client, null);
|
|
3365
|
+
return { status: "approved", user };
|
|
3366
|
+
}
|
|
3367
|
+
requireQrFlow(flowId) {
|
|
3368
|
+
const flow = this.qrFlows.get(flowId);
|
|
3369
|
+
if (!flow) {
|
|
3370
|
+
throw new NotFoundError("This Telegram QR sign-in has ended. Start again.");
|
|
3371
|
+
}
|
|
3372
|
+
if (Date.now() - flow.createdAt > QR_FLOW_TTL_MS) {
|
|
3373
|
+
this.qrFlows.delete(flowId);
|
|
3374
|
+
void this.disconnectQuietly(flow.client);
|
|
3375
|
+
throw new ValidationError("The QR sign-in expired. Start again.");
|
|
3376
|
+
}
|
|
3377
|
+
return flow;
|
|
3378
|
+
}
|
|
3379
|
+
sweepExpiredQrFlows() {
|
|
3380
|
+
const now = Date.now();
|
|
3381
|
+
for (const [flowId, flow] of this.qrFlows) {
|
|
3382
|
+
if (now - flow.createdAt > QR_FLOW_TTL_MS) {
|
|
3383
|
+
this.qrFlows.delete(flowId);
|
|
3384
|
+
void this.disconnectQuietly(flow.client);
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3223
3388
|
/** Aborts a pending login flow (disconnects the client). */
|
|
3224
3389
|
async cancelLogin(phone) {
|
|
3225
3390
|
const flow = this.flows.get(phone);
|
|
@@ -3281,11 +3446,20 @@ var TelegramConnectionService = class {
|
|
|
3281
3446
|
}
|
|
3282
3447
|
async finalizeLogin(flow) {
|
|
3283
3448
|
this.flows.delete(flow.phone);
|
|
3449
|
+
const user = await this.finalizeConnectedClient(flow.client, flow.phone);
|
|
3450
|
+
return { status: "ok", user };
|
|
3451
|
+
}
|
|
3452
|
+
/**
|
|
3453
|
+
* Saves an authorized client's session, upserts the user and creates storage.
|
|
3454
|
+
* `phone` is the number the user typed; QR sign-in passes null and relies on
|
|
3455
|
+
* the number Telegram reports.
|
|
3456
|
+
*/
|
|
3457
|
+
async finalizeConnectedClient(client, phone) {
|
|
3284
3458
|
let stringSession;
|
|
3285
3459
|
let user;
|
|
3286
3460
|
try {
|
|
3287
|
-
stringSession =
|
|
3288
|
-
const me = await
|
|
3461
|
+
stringSession = client.session.save();
|
|
3462
|
+
const me = await client.getMe();
|
|
3289
3463
|
if (!(me instanceof import_telegram3.Api.User)) {
|
|
3290
3464
|
throw new Error("Telegram did not return the authenticated user");
|
|
3291
3465
|
}
|
|
@@ -3294,16 +3468,16 @@ var TelegramConnectionService = class {
|
|
|
3294
3468
|
username: me.username ?? null,
|
|
3295
3469
|
firstName: me.firstName ?? null,
|
|
3296
3470
|
lastName: me.lastName ?? null,
|
|
3297
|
-
phone: me.phone ??
|
|
3471
|
+
phone: me.phone ?? phone ?? ""
|
|
3298
3472
|
});
|
|
3299
3473
|
} catch (error) {
|
|
3300
|
-
await this.disconnectQuietly(
|
|
3474
|
+
await this.disconnectQuietly(client);
|
|
3301
3475
|
throw mapTelegramError(error, "Could not complete the Telegram connection");
|
|
3302
3476
|
}
|
|
3303
3477
|
await this.repos.sessions.save(user.id, stringSession);
|
|
3304
|
-
await this.ensureStorageForClient(user.id,
|
|
3305
|
-
this.clientManager.cacheClient(user.id,
|
|
3306
|
-
return
|
|
3478
|
+
await this.ensureStorageForClient(user.id, client);
|
|
3479
|
+
this.clientManager.cacheClient(user.id, client);
|
|
3480
|
+
return user;
|
|
3307
3481
|
}
|
|
3308
3482
|
sweepExpiredFlows() {
|
|
3309
3483
|
const now = Date.now();
|