From f3d4f84fde24bb92e7cf6a22d94a703c753f3cbe Mon Sep 17 00:00:00 2001 From: Rory& Date: Fri, 14 Aug 2026 05:21:15 +0200 Subject: Working files and symlinks? --- Rhea/Controllers/MetaController.cs | 17 +++++++++ Rhea/Controllers/NarController.cs | 20 +++++++++++ Rhea/Controllers/NarInfoController.cs | 32 +++++++++++++++++ Rhea/Program.cs | 67 +++++++++++++++++++++++++++++++++++ Rhea/Properties/launchSettings.json | 14 ++++++++ Rhea/Rhea.csproj | 18 ++++++++++ Rhea/Rhea.http | 6 ++++ Rhea/RheaConfiguration.cs | 11 ++++++ Rhea/StoreMetaService.cs | 22 ++++++++++++ Rhea/appsettings.Development.json | 11 ++++++ Rhea/appsettings.json | 9 +++++ 11 files changed, 227 insertions(+) create mode 100644 Rhea/Controllers/MetaController.cs create mode 100644 Rhea/Controllers/NarController.cs create mode 100644 Rhea/Controllers/NarInfoController.cs create mode 100644 Rhea/Program.cs create mode 100644 Rhea/Properties/launchSettings.json create mode 100644 Rhea/Rhea.csproj create mode 100644 Rhea/Rhea.http create mode 100644 Rhea/RheaConfiguration.cs create mode 100644 Rhea/StoreMetaService.cs create mode 100644 Rhea/appsettings.Development.json create mode 100644 Rhea/appsettings.json (limited to 'Rhea') diff --git a/Rhea/Controllers/MetaController.cs b/Rhea/Controllers/MetaController.cs new file mode 100644 index 0000000..ca20e99 --- /dev/null +++ b/Rhea/Controllers/MetaController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Rhea.Controllers; + +[ApiController] +public class MetaController(RheaConfiguration cfg) : ControllerBase +{ + [HttpGet("/nix-cache-info")] + public async Task GetNixCacheInfo() + { + return $""" + StoreDir: {cfg.StorePath} + WantMassQuery: 1 + Priority: 5 + """; + } +} \ No newline at end of file diff --git a/Rhea/Controllers/NarController.cs b/Rhea/Controllers/NarController.cs new file mode 100644 index 0000000..7ba44ba --- /dev/null +++ b/Rhea/Controllers/NarController.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Rhea.Controllers; + +[ApiController] +public class NarController(RheaConfiguration cfg, StoreMetaService storeSvc) : ControllerBase +{ + [HttpHead("/nar/{hash}.nar.zst")] + public async Task CheckNixNarExists(string hash) + { + var path = await storeSvc.GetStorePathByBaseName(hash); + Response.StatusCode = string.IsNullOrWhiteSpace(path) ? 404 : 200; + } + + [HttpGet("/nar/{hash}.nar.zst")] + public async Task GetNixCacheInfo() + { + return ""; + } +} \ No newline at end of file diff --git a/Rhea/Controllers/NarInfoController.cs b/Rhea/Controllers/NarInfoController.cs new file mode 100644 index 0000000..b81b655 --- /dev/null +++ b/Rhea/Controllers/NarInfoController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Rhea.Controllers; + +[ApiController] +public class NarInfoController(RheaConfiguration cfg, StoreMetaService storeSvc) : ControllerBase +{ + [HttpHead("/{hash}.narinfo")] + public async Task CheckNixNarExists(string hash) + { + var path = await storeSvc.GetStorePathByBaseName(hash); + Response.StatusCode = string.IsNullOrWhiteSpace(path) ? 404 : 200; + } + + [HttpGet("/{hash}.narinfo")] + public async Task GetNixNarInfo(string hash) + { + var path = await storeSvc.GetStorePathByBaseName(hash); + + return $""" + StorePath: {cfg.StorePath}/{path} + URL: nar/09d83cyl9dlfkkbspkgkk7bfydj3mvw6r1x98kvc2v8wl2xd8ldy.nar.zst + Compression: zstd + FileHash: sha256:0yzqbk88qqh0c7dswwcm27y1pxs5a83hga81vjmcs9d331psv1g8 + FileSize: 48255244 + NarHash: sha256:09d83cyl9dlfkkbspkgkk7bfydj3mvw6r1x98kvc2v8wl2xd8ldy + NarSize: 209581048 + Sig: cache.nixos.org-1:sRYhPUkDDRCgZDlgPMvXYEgYvX3GF24wT0v6HOUrtB/4g5lZffgRksiSmsth2wKRPlMijKP/yoDkG4NZDJfJCA== + CA: fixed:r:sha256:09d83cyl9dlfkkbspkgkk7bfydj3mvw6r1x98kvc2v8wl2xd8ldy + """; + } +} \ No newline at end of file diff --git a/Rhea/Program.cs b/Rhea/Program.cs new file mode 100644 index 0000000..a7310b4 --- /dev/null +++ b/Rhea/Program.cs @@ -0,0 +1,67 @@ +using System.Diagnostics; +using ArcaneLibs; +using ArcaneLibs.Extensions; +using ArcaneLibs.Extensions.Streams; +using Rhea; +using Rhea.Nar; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +new NarWriter().GetHeader().ToArray().HexDump(64); + + +foreach (var srcPath in (string[])["/etc/nix/nix.conf", FileUtils.GetBinDir() + "/Rhea.runtimeconfig.json"]) +{ + Console.WriteLine($"Original {srcPath}:"); + + var pe = new ProcessStartInfo("nix", ["nar", "dump-path", srcPath]) + { + RedirectStandardOutput = true + }; + var origBuf = new List(); + var pr = Process.Start(pe); + int b; + while ((b = pr.StandardOutput.BaseStream.ReadByte()) != -1) origBuf.Add((byte)b); + origBuf.ToArray().HexDump(64); + + Console.WriteLine($"\nNew {srcPath}:"); + List tbuf = []; + List buf = []; + foreach (var chunk in new NarWriter().GetNarStreamContents(srcPath)) + { + tbuf.AddRange(chunk); + buf.AddRange(chunk); + while (buf.Count > 64) + { + byte[] data = buf[..64].ToArray(); + buf = buf[64..]; + data.HexDump(64); + } + } + + buf.ToArray().HexDump(64); + + Console.WriteLine("Is equal: " + origBuf.SequenceEqual(tbuf)); +} + +// app.Run(); \ No newline at end of file diff --git a/Rhea/Properties/launchSettings.json b/Rhea/Properties/launchSettings.json new file mode 100644 index 0000000..20f400a --- /dev/null +++ b/Rhea/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5087", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Rhea/Rhea.csproj b/Rhea/Rhea.csproj new file mode 100644 index 0000000..24387ad --- /dev/null +++ b/Rhea/Rhea.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + diff --git a/Rhea/Rhea.http b/Rhea/Rhea.http new file mode 100644 index 0000000..5b6e20f --- /dev/null +++ b/Rhea/Rhea.http @@ -0,0 +1,6 @@ +@NixCached_HostAddress = http://localhost:5087 + +GET {{NixCached_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Rhea/RheaConfiguration.cs b/Rhea/RheaConfiguration.cs new file mode 100644 index 0000000..8b18fc7 --- /dev/null +++ b/Rhea/RheaConfiguration.cs @@ -0,0 +1,11 @@ +namespace Rhea; + +public class RheaConfiguration +{ + public RheaConfiguration(IConfiguration config) + { + config.GetRequiredSection("Rhea").Bind(this); + } + + public string StorePath { get; set; } +} \ No newline at end of file diff --git a/Rhea/StoreMetaService.cs b/Rhea/StoreMetaService.cs new file mode 100644 index 0000000..6b92cd6 --- /dev/null +++ b/Rhea/StoreMetaService.cs @@ -0,0 +1,22 @@ +using ArcaneLibs.Collections; + +namespace Rhea; + +public class StoreMetaService(RheaConfiguration cfg) +{ + private LruCache _storePathsByBaseName = new(10000); + + public async Task GetStorePathByBaseName(string baseName) + { + return await _storePathsByBaseName.GetOrAddAsync(baseName, async () => + { + foreach (var path in Directory.EnumerateFileSystemEntries(cfg.StorePath)) + { + Console.WriteLine(path[(cfg.StorePath.Length + 1)..]); + if (path.StartsWith(baseName + '-')) return path; + } + + return ""; + }); + } +} \ No newline at end of file diff --git a/Rhea/appsettings.Development.json b/Rhea/appsettings.Development.json new file mode 100644 index 0000000..7ccad18 --- /dev/null +++ b/Rhea/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", +// "Microsoft.AspNetCore": "Warning" + } + }, + "Rhea": { + "StorePath": "/nix/store" + } +} diff --git a/Rhea/appsettings.json b/Rhea/appsettings.json new file mode 100644 index 0000000..c236d5a --- /dev/null +++ b/Rhea/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", +// "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} -- cgit 1.5.1