diff --git a/src/Moonglade.Configuration/BlogConfig.cs b/src/Moonglade.Configuration/BlogConfig.cs index e76004eb8..56aa9cd7c 100644 --- a/src/Moonglade.Configuration/BlogConfig.cs +++ b/src/Moonglade.Configuration/BlogConfig.cs @@ -1,4 +1,4 @@ - + namespace Moonglade.Configuration; public interface IBlogSettings; @@ -15,6 +15,7 @@ public interface IBlogConfig CustomMenuSettings CustomMenuSettings { get; set; } LocalAccountSettings LocalAccountSettings { get; set; } SystemManifestSettings SystemManifestSettings { get; set; } + SocialProfileSettings SocialProfileSettings { get; set; } IEnumerable LoadFromConfig(IDictionary config); KeyValuePair UpdateAsync(T blogSettings) where T : IBlogSettings; @@ -41,6 +42,7 @@ public class BlogConfig : IBlogConfig public LocalAccountSettings LocalAccountSettings { get; set; } public SystemManifestSettings SystemManifestSettings { get; set; } + public SocialProfileSettings SocialProfileSettings { get; set; } public IEnumerable LoadFromConfig(IDictionary config) { @@ -85,4 +87,4 @@ public KeyValuePair UpdateAsync(T blogSettings) where T : IBl return new(name, json); } -} \ No newline at end of file +} diff --git a/src/Moonglade.Configuration/GeneralSettings.cs b/src/Moonglade.Configuration/GeneralSettings.cs index 55be6ba38..ae3163056 100644 --- a/src/Moonglade.Configuration/GeneralSettings.cs +++ b/src/Moonglade.Configuration/GeneralSettings.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace Moonglade.Configuration; @@ -107,6 +107,9 @@ public class GeneralSettings : IBlogSettings [Display(Name = "Dublin Core License URL")] public string DcLicenseUrl { get; set; } + [Display(Name = "Github PAT")] + public string GithubPat { get; set; } + [JsonIgnore] public static GeneralSettings DefaultValue = new() { diff --git a/src/Moonglade.Configuration/SocialProfileSettings.cs b/src/Moonglade.Configuration/SocialProfileSettings.cs new file mode 100644 index 000000000..1cf45595e --- /dev/null +++ b/src/Moonglade.Configuration/SocialProfileSettings.cs @@ -0,0 +1,311 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonglade.Configuration; + +public class SocialProfileSettings : IBlogSettings +{ + /// + /// Gets or sets the GPG key URL. + /// + /// + /// The GPG key URL. + /// + [Display(Name = "Url to your public GPG key")] + public string GPGKeyUrl { get; set; } + + /// + /// Gets or sets the twitter profile link. + /// + /// + /// The twitter link. + /// + [Display(Name = "Twitter Profile Link")] + public string Twitter { get; set; } + + /// + /// Gets or sets the mastodon profile link.. + /// + /// + /// The mastodon url. + /// + [Display(Name = "Mastodon Profile Link")] + public string Mastodon { get; set; } + + /// + /// Gets or sets the facebook profile url. + /// + /// + /// The facebook profile url. + /// + [Display(Name = "Facebook Profile Link")] + public string Facebook { get; set; } + + /// + /// Gets or sets the instagram profile url. + /// + /// + /// The instagram profile url. + /// + [Display(Name = "Instagram Profile Link")] + public string Instagram { get; set; } + + /// + /// Gets or sets the linkedIn profile url. + /// + /// + /// The linkedIn profile url. + /// + [Display(Name = "Linkedin Profile Link")] + public string LinkedIn { get; set; } + + /// + /// Gets or sets YouTube profile url. + /// + /// + /// You tube profile url. + /// + [Display(Name = "Youtube Profile Link")] + public string YouTube { get; set; } + + /// + /// Gets or sets the GitHub profile url. + /// + /// + /// The GitHub profile url. + /// + [Display(Name = "Github Profile Link")] + public string GitHub { get; set; } + + /// + /// Gets or sets the xing profile url. + /// + /// + /// The xing profile url. + /// + [Display(Name = "Xing Profile Link")] + public string Xing { get; set; } + + /// + /// Gets or sets the Pinterest profile url. + /// + /// + /// The pinterest profile url. + /// + [Display(Name = "Pinterest Profile Link")] + public string Pinterest { get; set; } + + /// + /// Gets or sets the Reddit profile url. + /// + /// + /// The reddit profile url. + /// + [Display(Name = "Reddit Profile Link")] + public string Reddit { get; set; } + + /// + /// Gets or sets the Vimeo profile url. + /// + /// + /// The Vimeo profile url. + /// + [Display(Name = "Vimeo Profile Link")] + public string Vimeo { get; set; } + + /// + /// Gets or sets the Behance profile url. + /// + /// + /// The Behance profile url. + /// + [Display(Name = "Behance Profile Link")] + public string Behance { get; set; } + + /// + /// Gets or sets the Spotify profile url. + /// + /// + /// The Spotify profile url. + /// + [Display(Name = "Spotify Profile Link")] + public string Spotify { get; set; } + + /// + /// Gets or sets the Twitch profile url. + /// + /// + /// The Twitch profile url. + /// + [Display(Name = "Twitch Profile Link")] + public string Twitch { get; set; } + + /// + /// Gets or sets the WhatsAPp profile page. + /// + /// + /// The profile Url Like: https://wa.me/YourNumber . + /// . + /// + [Display(Name = "WhatsApp Profile Link")] + public string WhatsApp { get; set; } + + /// + /// Gets or sets the Skype profile page. + /// + /// + /// The Skype profile page. + /// + [Display(Name = "Skype Profile Link")] + public string Skype { get; set; } + + /// + /// Gets or sets the Discord profile page. + /// + /// + /// The Discord profile page. + /// + [Display(Name = "Discord Profile Link")] + public string Discord { get; set; } + + /// + /// Gets or sets the Steam profile page. + /// + /// + /// The Steam profile page. + /// + [Display(Name = "Steam Profile Link")] + public string Steam { get; set; } + + /// + /// Gets or sets the Stackoverflow profile page. + /// + /// + /// The Stackoverflow profile page. + /// + [Display(Name = "Stackoverflow Profile Link")] + public string Stackoverflow { get; set; } + + /// + /// Gets or sets the DevTo profile page. + /// + /// + /// The DevTo profile page. + /// + [Display(Name = "DevTo Profile Link")] + public string DevTo { get; set; } + + /// + /// Gets or sets the Codersrank profile page. + /// + /// + /// The Codersrank profile page. + /// + [Display(Name = "Codersrank Profile Link")] + public string Codersrank { get; set; } + + /// + /// Gets or sets the amazon author page. If you published a book, this is your author page on Amazon. + /// + /// + /// The amazon author page. + /// + [Display(Name = "AmazonAuthor Profile Link")] + public string AmazonAuthorPage { get; set; } + + /// + /// Gets or sets the Last.fm profile page. + /// + /// + /// The last.fm profile page. + /// + [Display(Name = "LastFM Profile Link")] + public string LastFm { get; set; } + + /// + /// Gets or sets the CodeProject profile page. + /// + /// + /// The CodeProject profile page. + /// + [Display(Name = "Codeproject Profile Link")] + public string CodeProject { get; set; } + + /// + /// Gets or sets the Matrix profile page. + /// + /// + /// The Matrix profile page. + /// + [Display(Name = "Matrix Profile Link")] + public string Matrix { get; set; } + + /// + /// Gets or sets the OpenHub profile page. + /// + /// + /// The OpenHub profile page. + /// + [Display(Name = "OpenHub Profile Link")] + public string OpenHub { get; set; } + + /// + /// Gets or sets the CodeStats profile page. + /// + /// + /// The CodeStats profile page. + /// + [Display(Name = "CodeStats Profile Link")] + public string CodeStats { get; set; } + + /// + /// Gets or sets the KeyBase profile page. + /// + [Display(Name = "KeyBase Profile link")] + public string KeyBase { get; set; } + + // Donations + /// + /// Gets or sets the buy me a coffee. + /// + /// + /// The buy me a coffee. + /// + [Display(Name = "BuyMeACoffe Link")] + public string BuyMeACoffee { get; set; } + + /// + /// Gets or sets the amazon wishlist. + /// + /// + /// The amazon wishlist. + /// + [Display(Name = "Amazon Wishlist Link")] + public string AmazonWishlist { get; set; } + + /// + /// Gets or sets the Paypal.me link. + /// + /// + /// The Paypal.me link. + /// + [Display(Name = "Paypal.me Link")] + public string Paypal { get; set; } + + /// + /// Gets or sets the Patreon profile page. + /// + /// + /// The Patreon profile page. + /// + [Display(Name = "Patreon Link")] + public string Patreon { get; set; } + + /// + /// Gets or sets the Liberapay profile page. + /// + /// + /// The Liberapay profile page. + /// + [Display(Name = "Liberapay Link")] + public string Liberapay { get; set; } +} diff --git a/src/Moonglade.Github.Client/GithubClient.cs b/src/Moonglade.Github.Client/GithubClient.cs new file mode 100644 index 000000000..7b97b7705 --- /dev/null +++ b/src/Moonglade.Github.Client/GithubClient.cs @@ -0,0 +1,85 @@ +using Microsoft.Extensions.Logging; + +using Moonglade.Configuration; + +using RestSharp; + +using ContentType = RestSharp.ContentType; + +namespace Moonglade.Github.Client; + +public class GithubClient : IGithubClient +{ + private readonly IBlogConfig _config; + private readonly ILogger _logger; + + + /// + /// Initializes a new instance of the class. + /// + /// The configuration object that contains settings for the Github client. + public GithubClient(IBlogConfig config, ILogger logger) + { + _config = config; + _logger = logger; + } + + /// + /// Helper function to send an HTTP request through the requests. + /// + /// + /// The response from the HTTP request. + /// + /// HTTP request method (POST, GET, PUT, DELETE, etc.) + /// Endpoint for the request. + /// Payload for the request (if it applies). + /// Throwed in case of not getting a HttpStatusCode.OK. + public Task SendRequest(Method method, string endpoint) + { + return SendRequest(method, endpoint, null); + } + + /// + /// Helper function to send an HTTP request through the requests. + /// + /// + /// The response from the HTTP request. + /// + /// HTTP request method (POST, GET, PUT, DELETE, etc.) + /// Endpoint for the request. + /// Payload for the request (if it applies). + /// Throwed in case of not getting a HttpStatusCode.OK. + public async Task SendRequest(Method method, string endpoint, string? body) + { + var baseUrl = $"https://api.github.com"; + var bodyContentType = "application/vnd.github+json"; + var finalurl = endpoint + "?Accept=" + bodyContentType + "&Authorization Bearer=" + _config.GeneralSettings.GithubPat + "&X-GitHub-Api-Version=2022-11-28"; + + var source = new CancellationTokenSource(); + var token = source.Token; + + var client = new RestClient(baseUrl); + var request = new RestRequest(finalurl, method); + + request.AddHeader("Accept", bodyContentType); + request.AddHeader("Authorization", "Bearer " + _config.GeneralSettings.GithubPat); + request.AddHeader("X-GitHub-Api-Version", "2022-11-28"); + + if ((method == Method.Post || method == Method.Patch) && body != null) + { + request.AddStringBody(body, ContentType.Json); + } + + try + { + var response = await client.ExecuteGetAsync(request, token); + _logger.LogInformation("Request to {FinalUrl} returned {StatusCode}", finalurl, response.StatusCode); + return response; + } + catch (Exception ex) + { + _logger.LogError(ex, "Request to {FinalUrl} failed", finalurl); + throw; + } + } +} diff --git a/src/Moonglade.Github.Client/IGithubClient.cs b/src/Moonglade.Github.Client/IGithubClient.cs new file mode 100644 index 000000000..d943aaa16 --- /dev/null +++ b/src/Moonglade.Github.Client/IGithubClient.cs @@ -0,0 +1,9 @@ +using RestSharp; + +namespace Moonglade.Github.Client +{ + public interface IGithubClient + { + Task SendRequest(Method method, string endpoint, string? body = ""); + } +} diff --git a/src/Moonglade.Github.Client/Models/UserRepoResponse.cs b/src/Moonglade.Github.Client/Models/UserRepoResponse.cs new file mode 100644 index 000000000..c21dc4c01 --- /dev/null +++ b/src/Moonglade.Github.Client/Models/UserRepoResponse.cs @@ -0,0 +1,340 @@ +namespace Moonglade.Github.Client.Models; + +using System.Text.Json.Serialization; + +public class License +{ + [JsonPropertyName("key")] + public string Key { get; set; } = String.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = String.Empty; + + [JsonPropertyName("spdx_id")] + public string SpdxId { get; set; } = String.Empty; + + [JsonPropertyName("url")] + public string Url { get; set; } = String.Empty; + + [JsonPropertyName("node_id")] + public string NodeId { get; set; } = String.Empty; +} + +public class Owner +{ + [JsonPropertyName("login")] + public string Login { get; set; } = String.Empty; + + [JsonPropertyName("id")] + public int? Id { get; set; } = 0; + + [JsonPropertyName("node_id")] + public string NodeId { get; set; } = String.Empty; + + [JsonPropertyName("avatar_url")] + public string AvatarUrl { get; set; } = String.Empty; + + [JsonPropertyName("gravatar_id")] + public string GravatarId { get; set; } = String.Empty; + + [JsonPropertyName("url")] + public string Url { get; set; } = String.Empty; + + [JsonPropertyName("html_url")] + public string HtmlUrl { get; set; } = String.Empty; + + [JsonPropertyName("followers_url")] + public string FollowersUrl { get; set; } = String.Empty; + + [JsonPropertyName("following_url")] + public string FollowingUrl { get; set; } = String.Empty; + + [JsonPropertyName("gists_url")] + public string GistsUrl { get; set; } = String.Empty; + + [JsonPropertyName("starred_url")] + public string StarredUrl { get; set; } = String.Empty; + + [JsonPropertyName("subscriptions_url")] + public string SubscriptionsUrl { get; set; } = String.Empty; + + [JsonPropertyName("organizations_url")] + public string OrganizationsUrl { get; set; } = String.Empty; + + [JsonPropertyName("repos_url")] + public string ReposUrl { get; set; } = String.Empty; + + [JsonPropertyName("events_url")] + public string EventsUrl { get; set; } = String.Empty; + + [JsonPropertyName("received_events_url")] + public string ReceivedEventsUrl { get; set; } = String.Empty; + + [JsonPropertyName("type")] + public string Type { get; set; } = String.Empty; + + [JsonPropertyName("site_admin")] + public bool? SiteAdmin { get; set; } +} + +public class Permissions +{ + [JsonPropertyName("admin")] + public bool? Admin { get; set; } + + [JsonPropertyName("maintain")] + public bool? Maintain { get; set; } + + [JsonPropertyName("push")] + public bool? Push { get; set; } + + [JsonPropertyName("triage")] + public bool? Triage { get; set; } + + [JsonPropertyName("pull")] + public bool? Pull { get; set; } +} + +public class UserRepository +{ + [JsonPropertyName("id")] + public int? Id { get; set; } + + [JsonPropertyName("node_id")] + public string NodeId { get; set; } = String.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = String.Empty; + + [JsonPropertyName("full_name")] + public string FullName { get; set; } = String.Empty; + + [JsonPropertyName("private")] + public bool? Private { get; set; } + + [JsonPropertyName("owner")] + public Owner Owner { get; set; } = new(); + + [JsonPropertyName("html_url")] + public string HtmlUrl { get; set; } = String.Empty; + + [JsonPropertyName("description")] + public string Description { get; set; } = String.Empty; + + [JsonPropertyName("fork")] + public bool? Fork { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } = String.Empty; + + [JsonPropertyName("forks_url")] + public string ForksUrl { get; set; } = String.Empty; + + [JsonPropertyName("keys_url")] + public string KeysUrl { get; set; } = String.Empty; + + [JsonPropertyName("collaborators_url")] + public string CollaboratorsUrl { get; set; } = String.Empty; + + [JsonPropertyName("teams_url")] + public string TeamsUrl { get; set; } = String.Empty; + + [JsonPropertyName("hooks_url")] + public string HooksUrl { get; set; } = String.Empty; + + [JsonPropertyName("issue_events_url")] + public string IssueEventsUrl { get; set; } = String.Empty; + + [JsonPropertyName("events_url")] + public string EventsUrl { get; set; } = String.Empty; + + [JsonPropertyName("assignees_url")] + public string AssigneesUrl { get; set; } = String.Empty; + + [JsonPropertyName("branches_url")] + public string BranchesUrl { get; set; } = String.Empty; + + [JsonPropertyName("tags_url")] + public string TagsUrl { get; set; } = String.Empty; + + [JsonPropertyName("blobs_url")] + public string BlobsUrl { get; set; } = String.Empty; + + [JsonPropertyName("git_tags_url")] + public string GitTagsUrl { get; set; } = String.Empty; + + [JsonPropertyName("git_refs_url")] + public string GitRefsUrl { get; set; } = String.Empty; + + [JsonPropertyName("trees_url")] + public string TreesUrl { get; set; } = String.Empty; + + [JsonPropertyName("statuses_url")] + public string StatusesUrl { get; set; } = String.Empty; + + [JsonPropertyName("languages_url")] + public string LanguagesUrl { get; set; } = String.Empty; + + [JsonPropertyName("stargazers_url")] + public string StargazersUrl { get; set; } = String.Empty; + + [JsonPropertyName("contributors_url")] + public string ContributorsUrl { get; set; } = String.Empty; + + [JsonPropertyName("subscribers_url")] + public string SubscribersUrl { get; set; } = String.Empty; + + [JsonPropertyName("subscription_url")] + public string SubscriptionUrl { get; set; } = String.Empty; + + [JsonPropertyName("commits_url")] + public string CommitsUrl { get; set; } = String.Empty; + + [JsonPropertyName("git_commits_url")] + public string GitCommitsUrl { get; set; } = String.Empty; + + [JsonPropertyName("comments_url")] + public string CommentsUrl { get; set; } = String.Empty; + + [JsonPropertyName("issue_comment_url")] + public string IssueCommentUrl { get; set; } = String.Empty; + + [JsonPropertyName("contents_url")] + public string ContentsUrl { get; set; } = String.Empty; + + [JsonPropertyName("compare_url")] + public string CompareUrl { get; set; } = String.Empty; + + [JsonPropertyName("merges_url")] + public string MergesUrl { get; set; } = String.Empty; + + [JsonPropertyName("archive_url")] + public string ArchiveUrl { get; set; } = String.Empty; + + [JsonPropertyName("downloads_url")] + public string DownloadsUrl { get; set; } = String.Empty; + + [JsonPropertyName("issues_url")] + public string IssuesUrl { get; set; } = String.Empty; + + [JsonPropertyName("pulls_url")] + public string PullsUrl { get; set; } = String.Empty; + + [JsonPropertyName("milestones_url")] + public string MilestonesUrl { get; set; } = String.Empty; + + [JsonPropertyName("notifications_url")] + public string NotificationsUrl { get; set; } = String.Empty; + + [JsonPropertyName("labels_url")] + public string LabelsUrl { get; set; } = String.Empty; + + [JsonPropertyName("releases_url")] + public string ReleasesUrl { get; set; } = String.Empty; + + [JsonPropertyName("deployments_url")] + public string DeploymentsUr { get; set; } = String.Empty; + + [JsonPropertyName("created_at")] + public DateTime? CreatedAt { get; set; } + + [JsonPropertyName("updated_at")] + public DateTime? UpdatedAt { get; set; } + + [JsonPropertyName("pushed_at")] + public DateTime? PushedAt { get; set; } + + [JsonPropertyName("git_url")] + public string GitUrl { get; set; } = String.Empty; + + [JsonPropertyName("ssh_url")] + public string SshUrl { get; set; } = String.Empty; + + [JsonPropertyName("clone_url")] + public string CloneUrl { get; set; } = String.Empty; + + [JsonPropertyName("svn_url")] + public string SvnUrl { get; set; } = String.Empty; + + [JsonPropertyName("homepage")] + public string Homepage { get; set; } = String.Empty; + + [JsonPropertyName("size")] + public int? Size { get; set; } + + [JsonPropertyName("stargazers_count")] + public int? StargazersCount { get; set; } + + [JsonPropertyName("watchers_count")] + public int? WatchersCount { get; set; } + + [JsonPropertyName("language")] + public string Language { get; set; } = String.Empty; + + [JsonPropertyName("has_issues")] + public bool? HasIssues { get; set; } + + [JsonPropertyName("has_projects")] + public bool? HasProjects { get; set; } + + [JsonPropertyName("has_downloads")] + public bool? HasDownloads { get; set; } + + [JsonPropertyName("has_wiki")] + public bool? HasWiki { get; set; } + + [JsonPropertyName("has_pages")] + public bool? HasPages { get; set; } + + [JsonPropertyName("has_discussions")] + public bool? HasDiscussions { get; set; } + + [JsonPropertyName("forks_count")] + public int? ForksCount { get; set; } + + [JsonPropertyName("mirror_url")] + public string MirrorUrl { get; set; } = String.Empty; + + [JsonPropertyName("archived")] + public bool? Archived { get; set; } + + [JsonPropertyName("disabled")] + public bool? Disabled { get; set; } + + [JsonPropertyName("open_issues_count")] + public int? OpenIssuesCount { get; set; } + + [JsonPropertyName("license")] + public License License { get; set; } = new(); + + [JsonPropertyName("allow_forking")] + public bool? AllowForking { get; set; } + + [JsonPropertyName("is_template")] + public bool? IsTemplate { get; set; } + + [JsonPropertyName("web_commit_signoff_required")] + public bool? WebCommitSignoffRequired { get; set; } + + [JsonPropertyName("topics")] + public List Topics { get; set; } = new(); + + [JsonPropertyName("visibility")] + public string Visibility { get; set; } = String.Empty; + + [JsonPropertyName("forks")] + public int? Forks { get; set; } + + [JsonPropertyName("open_issues")] + public int? OpenIssues { get; set; } + + [JsonPropertyName("watchers")] + public int? Watchers { get; set; } + + [JsonPropertyName("default_branch")] + public string DefaultBranch { get; set; } = String.Empty; + + [JsonPropertyName("permissions")] + public Permissions Permissions { get; set; } = new(); +} + diff --git a/src/Moonglade.Github.Client/Moonglade.Github.Client.csproj b/src/Moonglade.Github.Client/Moonglade.Github.Client.csproj new file mode 100644 index 000000000..bb3bef510 --- /dev/null +++ b/src/Moonglade.Github.Client/Moonglade.Github.Client.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/src/Moonglade.Web/Controllers/SettingsController.cs b/src/Moonglade.Web/Controllers/SettingsController.cs index 2e35972ae..33196fe40 100644 --- a/src/Moonglade.Web/Controllers/SettingsController.cs +++ b/src/Moonglade.Web/Controllers/SettingsController.cs @@ -1,5 +1,7 @@ -using Edi.PasswordGenerator; +using Edi.PasswordGenerator; + using Microsoft.AspNetCore.Localization; + using Moonglade.Email.Client; namespace Moonglade.Web.Controllers; @@ -54,6 +56,16 @@ public async Task General(GeneralSettings model, ITimeZoneResolve return NoContent(); } + [HttpPost("social")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task Social(SocialProfileSettings model) + { + blogConfig.SocialProfileSettings = model; + + await SaveConfigAsync(blogConfig.SocialProfileSettings); + return NoContent(); + } + [HttpPost("content")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task Content(ContentSettings model) @@ -238,4 +250,4 @@ private async Task SaveConfigAsync(T blogSettings) where T : IBlogSettings var kvp = blogConfig.UpdateAsync(blogSettings); await mediator.Send(new UpdateConfigurationCommand(kvp.Key, kvp.Value)); } -} \ No newline at end of file +} diff --git a/src/Moonglade.Web/Moonglade.Web.csproj b/src/Moonglade.Web/Moonglade.Web.csproj index f0f3dceb0..9a1c1a7de 100644 --- a/src/Moonglade.Web/Moonglade.Web.csproj +++ b/src/Moonglade.Web/Moonglade.Web.csproj @@ -11,6 +11,12 @@ https://github.com/EdiWang/Moonglade https://github.com/EdiWang/Moonglade + + + + + + @@ -38,6 +44,7 @@ + @@ -50,6 +57,7 @@ + diff --git a/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml b/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml new file mode 100644 index 000000000..8d0322b85 --- /dev/null +++ b/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml @@ -0,0 +1,412 @@ +@page "/admin/settings/socialprofiles" +@{ + var settings = BlogConfig.SocialProfileSettings; +} +@section scripts { + +} + + + +
+
+
+
+
+
@SharedLocalizer["Social Network Profiles"]
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
@SharedLocalizer["Donations"]
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
@SharedLocalizer["Social Profiles"]
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ diff --git a/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml.cs b/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml.cs new file mode 100644 index 000000000..39d70fda0 --- /dev/null +++ b/src/Moonglade.Web/Pages/Settings/SocialProfiles.cshtml.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Moonglade.Web.Pages.Settings +{ + public class SocialProfilesModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/src/Moonglade.Web/Pages/Settings/_SettingsHeader.cshtml b/src/Moonglade.Web/Pages/Settings/_SettingsHeader.cshtml index ba7999249..97b00540f 100644 --- a/src/Moonglade.Web/Pages/Settings/_SettingsHeader.cshtml +++ b/src/Moonglade.Web/Pages/Settings/_SettingsHeader.cshtml @@ -1,4 +1,4 @@ -@{ +@{ var currentPage = ViewContext.RouteData.Values["Page"]?.ToString(); } @@ -23,7 +23,10 @@ @SharedLocalizer["Notification"] + diff --git a/src/Moonglade.Web/Program.cs b/src/Moonglade.Web/Program.cs index 3ae4cf8b0..3559c9248 100644 --- a/src/Moonglade.Web/Program.cs +++ b/src/Moonglade.Web/Program.cs @@ -8,11 +8,13 @@ using Moonglade.Data.PostgreSql; using Moonglade.Data.SqlServer; using Moonglade.Email.Client; +using Moonglade.Github.Client; using Moonglade.Mention.Common; using Moonglade.Pingback; using Moonglade.Setup; using Moonglade.Syndication; using Moonglade.Web.Handlers; +using Moonglade.Web.Services; using Moonglade.Webmention; using SixLabors.Fonts; @@ -156,6 +158,8 @@ services.AddScoped(); services.AddScoped(); services.AddScoped(); +services.AddScoped(); +services.AddScoped(); var app = builder.Build(); diff --git a/src/Moonglade.Web/Services/GithubUserRepositoriesService.cs b/src/Moonglade.Web/Services/GithubUserRepositoriesService.cs new file mode 100644 index 000000000..c554b19bb --- /dev/null +++ b/src/Moonglade.Web/Services/GithubUserRepositoriesService.cs @@ -0,0 +1,40 @@ +using Moonglade.Github.Client; +using Moonglade.Github.Client.Models; + +using Newtonsoft.Json; + +using RestSharp; + +namespace Moonglade.Web.Services; + +public class GithubUserRepositoriesService : IGithubUserRepositoriesService +{ + private readonly string _ghProfile; + private readonly string _ghUser; + private readonly ILogger _logger; + private readonly IGithubClient _githubClient; + + public GithubUserRepositoriesService(IBlogConfig blogConfig, IGithubClient githubClient, ILogger logger) + { + _ghProfile = blogConfig.SocialProfileSettings.GitHub; + _ghUser = _ghProfile.Replace("https://github.com/", ""); + _githubClient = githubClient; + _logger = logger; + } + + public async Task> GetUserRepositories() + { + try + { + var endpoint = $"/users/{_ghUser}/repos"; + var ghResponse = await _githubClient.SendRequest(Method.Get, endpoint, ""); + _logger.LogInformation("Github response: {0}", ghResponse.Content); + return JsonConvert.DeserializeObject>(ghResponse.Content); + } + catch (Exception exception) + { + _logger.LogError(exception, "Error while fetching user repositories from Github. Error: {Message}", exception.Message); + throw; + } + } +} diff --git a/src/Moonglade.Web/Services/IGithubUserRepositoriesService.cs b/src/Moonglade.Web/Services/IGithubUserRepositoriesService.cs new file mode 100644 index 000000000..d1fcf9ce7 --- /dev/null +++ b/src/Moonglade.Web/Services/IGithubUserRepositoriesService.cs @@ -0,0 +1,8 @@ +using Moonglade.Github.Client.Models; + +namespace Moonglade.Web.Services; + +public interface IGithubUserRepositoriesService +{ + Task> GetUserRepositories(); +} diff --git a/src/Moonglade.sln b/src/Moonglade.sln index 5eb3680b0..df187d07f 100644 --- a/src/Moonglade.sln +++ b/src/Moonglade.sln @@ -66,6 +66,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mention", "Mention", "{1AE9 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moonglade.Setup", "Moonglade.Setup\Moonglade.Setup.csproj", "{648FDD86-E047-49A4-9054-BF041FCD0447}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonglade.Github.Client", "Moonglade.Github.Client\Moonglade.Github.Client.csproj", "{53735B46-0842-43BB-BD4B-FDAB379687E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -148,6 +150,10 @@ Global {648FDD86-E047-49A4-9054-BF041FCD0447}.Debug|Any CPU.Build.0 = Debug|Any CPU {648FDD86-E047-49A4-9054-BF041FCD0447}.Release|Any CPU.ActiveCfg = Release|Any CPU {648FDD86-E047-49A4-9054-BF041FCD0447}.Release|Any CPU.Build.0 = Release|Any CPU + {53735B46-0842-43BB-BD4B-FDAB379687E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53735B46-0842-43BB-BD4B-FDAB379687E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53735B46-0842-43BB-BD4B-FDAB379687E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53735B46-0842-43BB-BD4B-FDAB379687E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -174,9 +180,10 @@ Global {F8E8ECCB-0379-416A-8D34-3F3D8428F2E1} = {1AE9B003-34B0-4F5D-BF84-E1C714A7EE31} {1AE9B003-34B0-4F5D-BF84-E1C714A7EE31} = {97439361-03B4-46C9-BC06-49F76BA57879} {648FDD86-E047-49A4-9054-BF041FCD0447} = {97439361-03B4-46C9-BC06-49F76BA57879} + {53735B46-0842-43BB-BD4B-FDAB379687E9} = {F9400B71-ADCD-4FBE-A88F-F85873FDC60B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_CultureCountyOverrides = zh=zh-Hant SolutionGuid = {EBD14882-9899-4C4B-B9BC-22C0B93BD559} + RESX_CultureCountyOverrides = zh=zh-Hant EndGlobalSection EndGlobal