Skip to content
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
2fece03
initial prototype:add password reset dialog
ranjinikrishnan Mar 4, 2026
a9c6ed1
add manual workflow trigger
ranjinikrishnan Mar 5, 2026
9b0ab95
trigger built for prototype
ranjinikrishnan Mar 5, 2026
dab676a
trigger build
ranjinikrishnan Mar 5, 2026
c9dc7b0
add workflow dispatch trigger
ranjinikrishnan Mar 5, 2026
f678c45
feat: wire up password reset DB call via MySqlAdmin CLI
ranjinikrishnan Mar 10, 2026
0a2a3cb
feat: password reset backend logic
ranjinikrishnan Mar 10, 2026
bd16bc5
working button
Mar 11, 2026
3c7a6ef
Added automated database backup on zip file selection
Mar 11, 2026
0244d3e
ci: workflow trigger for feature branch passwordreset
ranjinikrishnan Mar 11, 2026
08ab970
feat: tools window with supervisor verification for password reset
Mar 16, 2026
65e8726
Update .github/workflows/ci.yml
ranjinikrishnan Mar 16, 2026
5d6238f
Update Server/MysqlAdmin.cs
ranjinikrishnan Mar 16, 2026
752b7b6
Added UI elements for the update process
Mar 16, 2026
351946f
Update document root to point to release
hakiatalov Mar 16, 2026
2fa9b2e
Merge branch 'feature/s2026-passwordreset' into feature/s2026-passwor…
ranjinikrishnan Mar 17, 2026
0cc3050
Merge pull request #38 from C4G/feature/s2026-passwordreset-tools-window
ranjinikrishnan Mar 17, 2026
1fbf7a3
Merge pull request #40 from luqmanrashad/main
luqmanrashad Mar 17, 2026
09ead14
Revert package-lock.json and ci.yml to main version
ranjinikrishnan Mar 17, 2026
268eb8c
merge: combined password reset functionality with update zip file ui
Mar 18, 2026
aaded27
feat: Combining features into a single dropdown menu option
Mar 18, 2026
f3660e5
Merge remote-tracking branch 'origin/feature/s2026-passwordreset' int…
ranjinikrishnan Mar 22, 2026
531caa1
adding hierarchy supervisor verification
ranjinikrishnan Mar 22, 2026
cb5b1ac
Implement Stage 2 self-update
luqmanrashad Mar 22, 2026
310b1bc
Inject local directory path
hakiatalov Mar 23, 2026
971ed31
Actually setting local directory path as environment vairable
hakiatalov Mar 23, 2026
7213856
Adopt LOCAL_DIR and releases/dev/htdocs directory structure
luqmanrashad Mar 25, 2026
0e77811
Extract version, backup/copy other files and update state.json
luqmanrashad Mar 25, 2026
289b89a
fixing staging setup
luqmanrashad Mar 25, 2026
4e31801
Stop servers before file ops, break update into 9 granular stages
luqmanrashad Mar 25, 2026
5b7a256
removed updates panel in the reset dialog
ranjinikrishnan Mar 26, 2026
db77dc6
Merge remote-tracking branch 'origin/feature/combined-updates' into s…
hakiatalov Mar 26, 2026
e0c6274
Add environment variables for storage and data directories in Apache …
hakiatalov Mar 27, 2026
1fa5063
Refactor: extract Mysql.cs from MySqlAdmin, register new classes in D…
ranjinikrishnan Mar 28, 2026
a9ab7d1
Removed old code-commented
ranjinikrishnan Mar 31, 2026
bdb6ec7
restore ci.yml to main
ranjinikrishnan Mar 31, 2026
2645b23
Comments
ranjinikrishnan Mar 31, 2026
11d5fc3
merge s2026-gha-peer-eval and feature/combined-update-tests into feat…
luqmanrashad Apr 2, 2026
1b5f0ef
Merge remote-tracking branch 'origin/feature/v2-password-reset' into …
hakiatalov Apr 3, 2026
24c0aab
Add update validation: same-version check, ZIP compatibility check, p…
Apr 7, 2026
8daf104
Split unpack/validate into separate stages, remove redundant Progress…
Apr 7, 2026
2b1cf82
Merge pull request #56 from C4G/main
asuquoe62-star Apr 9, 2026
4bdec0d
feat: password validation and strength meter
Apr 11, 2026
4281e28
Merge pull request #58 from C4G/feature/s2026-passwordreset-tools-window
simotony77 Apr 12, 2026
babd145
Merge branch 'backup-working-version' into feature/s2026-passwordreset
ranjinikrishnan Apr 15, 2026
fa4c4ba
merging new input validation with full scope and close dialog option
ranjinikrishnan Apr 15, 2026
6df04a3
restore packages.lock.json to unstable
ranjinikrishnan Apr 16, 2026
51f4178
Merge branch 'unstable' into feature/s2026-passwordreset
ranjinikrishnan Apr 16, 2026
aa0402c
Merge pull request #59 from C4G/feature/s2026-passwordreset
ranjinikrishnan Apr 16, 2026
7791b93
Merge pull request #61 from C4G/main
asuquoe62-star Apr 17, 2026
390eb7e
change made for french Lang
Apr 17, 2026
8d4daad
Merge branch 'unstable' into s2026-new-lang
asuquoe62-star Apr 18, 2026
8c69975
Merge pull request #62 from C4G/s2026-new-lang
asuquoe62-star Apr 18, 2026
ff783a1
Merge pull request #63 from C4G/unstable
asuquoe62-star Apr 18, 2026
05512ab
update the password english
Apr 18, 2026
d58a641
Merge pull request #64 from C4G/s2026-new-lang
asuquoe62-star Apr 18, 2026
1892f08
Merge branch 'unstable' into feature/s2026-self-update
luqmanrashad Apr 18, 2026
711ccdf
Merge pull request #66 from C4G/feature/s2026-self-update
luqmanrashad Apr 18, 2026
3027ff3
Conditionally enable self update feature, modify main window layout t…
mrysav Apr 26, 2026
6737643
Path.Combine -> Path.Join
mrysav Apr 26, 2026
ccbaa11
Reenable autostart
mrysav Apr 26, 2026
beb4f55
Remove unnecessary usings
mrysav Apr 26, 2026
b8c8908
Conditionally disable self update; enable docroot escape hatch
mrysav Apr 27, 2026
ac1912a
Merge branch 'main' into unstable
mrysav May 5, 2026
2e2d985
Update Views/ToolsWindow.axaml
mrysav May 5, 2026
553f6bb
Make UnpackZip async
mrysav May 5, 2026
cc3c16a
remove single-line single-use function
mrysav May 5, 2026
eca832c
Merge branch 'main' into unstable
mrysav May 5, 2026
bcc8ddc
Add cacert.pem location to php.ini template
mrysav Jun 1, 2026
08d9193
Add configuration dependencies
mrysav Jun 3, 2026
cef23c4
Add AppSettings.json configuration for reading/writing config
mrysav Jun 3, 2026
6460bd8
Use dependency injection
mrysav Jun 4, 2026
92d2a81
Use primary constructor
mrysav Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
with:
dotnet-version: "10.x"
cache: true
- run: dotnet publish -r win-x64 -c Release
- run: dotnet publish -r win-x64 -c Release /WarnAsError
- uses: actions/upload-artifact@v7
with:
name: blis-ng-exe
Expand Down
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"EditorConfig.EditorConfig",
"GitHub.vscode-github-actions",
"ms-dotnettools.csdevkit",
"redhat.vscode-xml",
]
}
19 changes: 16 additions & 3 deletions App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
using Avalonia.Markup.Xaml;
using BLIS_NG.Config;
using BLIS_NG.Lib;
using BLIS_NG.Lang;
using BLIS_NG.ViewModels;
using BLIS_NG.Views;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Globalization;

namespace BLIS_NG;

Expand All @@ -19,26 +21,37 @@ public override void Initialize()

public override void OnFrameworkInitializationCompleted()
{
var selectedLanguageCode = LanguagePreferences.GetLanguageCode();
var appCulture = new CultureInfo(selectedLanguageCode);
CultureInfo.DefaultThreadCurrentCulture = appCulture;
CultureInfo.DefaultThreadCurrentUICulture = appCulture;
CultureInfo.CurrentCulture = appCulture;
CultureInfo.CurrentUICulture = appCulture;
BLIS_NG.Lang.Resources.Culture = appCulture;

Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Debug()
.WriteTo.File(Path.Combine(ConfigurationFile.ResolveBaseDirectory(), "log", "blis_ng_.log"), rollingInterval: RollingInterval.Day)
.WriteTo.File(Path.Join(ConfigurationFile.ResolveBaseDirectory(), "log", "blis_ng_.log"), rollingInterval: RollingInterval.Day)
.CreateLogger();

// Clean up leftover artifacts from a previous self-update (old exe + staging dir)
UpdateProgressViewModel.StartupCleanup();

var collection = new ServiceCollection()
.AddLogging(builder => builder.AddSerilog(dispose: true))
// See Lib/ServiceCollectionExtensions.cs to see the dependency injection entrypoint.
.AddDependencies();

if (ApplicationLifetime != null)
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
collection.AddSingleton(ApplicationLifetime);
collection.AddSingleton(desktopLifetime);
}

var services = collection.BuildServiceProvider();

var vm = services.GetRequiredService<MainWindowViewModel>();
var serverControl = services.GetRequiredService<ServerControlViewModel>();

if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
Expand Down
5 changes: 5 additions & 0 deletions Assets/Templates/httpd.conf.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ ServerAdmin mitchell.rysavy@gmail.com
#
ServerName {{ apache_ip }}

SetEnv LOCAL_DIR "{{ local_dir }}"
SetEnv STORAGE_DIR "{{ storage_dir }}"
SetEnv DATA_DIR "{{ data_dir }}"
SetEnv LOG_DIR "{{ log_dir }}"

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
Expand Down
14 changes: 11 additions & 3 deletions BLIS-NG.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
<TrimMode>copyused</TrimMode>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)'=='Release'">
<!--
Self-update is enabled _only_ in debug builds right now;
Define the constant in Release configurations as well to enable everywhere.
-->
<DefineConstants>SelfUpdateEnabled</DefineConstants>
</PropertyGroup>

<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
Expand Down Expand Up @@ -53,8 +61,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="12.0.0" />
<PackageReference Include="Avalonia.Desktop" Version="12.0.0" />
<PackageReference Include="Avalonia" Version="12.0.1" />
<PackageReference Include="Avalonia.Desktop" Version="12.0.1" />
<!--
Condition
below is needed to remove Avalonia.Diagnostics package from build output in Release
Expand All @@ -63,7 +71,7 @@
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics"
Version="11.3.13" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.3.9" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.1" />
<PackageReference Include="Fluid.Core" Version="2.31.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.5" />
Expand Down
6 changes: 3 additions & 3 deletions Config/ConfigurationFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ public ConfigurationFile(Uri templatePath)
this.templatePath = templatePath;

BASE_DIR = ResolveBaseDirectory();
SERVER_BASE_DIR = Path.Combine(BASE_DIR, "server");
SERVER_BASE_DIR = Path.Join(BASE_DIR, "server");

TMP_DIR = Path.Combine(SERVER_BASE_DIR, "tmp");
TMP_DIR = Path.Join(SERVER_BASE_DIR, "tmp");
if (!Path.Exists(TMP_DIR))
{
Directory.CreateDirectory(TMP_DIR);
}

LOG_DIR = Path.Combine(BASE_DIR, "log");
LOG_DIR = Path.Join(BASE_DIR, "log");
if (!Path.Exists(LOG_DIR))
{
Directory.CreateDirectory(LOG_DIR);
Expand Down
22 changes: 17 additions & 5 deletions Config/HttpdConf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ public class HttpdConf : ConfigurationFile
public readonly string CONFIG_FILE_PATH;
public readonly string DOCROOT;
public readonly string PID_FILE;
public readonly string LOCAL_DIR;
public readonly string STORAGE_DIR;
public readonly string DATA_DIR;

public HttpdConf() : base(new Uri("avares://BLIS-NG/Assets/Templates/httpd.conf.liquid"))
{
APACHE2_BASE = Path.Combine(SERVER_BASE_DIR, "Apache");
CONFIG_FILE_PATH = Path.Combine(APACHE2_BASE, "conf", "httpd.conf");
DOCROOT = Path.Combine(BASE_DIR, "htdocs");
PID_FILE = Path.Combine(TMP_DIR, "httpd.pid");
APACHE2_BASE = Path.Join(SERVER_BASE_DIR, "Apache");
CONFIG_FILE_PATH = Path.Join(APACHE2_BASE, "conf", "httpd.conf");

var state = StateFile.Load(BASE_DIR);
DOCROOT = Path.Join(BASE_DIR, "releases", state.ActiveVersion, "htdocs");

PID_FILE = Path.Join(TMP_DIR, "httpd.pid");
LOCAL_DIR = Path.Join(BASE_DIR, "local");
STORAGE_DIR = Path.Join(BASE_DIR, "storage");
DATA_DIR = Path.Join(BASE_DIR, "data");
}

public override void Write()
Expand All @@ -28,7 +37,10 @@ public override void Write()
{ "apache_base", APACHE2_BASE },
{ "log_dir", LOG_DIR },
{ "server_dir", SERVER_BASE_DIR },
{ "pid_file", PID_FILE }
{ "pid_file", PID_FILE },
{ "local_dir", LOCAL_DIR },
{ "storage_dir", STORAGE_DIR },
{ "data_dir", DATA_DIR }
});
}
}
52 changes: 52 additions & 0 deletions Config/LanguagePreferences.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace BLIS_NG.Config;

public static class LanguagePreferences
{
private const string DefaultLanguageCode = "en";
private const string SettingsFileName = "launcher-language.txt";

public static string GetLanguageCode()
Comment thread
mrysav marked this conversation as resolved.
Outdated
{
try
{
var settingsPath = GetSettingsPath();
if (!File.Exists(settingsPath))
{
return DefaultLanguageCode;
}

var code = File.ReadAllText(settingsPath).Trim().ToLowerInvariant();
if (code == "en" || code == "fr")
{
return code;
}
}
catch
{
// Fall back to English if settings can't be read.
}
Comment thread
mrysav marked this conversation as resolved.
Dismissed

return DefaultLanguageCode;
}

public static void SaveLanguageCode(string languageCode)
{
try
{
var normalized = languageCode.Trim().ToLowerInvariant();
if (normalized != "en" && normalized != "fr")
{
normalized = DefaultLanguageCode;
}

File.WriteAllText(GetSettingsPath(), normalized);
}
catch
{
// Ignore write failures; app can continue with default language.
}
Comment thread
mrysav marked this conversation as resolved.
Dismissed
}

private static string GetSettingsPath() =>
Path.Join(ConfigurationFile.ResolveBaseDirectory(), SettingsFileName);
}
4 changes: 2 additions & 2 deletions Config/MySqlIni.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class MySqlIni : ConfigurationFile

public MySqlIni() : base(new Uri("avares://BLIS-NG/Assets/Templates/my.ini.liquid"))
{
CONFIG_FILE_PATH = Path.Combine(SERVER_BASE_DIR, "mysql", "my.ini");
MYSQL_DBDIR = Path.Combine(BASE_DIR, "dbdir");
CONFIG_FILE_PATH = Path.Join(SERVER_BASE_DIR, "mysql", "my.ini");
MYSQL_DBDIR = Path.Join(BASE_DIR, "dbdir");
}

public override void Write()
Expand Down
6 changes: 3 additions & 3 deletions Config/PhpIni.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ public class PhpIni : ConfigurationFile

public PhpIni() : base(new Uri("avares://BLIS-NG/Assets/Templates/php.ini.liquid"))
{
PHP_BASE = Path.Combine(SERVER_BASE_DIR, "php");
PHP_SESSION_PATH = Path.Combine(SERVER_BASE_DIR, "session");
PHP_BASE = Path.Join(SERVER_BASE_DIR, "php");
PHP_SESSION_PATH = Path.Join(SERVER_BASE_DIR, "session");
if (!Path.Exists(PHP_SESSION_PATH))
{
Directory.CreateDirectory(PHP_SESSION_PATH);
}
CONFIG_FILE_PATH = Path.Combine(PHP_BASE, "php.ini");
CONFIG_FILE_PATH = Path.Join(PHP_BASE, "php.ini");
}

public override void Write()
Expand Down
79 changes: 79 additions & 0 deletions Config/StateFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace BLIS_NG.Config;

public class StateFile
{
private const string FileName = "state.json";
private const string DefaultVersion = "dev";

[JsonPropertyName("active_version")]
public string ActiveVersion { get; set; } = DefaultVersion;

[JsonPropertyName("previous_version")]
public string? PreviousVersion { get; set; }

/// <summary>
/// Reads state.json from the base directory. Returns defaults if the file is missing or invalid.
/// </summary>
public static StateFile Load(string baseDir)
{
var path = Path.Join(baseDir, FileName);
if (!File.Exists(path))
{
return new StateFile();
}

try
{
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<StateFile>(json) ?? new StateFile();
}
catch
{
return new StateFile();
}
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
Comment thread
mrysav marked this conversation as resolved.
Dismissed
}

/// <summary>
/// Writes state.json to the base directory.
/// </summary>
public void Save(string baseDir)
{
var path = Path.Join(baseDir, FileName);
var options = new JsonSerializerOptions { WriteIndented = true };
var json = JsonSerializer.Serialize(this, options);
File.WriteAllText(path, json);
}
}

public class VersionFile
{
private const string FileName = "version.json";

[JsonPropertyName("version")]
public string Version { get; set; } = string.Empty;

/// <summary>
/// Reads version.json from the given directory. Returns null if the file is missing or invalid.
/// </summary>
public static VersionFile? Load(string directory)
{
var path = Path.Join(directory, FileName);
if (!File.Exists(path))
{
return null;
}

try
{
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<VersionFile>(json);
}
catch
{
return null;
}
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
Comment thread
mrysav marked this conversation as resolved.
Dismissed
}
}
Loading
Loading