Skip to content

Commit 434fad3

Browse files
committed
feat(auth): restore the Telegram session automatically on startup
After a container restart the WTelegram client was created but the saved session was only loaded when someone opened the web UI (Index.razor -> checkAuth), so API/WebDAV requests failed with not_logged_in until then. - TelegramService.TryRestoreSessionAsync(): non-interactive restore of a previously authorized session (client.UserId != 0), attempted once per process and shared by concurrent callers to avoid triggering new verification codes on a revoked session. - TelegramSessionRestoreService (IHostedService): kicks the restore in the background on startup; OnUserLoggedIn then drives TaskResumeService as with the interactive login. - RequireTelegramSession: when no session is active, awaits the automatic restore before answering 401, so the first API call after a restart succeeds even if it arrives before the startup restore finishes. If there is no previous session nothing changes: interactive login through the web UI (or /api/v1/auth) is still required.
1 parent b41796c commit 434fad3

5 files changed

Lines changed: 120 additions & 0 deletions

File tree

TelegramDownloader/Controllers/Api/V1/RequireTelegramSessionAttribute.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
4040
loggedIn = false;
4141
}
4242

43+
if (!loggedIn)
44+
{
45+
// After a restart the session file may hold a valid session
46+
// that nobody has loaded yet (it is normally loaded when the
47+
// web UI is opened). Try the automatic restore before failing;
48+
// concurrent requests share the same single attempt.
49+
try
50+
{
51+
loggedIn = await telegram.TryRestoreSessionAsync();
52+
}
53+
catch
54+
{
55+
loggedIn = false;
56+
}
57+
}
58+
4359
if (!loggedIn)
4460
{
4561
context.Result = new ObjectResult(ApiResult.Fail(

TelegramDownloader/Data/ITelegramService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public interface ITelegramService
1111
bool IsConfigured { get; }
1212
void InitializeClient();
1313
Task<string> checkAuth(string number, bool isPhone = false);
14+
Task<bool> TryRestoreSessionAsync();
1415
Task<User> GetUser();
1516
bool checkChannelExist(string id);
1617
bool checkUserLogin();

TelegramDownloader/Data/TelegramService.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,52 @@ public async Task sendVerificationCode(string vc)
794794
await DoLogin(vc);
795795
}
796796

797+
// Single automatic restore attempt per process, shared by every caller
798+
// (startup service and concurrent API requests await the same task). A
799+
// failed attempt is NOT retried automatically: retrying checkAuth with
800+
// a saved phone on a revoked session would trigger a new verification
801+
// code on every call - the interactive (web) login is required then.
802+
private static Task<bool> autoRestoreTask = null;
803+
private static readonly object autoRestoreLock = new object();
804+
805+
public Task<bool> TryRestoreSessionAsync()
806+
{
807+
if (client == null)
808+
return Task.FromResult(false);
809+
if (client.User != null)
810+
return Task.FromResult(true);
811+
// UserId == 0 means the session file holds no authorized user:
812+
// there is nothing to restore, interactive login is required.
813+
if (client.UserId == 0)
814+
return Task.FromResult(false);
815+
lock (autoRestoreLock)
816+
{
817+
autoRestoreTask ??= RestoreSessionAsync();
818+
return autoRestoreTask;
819+
}
820+
}
821+
822+
private async Task<bool> RestoreSessionAsync()
823+
{
824+
try
825+
{
826+
_logger.LogInformation("Restoring previous Telegram session automatically");
827+
string result = await checkAuth(null);
828+
if (result == "ok")
829+
{
830+
_logger.LogInformation("Telegram session restored automatically");
831+
return true;
832+
}
833+
_logger.LogWarning("Automatic session restore not completed (result: {Result}) - interactive login required", result);
834+
return false;
835+
}
836+
catch (Exception ex)
837+
{
838+
_logger.LogWarning(ex, "Automatic session restore failed - interactive login required");
839+
return false;
840+
}
841+
}
842+
797843
public bool checkUserLogin()
798844
{
799845

TelegramDownloader/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@
156156
builder.Services.AddSingleton<ITaskPersistenceService, TaskPersistenceService>();
157157
builder.Services.AddHostedService<TaskResumeService>();
158158

159+
// Restore a previously authorized Telegram session on startup so the API and
160+
// WebDAV work after a restart without opening the web UI first. Registered
161+
// after TaskResumeService so its OnUserLoggedIn subscription exists when the
162+
// restore completes.
163+
builder.Services.AddHostedService<TelegramSessionRestoreService>();
164+
159165
// Log query service - only if MongoDB is available
160166
builder.Services.AddSingleton<ILogQueryService>(sp =>
161167
new LogQueryService(mongoConnectionString ?? "mongodb://localhost:27017", sp.GetRequiredService<ILogger<LogQueryService>>()));
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using TelegramDownloader.Data;
2+
3+
namespace TelegramDownloader.Services
4+
{
5+
/// <summary>
6+
/// Restores the previously authorized Telegram session on application
7+
/// startup (e.g. after a container restart), so API clients and WebDAV
8+
/// work without having to open the web UI first. When no previous session
9+
/// exists this does nothing and the interactive (web) login is still
10+
/// required.
11+
/// </summary>
12+
public class TelegramSessionRestoreService : IHostedService
13+
{
14+
private readonly IServiceProvider _serviceProvider;
15+
private readonly ILogger<TelegramSessionRestoreService> _logger;
16+
17+
public TelegramSessionRestoreService(IServiceProvider serviceProvider, ILogger<TelegramSessionRestoreService> logger)
18+
{
19+
_serviceProvider = serviceProvider;
20+
_logger = logger;
21+
}
22+
23+
public Task StartAsync(CancellationToken cancellationToken)
24+
{
25+
// Run in the background so a slow Telegram connection never delays
26+
// the web server startup.
27+
_ = Task.Run(async () =>
28+
{
29+
try
30+
{
31+
var telegram = _serviceProvider.GetRequiredService<ITelegramService>();
32+
if (!telegram.IsConfigured)
33+
{
34+
_logger.LogInformation("Telegram not configured - skipping automatic session restore");
35+
return;
36+
}
37+
bool restored = await telegram.TryRestoreSessionAsync();
38+
if (!restored)
39+
_logger.LogInformation("No previous Telegram session to restore - login through the web UI or /api/v1/auth");
40+
}
41+
catch (Exception ex)
42+
{
43+
_logger.LogWarning(ex, "Automatic Telegram session restore failed on startup");
44+
}
45+
}, cancellationToken);
46+
return Task.CompletedTask;
47+
}
48+
49+
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
50+
}
51+
}

0 commit comments

Comments
 (0)