feat(links): fetch website and docs URLs from GitHub at startup

- Add links.json to repo (alongside update.xml) with Website and Documentation URLs.
- Add LinksManager to support link fetching on startup via raw.githubusercontent.com
- Suppress TimeAligner failure instead of blocking app launch
This commit is contained in:
achiez
2026-06-19 22:34:37 +03:00
parent 07eb9f15a9
commit ed5d3c2eda
7 changed files with 75 additions and 11 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"Website": "https://achiefy.pro/?nebula=true",
"Documentation": "https://achiefy-project.gitbook.io/nebulaauth"
}
+33
View File
@@ -0,0 +1,33 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using NebulaAuth.Model;
using Newtonsoft.Json;
namespace NebulaAuth.Core;
public static class LinksManager
{
private const string LINKS_URL =
"https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/links.json";
private static readonly HttpClient HttpClient = new();
public static string? WebsiteUrl { get; private set; }
public static string? DocumentationUrl { get; private set; }
public static async Task FetchAsync()
{
try
{
var json = await HttpClient.GetStringAsync(LINKS_URL).ConfigureAwait(false);
var links = JsonConvert.DeserializeObject<RemoteLinks>(json);
WebsiteUrl = links?.Website;
DocumentationUrl = links?.Documentation;
}
catch (Exception ex)
{
Shell.Logger.Debug(ex, "Failed to fetch remote links");
}
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace NebulaAuth.Model;
public class RemoteLinks
{
public string? Website { get; set; }
public string? Documentation { get; set; }
}
+3 -2
View File
@@ -3,7 +3,6 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.Exceptions;
using NebulaAuth.Model.MAAC; using NebulaAuth.Model.MAAC;
using NebulaAuth.Model.MafileExport; using NebulaAuth.Model.MafileExport;
using NebulaAuth.Model.Mafiles; using NebulaAuth.Model.Mafiles;
@@ -40,7 +39,8 @@ public static class Shell
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new CantAlignTimeException("", ex); Logger.Error(ex, "Failed to align time with Steam");
TimeAligner.SetTimeDifference(0);
} }
var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1; var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1;
@@ -48,6 +48,7 @@ public static class Shell
ProxyAssignmentCache.Initialize(Storage.MaFiles); ProxyAssignmentCache.Initialize(Storage.MaFiles);
MAACStorage.Initialize(); MAACStorage.Initialize();
MafileExporterStorage.Initialize(); MafileExporterStorage.Initialize();
_ = LinksManager.FetchAsync();
ExtensionsLogger.LogDebug("Application started"); ExtensionsLogger.LogDebug("Application started");
} }
+11 -6
View File
@@ -8,7 +8,8 @@
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
Background="Transparent" Background="Transparent"
RenderOptions.BitmapScalingMode="HighQuality"> RenderOptions.BitmapScalingMode="HighQuality"
Loaded="LinksView_Loaded">
<materialDesign:Card Padding="24" HorizontalAlignment="Center" VerticalAlignment="Center" UniformCornerRadius="12"> <materialDesign:Card Padding="24" HorizontalAlignment="Center" VerticalAlignment="Center" UniformCornerRadius="12">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
@@ -49,16 +50,20 @@
</StackPanel> </StackPanel>
</Button> </Button>
<Button Margin="0,0,0,5" FontSize="18" Style="{StaticResource MaterialDesignFlatButton}" <Button x:Name="WebsiteButton" Margin="0,0,0,5" FontSize="18"
Click="Website_Click" IsEnabled="False"> Style="{StaticResource MaterialDesignFlatButton}"
IsEnabled="False"
Click="Website_Click">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<materialDesign:PackIcon Kind="Web" Width="24" Height="24" Margin="0 0 8 0" /> <materialDesign:PackIcon Kind="Web" Width="24" Height="24" Margin="0 0 8 0" />
<TextBlock Text="Web-site" VerticalAlignment="Center" /> <TextBlock Text="Achiefy" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Margin="0,0,0,5" FontSize="18" Style="{StaticResource MaterialDesignFlatButton}" <Button x:Name="DocumentationButton" Margin="0,0,0,5" FontSize="18"
Click="Documentation_Click" IsEnabled="False"> Style="{StaticResource MaterialDesignFlatButton}"
IsEnabled="False"
Click="Documentation_Click">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Viewbox Width="22" Height="22" Margin="0 0 8 0"> <Viewbox Width="22" Height="22" Margin="0 0 8 0">
<Canvas Width="22" Height="22"> <Canvas Width="22" Height="22">
+11 -3
View File
@@ -1,4 +1,4 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
@@ -13,6 +13,12 @@ public partial class LinksView : UserControl
InitializeComponent(); InitializeComponent();
} }
private void LinksView_Loaded(object sender, RoutedEventArgs e)
{
WebsiteButton.IsEnabled = LinksManager.WebsiteUrl != null;
DocumentationButton.IsEnabled = LinksManager.DocumentationUrl != null;
}
private void Telegram_Click(object sender, RoutedEventArgs e) private void Telegram_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://t.me/nebulaauth") {UseShellExecute = true}); Process.Start(new ProcessStartInfo("https://t.me/nebulaauth") {UseShellExecute = true});
@@ -26,12 +32,14 @@ public partial class LinksView : UserControl
private void Website_Click(object sender, RoutedEventArgs e) private void Website_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://yourwebsite.com") {UseShellExecute = true}); if (LinksManager.WebsiteUrl != null)
Process.Start(new ProcessStartInfo(LinksManager.WebsiteUrl) {UseShellExecute = true});
} }
private void Documentation_Click(object sender, RoutedEventArgs e) private void Documentation_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://yourwebsite.com") {UseShellExecute = true}); if (LinksManager.DocumentationUrl != null)
Process.Start(new ProcessStartInfo(LinksManager.DocumentationUrl) {UseShellExecute = true});
} }
private void CheckForUpdates_Click(object sender, RoutedEventArgs e) private void CheckForUpdates_Click(object sender, RoutedEventArgs e)
@@ -80,4 +80,10 @@ public static class TimeAligner
client.Dispose(); client.Dispose();
} }
} }
public static void SetTimeDifference(int timeDifference)
{
_timeDifference = timeDifference;
_aligned = true;
}
} }