샘플 SDK 최초 Commit

This commit is contained in:
AI-team\cyhan
2025-07-07 16:06:18 +09:00
commit be3d38d066
23 changed files with 2248 additions and 0 deletions

162
src/AptabaseClientBase.cs Normal file
View File

@@ -0,0 +1,162 @@
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Reflection;
using System.Threading.Tasks;
namespace Aptabase.WPF;
internal class AptabaseClientBase : IAsyncDisposable
{
protected static readonly TimeSpan SESSION_TIMEOUT = TimeSpan.FromMinutes(60);
private static readonly Random _random = new();
private readonly ILogger? _logger;
private readonly HttpClient? _http;
private static readonly HttpClient _ipClient = new();
private DateTime _lastTouched = DateTime.UtcNow;
private string _sessionId = NewSessionId();
private static readonly SystemInfo _sysInfo = new();
private static readonly Dictionary<string, string> _hosts = new()
{
{ "US", "https://us.aptabase.com" },
{ "EU", "https://eu.aptabase.com" },
{ "DEV", "https://localhost:3000" },
{ "SH", "" },
};
public AptabaseClientBase(string appKey, AptabaseOptions? options, ILogger? logger)
{
_logger = logger;
var parts = appKey.Split("-");
if (parts.Length != 3 || !_hosts.ContainsKey(parts[1]))
{
_logger?.LogWarning("The Aptabase App Key {AppKey} is invalid. Tracking will be disabled.", appKey);
return;
}
var region = parts[1];
var baseUrl = GetBaseUrl(parts[1], options);
if (baseUrl is null)
{
return;
}
_sysInfo.IsDebug = options?.IsDebugMode ?? SystemInfo.IsInDebugMode(Assembly.GetExecutingAssembly());
_http = region == "DEV" ? new(new LocalHttpsClientHandler()) : new();
_http.BaseAddress = new Uri(baseUrl);
_http.DefaultRequestHeaders.Add("App-Key", appKey);
}
internal async Task TrackEvent(EventData eventData)
{
if (_http is null)
{
return;
}
RefreshSession();
eventData.SessionId = _sessionId;
eventData.SystemProps = _sysInfo;
if (eventData.Props is null)
eventData.Props = new Dictionary<string, object>();
eventData.Props["ipAddress"] = await GetPublicIpAddress();
var body = JsonContent.Create(eventData);
var response = await _http.PostAsync("/api/v0/event", body);
var logPath = "aptabase-debug.log";
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode >= HttpStatusCode.InternalServerError ||
response.StatusCode == HttpStatusCode.RequestTimeout ||
response.StatusCode == HttpStatusCode.TooManyRequests)
{
// throw error, should be retried
response.EnsureSuccessStatusCode();
}
var responseBody = await response.Content.ReadAsStringAsync();
_logger?.LogError("Failed to perform TrackEvent due to {StatusCode} and response body {Body}", response.StatusCode, responseBody);
File.AppendAllText(logPath, $"Failed to perform TrackEvent due to {response.StatusCode} and response body {responseBody}\n");
}
else
{ var responseBody = await response.Content.ReadAsStringAsync();
File.AppendAllText(logPath, $"TrackEvent successful with response body {responseBody}\n");
}
}
private async Task<string> GetPublicIpAddress()
{
try
{
return await _ipClient.GetStringAsync("https://api.ipify.org");
}
catch
{
return "unknown";
}
}
public virtual ValueTask DisposeAsync()
{
_http?.Dispose();
return ValueTask.CompletedTask;
}
private void RefreshSession()
{
var now = DateTime.UtcNow;
var timeSince = now.Subtract(_lastTouched);
if (timeSince >= SESSION_TIMEOUT)
{
_sessionId = NewSessionId();
}
_lastTouched = now;
}
private static string NewSessionId()
{
var epochInSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var random = _random.NextInt64(0, 99999999);
return (epochInSeconds * 100000000 + random).ToString();
}
private string? GetBaseUrl(string region, AptabaseOptions? options)
{
if (region == "SH")
{
if (string.IsNullOrEmpty(options?.Host))
{
_logger?.LogWarning("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.");
return null;
}
return options.Host;
}
return _hosts[region];
}
}