diff --git a/DTOs/GithubReleaseResponseDto.cs b/DTOs/GithubReleaseResponseDto.cs new file mode 100644 index 0000000..1786501 --- /dev/null +++ b/DTOs/GithubReleaseResponseDto.cs @@ -0,0 +1,199 @@ +using System.Text.Json.Serialization; + +namespace ForgejoReleaseSync.DTOs; + +public partial class GithubReleaseResponseDto +{ + [JsonPropertyName("url")] + public required Uri Url { get; set; } + + [JsonPropertyName("assets_url")] + public required Uri AssetsUrl { get; set; } + + [JsonPropertyName("upload_url")] + public required string UploadUrl { get; set; } + + [JsonPropertyName("html_url")] + public required Uri HtmlUrl { get; set; } + + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("author")] + public required Author Author { get; set; } + + [JsonPropertyName("node_id")] + public required string NodeId { get; set; } + + [JsonPropertyName("tag_name")] + public required string TagName { get; set; } + + [JsonPropertyName("target_commitish")] + public required string TargetCommitish { get; set; } + + [JsonPropertyName("name")] + public required string Name { get; set; } + + [JsonPropertyName("draft")] + public bool Draft { get; set; } + + [JsonPropertyName("immutable")] + public bool Immutable { get; set; } + + [JsonPropertyName("prerelease")] + public bool Prerelease { get; set; } + + [JsonPropertyName("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonPropertyName("updated_at")] + public DateTimeOffset UpdatedAt { get; set; } + + [JsonPropertyName("published_at")] + public DateTimeOffset PublishedAt { get; set; } + + [JsonPropertyName("assets")] + public required Asset[] Assets { get; set; } + + [JsonPropertyName("tarball_url")] + public required Uri TarballUrl { get; set; } + + [JsonPropertyName("zipball_url")] + public required Uri ZipballUrl { get; set; } + + [JsonPropertyName("body")] + public required string Body { get; set; } + + [JsonPropertyName("reactions")] + public required Reactions Reactions { get; set; } +} + +public partial class Asset +{ + [JsonPropertyName("url")] + public required Uri Url { get; set; } + + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("node_id")] + public required string NodeId { get; set; } + + [JsonPropertyName("name")] + public required string Name { get; set; } + + [JsonPropertyName("label")] + public required string Label { get; set; } + + [JsonPropertyName("uploader")] + public required Author Uploader { get; set; } + + [JsonPropertyName("state")] + public State State { get; set; } + + [JsonPropertyName("size")] + public long Size { get; set; } + + [JsonPropertyName("digest")] + public required string Digest { get; set; } + + [JsonPropertyName("download_count")] + public long DownloadCount { get; set; } + + [JsonPropertyName("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonPropertyName("updated_at")] + public DateTimeOffset UpdatedAt { get; set; } + + [JsonPropertyName("browser_download_url")] + public required Uri BrowserDownloadUrl { get; set; } +} + +public partial class Author +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("avatar_url")] + public required Uri AvatarUrl { get; set; } + + [JsonPropertyName("gravatar_id")] + public required string GravatarId { get; set; } + + [JsonPropertyName("url")] + public required Uri Url { get; set; } + + [JsonPropertyName("html_url")] + public required Uri HtmlUrl { get; set; } + + [JsonPropertyName("followers_url")] + public required Uri FollowersUrl { get; set; } + + [JsonPropertyName("following_url")] + public required string FollowingUrl { get; set; } + + [JsonPropertyName("gists_url")] + public required string GistsUrl { get; set; } + + [JsonPropertyName("starred_url")] + public required string StarredUrl { get; set; } + + [JsonPropertyName("subscriptions_url")] + public required Uri SubscriptionsUrl { get; set; } + + [JsonPropertyName("organizations_url")] + public required Uri OrganizationsUrl { get; set; } + + [JsonPropertyName("repos_url")] + public required Uri ReposUrl { get; set; } + + [JsonPropertyName("events_url")] + public required string EventsUrl { get; set; } + + [JsonPropertyName("received_events_url")] + public required Uri ReceivedEventsUrl { get; set; } + + [JsonPropertyName("site_admin")] + public bool SiteAdmin { get; set; } +} + +public partial class Reactions +{ + [JsonPropertyName("url")] + public required Uri Url { get; set; } + + [JsonPropertyName("total_count")] + public long TotalCount { get; set; } + + [JsonPropertyName("+1")] + public long The1 { get; set; } + + [JsonPropertyName("-1")] + public long Reactions1 { get; set; } + + [JsonPropertyName("laugh")] + public long Laugh { get; set; } + + [JsonPropertyName("hooray")] + public long Hooray { get; set; } + + [JsonPropertyName("confused")] + public long Confused { get; set; } + + [JsonPropertyName("heart")] + public long Heart { get; set; } + + [JsonPropertyName("rocket")] + public long Rocket { get; set; } + + [JsonPropertyName("eyes")] + public long Eyes { get; set; } +} + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum State +{ + [JsonStringEnumMemberName("uploaded")] + Uploaded +} diff --git a/Program.cs b/Program.cs index ab0b632..8ddd258 100644 --- a/Program.cs +++ b/Program.cs @@ -11,5 +11,14 @@ var repositoryConfig = await RepoConfigLoader.LoadConfigAsync( config.RepositoryConfigFilePath); builder.Services.AddSingleton(repositoryConfig); +builder.Services.AddSingleton(); + var app = builder.Build(); + +using var scope = app.Services.CreateScope(); + +await scope.ServiceProvider + .GetRequiredService() + .FetchRepoReleasesAsync("SagerNet","sing-box"); + await app.RunAsync(); \ No newline at end of file diff --git a/Services/GithubService.cs b/Services/GithubService.cs index 4c81531..96081bf 100644 --- a/Services/GithubService.cs +++ b/Services/GithubService.cs @@ -1,6 +1,57 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using ForgejoReleaseSync.DTOs; +using Microsoft.Extensions.Logging; + namespace ForgejoReleaseSync.Services; public class GithubService { + private readonly AppConfig _config; + private readonly RepositoryConfig _repoConfig; + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public GithubService(AppConfig config, RepositoryConfig repoConfig, ILogger logger) + { + var client = new HttpClient(); + client.BaseAddress = new Uri("https://api.github.com"); + client.DefaultRequestHeaders.Add("User-Agent", "ForgejoReleaseSync"); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + client.Timeout = TimeSpan.FromSeconds(90); + + _config = config; + _repoConfig = repoConfig; + _httpClient = client; + _logger = logger; + } + + + public async Task> FetchRepoReleasesAsync(string owner, string repo) + { + try + { + var response = await _httpClient.GetAsync( + $"/repos/{owner}/{repo}/releases?per_page=3&page=1"); + + response.EnsureSuccessStatusCode(); + + var responseStream = await response.Content.ReadAsStreamAsync(); + var releases = await JsonSerializer.DeserializeAsync(responseStream, + AppJsonSerializerContext.Default.ListGithubReleaseResponseDto); + + return releases ?? []; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to fetch posts from Mastodon"); + return []; + } + } +} + +[JsonSerializable(typeof(List))] +internal partial class AppJsonSerializerContext : JsonSerializerContext +{ } \ No newline at end of file diff --git a/Services/SyncService.cs b/Services/SyncService.cs new file mode 100644 index 0000000..57ba7d7 --- /dev/null +++ b/Services/SyncService.cs @@ -0,0 +1,6 @@ +namespace ForgejoReleaseSync.Services; + +public class SyncService +{ + +} \ No newline at end of file