using Microsoft.Data.Sqlite; using MoShiro.Backend.Application; namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteNodeMetaStore : INodeMetaStore { private readonly SqliteDatabase _db; public SqliteNodeMetaStore(SqliteDatabase db) { _db = db; } public ValueTask> ListAsync(CancellationToken ct) => _db.ReadAsync>( c => c.Query("SELECT node_id, os, os_name, arch, country, isp_key, isp_name, geo_host, geo_at, updated_at, version, tag, commit_id, built_at, isp_manual FROM node_meta", Read), ct); public ValueTask SaveAsync(NodeMeta meta, CancellationToken ct) => _db.ExecuteAsync( c => c.Exec( "INSERT INTO node_meta (node_id, os, os_name, arch, country, isp_key, isp_name, geo_host, geo_at, updated_at, version, tag, commit_id, built_at, isp_manual) " + "VALUES (@id, @os, @osn, @arch, @cc, @ik, @in, @gh, @ga, @ua, @ver, @tag, @cm, @bt, @im) ON CONFLICT(node_id) DO UPDATE SET " + "os = excluded.os, os_name = excluded.os_name, arch = excluded.arch, country = excluded.country, " + "isp_key = excluded.isp_key, isp_name = excluded.isp_name, geo_host = excluded.geo_host, geo_at = excluded.geo_at, updated_at = excluded.updated_at, " + "version = excluded.version, tag = excluded.tag, commit_id = excluded.commit_id, built_at = excluded.built_at, isp_manual = excluded.isp_manual", ("@id", meta.Info.NodeId), ("@os", meta.Info.Os), ("@osn", meta.Info.OsName), ("@arch", meta.Info.Arch), ("@cc", meta.Info.Country), ("@ik", meta.Info.IspKey), ("@in", meta.Info.IspName), ("@gh", meta.GeoHost), ("@ga", meta.GeoAtMs), ("@ua", meta.UpdatedAtMs), ("@ver", meta.Info.Version), ("@tag", meta.Info.Tag), ("@cm", meta.Info.Commit), ("@bt", meta.Info.BuiltAt), ("@im", meta.Info.IspManual ? 1 : 0)), ct); public ValueTask DeleteAsync(string nodeId, CancellationToken ct) => _db.ExecuteAsync(c => c.Exec("DELETE FROM node_meta WHERE node_id = @id", ("@id", nodeId)), ct); private static NodeMeta Read(SqliteDataReader r) { static string? S(SqliteDataReader reader, int i) => reader.IsDBNull(i) ? null : reader.GetString(i); return new NodeMeta(new NodeInfo(r.GetString(0), S(r, 1), S(r, 2), S(r, 3), S(r, 4), S(r, 5), S(r, 6), S(r, 10), S(r, 11), S(r, 12), S(r, 13), r.GetInt64(14) != 0), S(r, 7), r.GetInt64(8), r.GetInt64(9)); } }