using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Logging; namespace File_Folder_Helper.ADO2025.PI5; internal static partial class Helper20250404 { internal record KafkaProducerSaslOptions( [property: JsonPropertyName("mechanism")] string Mechanism ); internal record MonitorList( [property: JsonPropertyName("id")] int Id, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("description")] string Description, [property: JsonPropertyName("pathName")] string PathName, [property: JsonPropertyName("parent")] int? Parent, [property: JsonPropertyName("childrenIDs")] IReadOnlyList ChildrenIDs, [property: JsonPropertyName("url")] string Url, [property: JsonPropertyName("method")] string Method, [property: JsonPropertyName("hostname")] object Hostname, [property: JsonPropertyName("port")] object Port, [property: JsonPropertyName("maxretries")] int MaxRetries, [property: JsonPropertyName("weight")] int Weight, [property: JsonPropertyName("active")] bool Active, [property: JsonPropertyName("forceInactive")] bool ForceInactive, [property: JsonPropertyName("type")] string Type, [property: JsonPropertyName("timeout")] int Timeout, [property: JsonPropertyName("interval")] int Interval, [property: JsonPropertyName("retryInterval")] int RetryInterval, [property: JsonPropertyName("resendInterval")] int ResendInterval, [property: JsonPropertyName("keyword")] object Keyword, [property: JsonPropertyName("invertKeyword")] bool InvertKeyword, [property: JsonPropertyName("expiryNotification")] bool ExpiryNotification, [property: JsonPropertyName("ignoreTls")] bool IgnoreTls, [property: JsonPropertyName("upsideDown")] bool UpsideDown, [property: JsonPropertyName("packetSize")] int PacketSize, [property: JsonPropertyName("maxredirects")] int MaxRedirects, [property: JsonPropertyName("accepted_statuscodes")] IReadOnlyList AcceptedStatusCodes, [property: JsonPropertyName("dns_resolve_type")] string DnsResolveType, [property: JsonPropertyName("dns_resolve_server")] string DnsResolveServer, [property: JsonPropertyName("dns_last_result")] object DnsLastResult, [property: JsonPropertyName("docker_container")] string DockerContainer, [property: JsonPropertyName("docker_host")] object DockerHost, [property: JsonPropertyName("proxyId")] object ProxyId, [property: JsonPropertyName("notificationIDList")] NotificationIDList NotificationIDList, [property: JsonPropertyName("tags")] IReadOnlyList Tags, [property: JsonPropertyName("maintenance")] bool Maintenance, [property: JsonPropertyName("mqttTopic")] string MqttTopic, [property: JsonPropertyName("mqttSuccessMessage")] string MqttSuccessMessage, [property: JsonPropertyName("databaseQuery")] object DatabaseQuery, [property: JsonPropertyName("authMethod")] string AuthMethod, [property: JsonPropertyName("grpcUrl")] object GrpcUrl, [property: JsonPropertyName("grpcProtobuf")] object GrpcProtobuf, [property: JsonPropertyName("grpcMethod")] object GrpcMethod, [property: JsonPropertyName("grpcServiceName")] object GrpcServiceName, [property: JsonPropertyName("grpcEnableTls")] bool GrpcEnableTls, [property: JsonPropertyName("radiusCalledStationId")] object RadiusCalledStationId, [property: JsonPropertyName("radiusCallingStationId")] object RadiusCallingStationId, [property: JsonPropertyName("game")] object Game, [property: JsonPropertyName("gamedigGivenPortOnly")] bool GameDigGivenPortOnly, [property: JsonPropertyName("httpBodyEncoding")] string HttpBodyEncoding, [property: JsonPropertyName("jsonPath")] object JsonPath, [property: JsonPropertyName("expectedValue")] object ExpectedValue, [property: JsonPropertyName("kafkaProducerTopic")] object KafkaProducerTopic, [property: JsonPropertyName("kafkaProducerBrokers")] IReadOnlyList KafkaProducerBrokers, [property: JsonPropertyName("kafkaProducerSsl")] bool KafkaProducerSsl, [property: JsonPropertyName("kafkaProducerAllowAutoTopicCreation")] bool KafkaProducerAllowAutoTopicCreation, [property: JsonPropertyName("kafkaProducerMessage")] object KafkaProducerMessage, [property: JsonPropertyName("screenshot")] object Screenshot, [property: JsonPropertyName("headers")] object Headers, [property: JsonPropertyName("body")] object Body, [property: JsonPropertyName("grpcBody")] object GrpcBody, [property: JsonPropertyName("grpcMetadata")] object GrpcMetadata, [property: JsonPropertyName("basic_auth_user")] string BasicAuthUser, [property: JsonPropertyName("basic_auth_pass")] string BasicAuthPass, [property: JsonPropertyName("oauth_client_id")] object OauthClientId, [property: JsonPropertyName("oauth_client_secret")] object OauthClientSecret, [property: JsonPropertyName("oauth_token_url")] object OauthTokenUrl, [property: JsonPropertyName("oauth_scopes")] object OauthScopes, [property: JsonPropertyName("oauth_auth_method")] string OauthAuthMethod, [property: JsonPropertyName("pushToken")] string PushToken, [property: JsonPropertyName("databaseConnectionString")] string DatabaseConnectionString, [property: JsonPropertyName("radiusUsername")] object RadiusUsername, [property: JsonPropertyName("radiusPassword")] object RadiusPassword, [property: JsonPropertyName("radiusSecret")] object RadiusSecret, [property: JsonPropertyName("mqttUsername")] string MqttUsername, [property: JsonPropertyName("mqttPassword")] string MqttPassword, [property: JsonPropertyName("authWorkstation")] object AuthWorkstation, [property: JsonPropertyName("authDomain")] object AuthDomain, [property: JsonPropertyName("tlsCa")] object TlsCa, [property: JsonPropertyName("tlsCert")] object TlsCert, [property: JsonPropertyName("tlsKey")] object TlsKey, [property: JsonPropertyName("kafkaProducerSaslOptions")] KafkaProducerSaslOptions KafkaProducerSaslOptions, [property: JsonPropertyName("includeSensitiveData")] bool IncludeSensitiveData ); internal record NotificationIDList( [property: JsonPropertyName("4")] bool _4 ); internal record NotificationList( [property: JsonPropertyName("id")] int Id, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("active")] bool Active, [property: JsonPropertyName("userId")] int UserId, [property: JsonPropertyName("isDefault")] bool IsDefault, [property: JsonPropertyName("config")] string Config ); internal record Kuma( [property: JsonPropertyName("version")] string Version, [property: JsonPropertyName("notificationList")] IReadOnlyList NotificationList, [property: JsonPropertyName("monitorList")] IReadOnlyList MonitorList ); [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Kuma))] private partial class KumaCommonSourceGenerationContext : JsonSerializerContext { } internal static void KumaToGatus(ILogger logger, List args) { string url = args[4]; string fileName = args[3]; string searchPattern = args[2]; ParseMetrics(logger, fileName, url); string sourceDirectory = Path.GetFullPath(args[0]); string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); if (files.Length == 0) logger.LogWarning("<{files}>(s)", files.Length); else KumaToGatus(files); } private static void ParseMetrics(ILogger logger, string fileName, string url) { FileStream fileStream = new(fileName, FileMode.Truncate); HttpClient httpClient = new(); Task streamTask = httpClient.GetStreamAsync(url); streamTask.Wait(); Task task = streamTask.Result.CopyToAsync(fileStream); task.Wait(); ParseMetrics(logger, fileStream); fileStream.Dispose(); streamTask.Dispose(); httpClient.Dispose(); } private static void ParseMetrics(ILogger _, FileStream __) { // Task> metrics = PrometheusMetricsParser.ParseAsync(fileStream); // metrics.Wait(); // foreach (IMetric metric in metrics.Result) { // if (metric is not Gauge gauge) // continue; // foreach (GaugeMeasurement gaugeMeasurement in gauge.Measurements) { // if (string.IsNullOrEmpty(metric.Name)) // continue; // foreach (KeyValuePair keyValuePair in gaugeMeasurement.Labels) { // logger.LogInformation("name:{name}; timestamp:{timestamp}; value:{value}; key-name:{key-name}; key-value:{key-value}", // metric.Name, // gaugeMeasurement.Timestamp, // gaugeMeasurement.Value, // keyValuePair.Key, // keyValuePair.Value); // } // } // } } private static void KumaToGatus(string[] files) { Kuma? kuma; string json; string checkFile; foreach (string file in files) { checkFile = file.ToLower().Replace('_', '-'); if (checkFile != file) File.Move(file, checkFile); json = File.ReadAllText(checkFile); kuma = JsonSerializer.Deserialize(json, KumaCommonSourceGenerationContext.Default.Kuma); if (kuma is null) continue; WriteGatus(checkFile, kuma); } } private static void WriteGatus(string file, Kuma kuma) { List results = [ string.Empty, $"# set GATUS_CONFIG_PATH=./{Path.GetFileName(file)}.yaml", string.Empty, "endpoints:" ]; string[] segments; foreach (MonitorList monitorList in kuma.MonitorList) { if (monitorList.Type is not "http" and not "postgres") continue; results.Add($" - name: {monitorList.Name}"); results.Add($" group: {monitorList.PathName.Split(' ')[0]}"); results.Add($" enabled: {monitorList.Active.ToString().ToLower()}"); results.Add($" interval: {monitorList.Interval}s"); if (monitorList.Type == "http") { results.Add($" method: {monitorList.Method}"); results.Add($" url: \"{monitorList.Url}\""); if (monitorList.AuthMethod == "basic") { results.Add($" # user: \"{monitorList.BasicAuthUser}\""); results.Add($" # password: \"{monitorList.BasicAuthPass}\""); } results.Add(" conditions:"); results.Add(" - \"[STATUS] < 300\""); if (monitorList.Url.Contains("https")) results.Add(" - \"[CERTIFICATE_EXPIRATION] > 48h\""); results.Add($" - \"[RESPONSE_TIME] < {monitorList.Timeout}\""); } else if (monitorList.Type == "postgres") { segments = monitorList.DatabaseConnectionString.Split('@'); if (segments.Length != 2) continue; results.Add($" # connectionString: \"{monitorList.DatabaseConnectionString}\""); results.Add($" url: \"tcp://{segments[1].Split('/')[0]}\""); results.Add(" conditions:"); results.Add(" - \"[CONNECTED] == true\""); } else throw new NotImplementedException(); results.Add(" alerts:"); results.Add(" - type: email"); results.Add(" description: \"healthcheck failed\""); results.Add(" send-on-resolved: true"); results.Add(" - type: gotify"); results.Add(" description: \"healthcheck failed\""); results.Add(" send-on-resolved: true"); results.Add(string.Empty); } File.WriteAllText($"{file}.yaml", string.Join(Environment.NewLine, results)); } }