Skip to content

Commit bb6d90b

Browse files
Feature/config updates (#127)
* Refactor forum integration and configuration management - Updated AdminActionTopics to utilize IConfiguration for dynamic forum user ID and forum ID resolution. - Refactored BannersController and ExternalController to use IConfiguration for base URLs. - Enhanced HealthCheckController to validate community URL against configuration. - Modified IdentityHostingStartup to derive OAuth endpoints from configuration. - Adjusted AdminActionsController and ServerAdminController to use configuration for forum base URL and admin ID. - Updated GameTypeIconTagHelper and ServerLinkTagHelper to utilize IConfiguration for dynamic URLs. - Refactored Program.cs to streamline Azure App Configuration integration. - Updated ProxyCheckService to derive API base URL from configuration. - Adjusted Startup.cs to read Application Insights sampling percentages from configuration. - Refactored various Razor views to utilize IConfiguration for dynamic URLs and API keys. - Removed geo_location_api configuration from tfvars files and variables.tf. * fix: update configuration key from SiteUrl to BaseUrl for forum integration * fix: validate Google Maps and Analytics API keys before usage in views * fix: improve parsing of DefaultAdminUserId configuration for admin actions * fix: update configuration access from lowercase to PascalCase for consistency * fix: standardize IConfiguration injection and improve API key validation across views
1 parent 9462fda commit bb6d90b

26 files changed

Lines changed: 159 additions & 150 deletions

src/XtremeIdiots.Portal.Integrations.Forums/AdminActionTopics.cs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.Extensions.Logging;
1+
using Microsoft.Extensions.Configuration;
2+
using Microsoft.Extensions.Logging;
23
using System.Globalization;
34
using MX.InvisionCommunity.Api.Abstractions;
45
using XtremeIdiots.Portal.Integrations.Forums.Extensions;
@@ -11,39 +12,22 @@ namespace XtremeIdiots.Portal.Integrations.Forums;
1112
/// </summary>
1213
/// <param name="logger">Logger for tracking operations and errors</param>
1314
/// <param name="forumsClient">Invision Community API client for forum operations</param>
14-
public class AdminActionTopics(ILogger<AdminActionTopics> logger, IInvisionApiClient forumsClient) : IAdminActionTopics
15+
public class AdminActionTopics(ILogger<AdminActionTopics> logger, IInvisionApiClient forumsClient, IConfiguration configuration) : IAdminActionTopics
1516
{
1617

1718
/// <summary>
1819
/// Creates a forum topic for a new admin action
1920
/// </summary>
20-
/// <param name="type">Type of admin action (warning, ban, etc.)</param>
21-
/// <param name="gameType">Game type to determine appropriate forum section</param>
22-
/// <param name="playerId">Unique identifier of the player</param>
23-
/// <param name="username">Player's username</param>
24-
/// <param name="created">When the admin action was created</param>
25-
/// <param name="text">Admin action description/reason</param>
26-
/// <param name="adminId">ID of the admin who created the action</param>
27-
/// <param name="cancellationToken">Cancellation token for the async operation</param>
28-
/// <returns>Topic ID of the created forum topic, or 0 if creation failed</returns>
2921
public async Task<int> CreateTopicForAdminAction(AdminActionType type, GameType gameType, Guid playerId, string username, DateTime created, string text, string? adminId, CancellationToken cancellationToken = default)
3022
{
3123
try
3224
{
33-
var userId = 21145;
25+
var userId = int.TryParse(configuration["XtremeIdiots:Forums:DefaultAdminUserId"], out var defaultUserId) ? defaultUserId : 21145;
3426

3527
if (adminId is not null)
3628
userId = Convert.ToInt32(adminId);
3729

38-
var forumId = type switch
39-
{
40-
AdminActionType.Observation => gameType.ForumIdForObservations(),
41-
AdminActionType.Warning => gameType.ForumIdForWarnings(),
42-
AdminActionType.Kick => gameType.ForumIdForKicks(),
43-
AdminActionType.TempBan => gameType.ForumIdForTempBans(),
44-
AdminActionType.Ban => gameType.ForumIdForBans(),
45-
_ => 28
46-
};
30+
var forumId = ResolveForumId(type, gameType);
4731

4832
var postTopicResult = await forumsClient.Forums.PostTopic(forumId, userId, $"{username} - {type}", PostContent(type, playerId, username, created, text), type.ToString(), cancellationToken).ConfigureAwait(false);
4933

@@ -83,19 +67,20 @@ public async Task UpdateTopicForAdminAction(int topicId, AdminActionType type, G
8367
if (topicId == 0)
8468
return;
8569

86-
var userId = 21145;
70+
var userId = int.TryParse(configuration["XtremeIdiots:Forums:DefaultAdminUserId"], out var defaultUserId) ? defaultUserId : 21145;
8771

8872
if (adminId is not null)
8973
userId = Convert.ToInt32(adminId);
9074

9175
await forumsClient.Forums.UpdateTopic(topicId, userId, PostContent(type, playerId, username, created, text), cancellationToken).ConfigureAwait(false);
9276
}
9377

94-
private static string PostContent(AdminActionType type, Guid playerId, string username, DateTime created, string text)
78+
private string PostContent(AdminActionType type, Guid playerId, string username, DateTime created, string text)
9579
{
80+
var portalBaseUrl = (configuration["XtremeIdiots:PortalBaseUrl"] ?? "https://portal.xtremeidiots.com").TrimEnd('/');
9681
return "<p>" +
9782
$" Username: {username}<br>" +
98-
$" Player Link: <a href=\"https://portal.xtremeidiots.com/Players/Details/{playerId}\">Portal</a><br>" +
83+
$" Player Link: <a href=\"{portalBaseUrl}/Players/Details/{playerId}\">Portal</a><br>" +
9984
$" {type} Created: {created.ToString(CultureInfo.InvariantCulture)}" +
10085
"</p>" +
10186
"<p>" +
@@ -105,4 +90,40 @@ private static string PostContent(AdminActionType type, Guid playerId, string us
10590
" <small>Do not edit this post directly as it will be overwritten by the Portal. Add comments on posts below or edit the record in the Portal.</small>" +
10691
"</p>";
10792
}
93+
94+
private int ResolveForumId(AdminActionType type, GameType gameType)
95+
{
96+
var defaultForumId = int.TryParse(configuration["XtremeIdiots:Forums:DefaultForumId"], out var parsedForumId) ? parsedForumId : 28;
97+
98+
var category = type switch
99+
{
100+
AdminActionType.Observation or AdminActionType.Warning or AdminActionType.Kick => "AdminLogs",
101+
AdminActionType.TempBan or AdminActionType.Ban => "Bans",
102+
_ => null
103+
};
104+
105+
if (category is null)
106+
return defaultForumId;
107+
108+
var gameKey = gameType switch
109+
{
110+
GameType.Arma or GameType.Arma2 or GameType.Arma3 => "Arma",
111+
_ => gameType.ToString()
112+
};
113+
114+
var configValue = configuration[$"XtremeIdiots:Forums:{category}:{gameKey}"];
115+
if (configValue is not null && int.TryParse(configValue, out var forumId))
116+
return forumId;
117+
118+
// Fallback to hardcoded values from GameTypeExtensions
119+
return type switch
120+
{
121+
AdminActionType.Observation => gameType.ForumIdForObservations(),
122+
AdminActionType.Warning => gameType.ForumIdForWarnings(),
123+
AdminActionType.Kick => gameType.ForumIdForKicks(),
124+
AdminActionType.TempBan => gameType.ForumIdForTempBans(),
125+
AdminActionType.Ban => gameType.ForumIdForBans(),
126+
_ => defaultForumId
127+
};
128+
}
108129
}

src/XtremeIdiots.Portal.Web/ApiControllers/BannersController.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class BannersController(
3838
IConfiguration configuration) : BaseApiController(telemetryClient, logger, configuration)
3939
{
4040
private const string GameServersListCacheKey = nameof(GameServersListCacheKey);
41+
private readonly string gameTrackerBannerBaseUrl = (configuration["GameTracker:BannerBaseUrl"] ?? "https://cache.gametracker.com/server_info/").TrimEnd('/') + "/";
4142

4243
/// <summary>
4344
/// Gets HTML banners for game servers that are enabled for banner display
@@ -168,7 +169,7 @@ public async Task<IActionResult> GetGameTrackerBanner(string ipAddress, string q
168169
{ "BannerUrl", bannerData.BannerUrl ?? "null" }
169170
});
170171

171-
return Redirect(bannerData.BannerUrl ?? $"https://cache.gametracker.com/server_info/{ipAddress}:{queryPort}/{imageName}");
172+
return Redirect(bannerData.BannerUrl ?? $"{gameTrackerBannerBaseUrl}{ipAddress}:{queryPort}/{imageName}");
172173
}
173174

174175
Logger.LogWarning("Failed to retrieve GameTracker banner data for {IpAddress}:{QueryPort}/{ImageName}, falling back to default GameTracker URL",
@@ -184,7 +185,7 @@ public async Task<IActionResult> GetGameTrackerBanner(string ipAddress, string q
184185
{ "Fallback", "true" }
185186
});
186187

187-
return Redirect($"https://cache.gametracker.com/server_info/{ipAddress}:{queryPort}/{imageName}");
188+
return Redirect($"{gameTrackerBannerBaseUrl}{ipAddress}:{queryPort}/{imageName}");
188189
}, nameof(GetGameTrackerBanner), $"ipAddress: {ipAddress}, queryPort: {queryPort}, imageName: {imageName}").ConfigureAwait(false);
189190
}
190191
}

src/XtremeIdiots.Portal.Web/ApiControllers/ExternalController.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class ExternalController(
2424
ILogger<ExternalController> logger,
2525
IConfiguration configuration) : BaseApiController(telemetryClient, logger, configuration)
2626
{
27+
private readonly string portalBaseUrl = (configuration["XtremeIdiots:PortalBaseUrl"] ?? "https://portal.xtremeidiots.com").TrimEnd('/');
2728

2829
/// <summary>
2930
/// Retrieves the latest admin actions for display in external forum widgets
@@ -60,13 +61,13 @@ public async Task<IActionResult> GetLatestAdminActions(CancellationToken cancell
6061

6162
results.Add(new
6263
{
63-
GameIconUrl = $"https://portal.xtremeidiots.com/images/game-icons/{adminActionDto.Player?.GameType.ToString()}.png",
64+
GameIconUrl = $"{portalBaseUrl}/images/game-icons/{adminActionDto.Player?.GameType.ToString()}.png",
6465
AdminName = adminName,
6566
AdminId = adminId,
6667
ActionType = adminActionDto.Type.ToString(),
6768
ActionText = actionText,
6869
PlayerName = adminActionDto.Player?.Username,
69-
PlayerLink = $"https://portal.xtremeidiots.com/Players/Details/{adminActionDto.PlayerId}"
70+
PlayerLink = $"{portalBaseUrl}/Players/Details/{adminActionDto.PlayerId}"
7071
});
7172
}
7273

src/XtremeIdiots.Portal.Web/ApiControllers/HealthCheckController.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public HealthCheckController(
3232
ArgumentNullException.ThrowIfNull(forumsClient);
3333
this.forumsClient = forumsClient;
3434

35+
var expectedCommunityUrl = configuration["XtremeIdiots:Forums:BaseUrl"] ?? "https://www.xtremeidiots.com";
36+
var expectedCommunityUrlWithTrailingSlash = expectedCommunityUrl.TrimEnd('/') + "/";
37+
3538
healthCheckComponents.Add(new HealthCheckComponent
3639
{
3740
Name = "forums-api",
@@ -41,7 +44,7 @@ public HealthCheckController(
4144
try
4245
{
4346
var response = await this.forumsClient.Core.GetCoreHello().ConfigureAwait(false);
44-
var checkResponse = response?.Result?.Data?.CommunityUrl == "https://www.xtremeidiots.com/";
47+
var checkResponse = response?.Result?.Data?.CommunityUrl == expectedCommunityUrlWithTrailingSlash;
4548
return new Tuple<bool, string>(checkResponse, checkResponse ? "OK" : "Unexpected or missing CommunityUrl in forums API response");
4649
}
4750
catch (Exception ex)

src/XtremeIdiots.Portal.Web/Areas/Identity/IdentityHostingStartup.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,10 @@ private static void ConfigureAuthentication(IServiceCollection services, IConfig
113113

114114
options.CallbackPath = new PathString("/signin-xtremeidiots");
115115

116-
options.AuthorizationEndpoint = configuration["xtremeidiots_auth_authorization_endpoint"] ?? "https://www.xtremeidiots.com/oauth/authorize/";
117-
options.TokenEndpoint = configuration["xtremeidiots_auth_token_endpoint"] ?? "https://www.xtremeidiots.com/oauth/token/";
118-
options.UserInformationEndpoint = configuration["xtremeidiots_auth_userinfo_endpoint"] ?? "https://www.xtremeidiots.com/api/core/me";
116+
var forumSiteUrl = (configuration["XtremeIdiots:Forums:BaseUrl"] ?? "https://www.xtremeidiots.com").TrimEnd('/');
117+
options.AuthorizationEndpoint = configuration["xtremeidiots_auth_authorization_endpoint"] ?? $"{forumSiteUrl}/oauth/authorize/";
118+
options.TokenEndpoint = configuration["xtremeidiots_auth_token_endpoint"] ?? $"{forumSiteUrl}/oauth/token/";
119+
options.UserInformationEndpoint = configuration["xtremeidiots_auth_userinfo_endpoint"] ?? $"{forumSiteUrl}/api/core/me";
119120

120121
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
121122
options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");

src/XtremeIdiots.Portal.Web/Controllers/AdminActionsController.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ public class AdminActionsController(
2424
ILogger<AdminActionsController> logger,
2525
IConfiguration configuration) : BaseController(telemetryClient, logger, configuration)
2626
{
27-
private const string DefaultForumBaseUrl = "https://www.xtremeidiots.com/forums/topic/";
28-
private const string DefaultFallbackAdminId = "21145";
29-
private const int DefaultTempBanDurationDays = 7;
3027

3128
/// <summary>
3229
/// Displays the create admin action form for a specific player
@@ -58,12 +55,14 @@ public async Task<IActionResult> Create(Guid id, AdminActionType adminActionType
5855
if (authResult is not null)
5956
return authResult;
6057

58+
var tempBanDurationDays = int.TryParse(Configuration["XtremeIdiots:Forums:DefaultTempBanDays"], out var days) ? days : 7;
59+
6160
var createAdminActionViewModel = new CreateAdminActionViewModel
6261
{
6362
Type = adminActionType,
6463
PlayerId = playerData.PlayerId,
6564
PlayerDto = playerData,
66-
Expires = adminActionType == AdminActionType.TempBan ? DateTime.UtcNow.AddDays(DefaultTempBanDurationDays) : null
65+
Expires = adminActionType == AdminActionType.TempBan ? DateTime.UtcNow.AddDays(tempBanDurationDays) : null
6766
};
6867

6968
return View(createAdminActionViewModel);
@@ -607,12 +606,12 @@ public async Task<IActionResult> DeleteConfirmed(Guid id, Guid playerId, Cancell
607606

608607
private string GetForumBaseUrl()
609608
{
610-
return GetConfigurationValue("AdminActions:ForumBaseUrl", DefaultForumBaseUrl);
609+
return (Configuration["XtremeIdiots:Forums:TopicBaseUrl"] ?? "https://www.xtremeidiots.com/forums/topic/").TrimEnd('/') + "/";
611610
}
612611

613612
private string GetFallbackAdminId()
614613
{
615-
return GetConfigurationValue("AdminActions:FallbackAdminId", DefaultFallbackAdminId);
614+
return Configuration["XtremeIdiots:Forums:DefaultAdminUserId"] ?? "21145";
616615
}
617616

618617
private async Task<PlayerDto?> GetPlayerDataAsync(Guid playerId, CancellationToken cancellationToken = default)

src/XtremeIdiots.Portal.Web/Controllers/ServerAdminController.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ public class ServerAdminController(
4848
ILogger<ServerAdminController> logger,
4949
IConfiguration configuration) : BaseController(telemetryClient, logger, configuration)
5050
{
51-
private const string DefaultForumBaseUrl = "https://www.xtremeidiots.com/forums/topic/";
52-
private const string DefaultFallbackAdminId = "21145";
53-
private const int DefaultTempBanDurationDays = 7;
5451

5552
/// <summary>
5653
/// Displays the main server administration dashboard with available game servers
@@ -854,9 +851,11 @@ public async Task<IActionResult> TempBanRconPlayer(Guid id, int playerSlot, stri
854851
}
855852

856853
// Create admin action record with expiry if we have a GUID
854+
var tempBanDurationDays = int.TryParse(Configuration["XtremeIdiots:Forums:DefaultTempBanDays"], out var days) ? days : 7;
855+
857856
if (!string.IsNullOrWhiteSpace(playerGuid))
858857
{
859-
var expiryDate = DateTime.UtcNow.AddDays(DefaultTempBanDurationDays);
858+
var expiryDate = DateTime.UtcNow.AddDays(tempBanDurationDays);
860859
await CreateAdminActionForRconOperationAsync(
861860
gameServerData.GameType, playerGuid, playerName, AdminActionType.TempBan,
862861
$"Player temp banned from {gameServerData.Title} via RCON by {User.Username()}. Please update with proper reason.",
@@ -871,7 +870,7 @@ await CreateAdminActionForRconOperationAsync(
871870
{ "GameType", gameServerData.GameType.ToString() }
872871
});
873872

874-
return Json(new { success = true, message = $"Player {playerName} has been temp banned for {DefaultTempBanDurationDays} days" });
873+
return Json(new { success = true, message = $"Player {playerName} has been temp banned for {tempBanDurationDays} days" });
875874
}
876875
catch (Exception ex)
877876
{

src/XtremeIdiots.Portal.Web/Helpers/GameTypeIconTagHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace XtremeIdiots.Portal.Web.Helpers;
55

66
[HtmlTargetElement("game-type-icon")]
7-
public class GameTypeIconTagHelper : TagHelper
7+
public class GameTypeIconTagHelper(IConfiguration configuration) : TagHelper
88
{
99
[HtmlAttributeName("game")] public GameType Game { get; set; }
1010
[HtmlAttributeName("external")] public bool External { get; set; }
@@ -14,7 +14,7 @@ public class GameTypeIconTagHelper : TagHelper
1414
public override void Process(TagHelperContext context, TagHelperOutput output)
1515
{
1616
output.TagName = "img";
17-
var baseUrl = External ? "https://portal.xtremeidiots.com" : string.Empty;
17+
var baseUrl = External ? (configuration["XtremeIdiots:PortalBaseUrl"] ?? "https://portal.xtremeidiots.com").TrimEnd('/') : string.Empty;
1818
output.Attributes.SetAttribute("src", $"{baseUrl}/images/game-icons/{Game}.png");
1919
output.Attributes.SetAttribute("alt", Game.ToString());
2020
output.Attributes.SetAttribute("width", Size.ToString());

src/XtremeIdiots.Portal.Web/Helpers/ServerTagHelpers.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
4747
}
4848

4949
[HtmlTargetElement("server-link")]
50-
public class ServerLinkTagHelper : TagHelper
50+
public class ServerLinkTagHelper(IConfiguration configuration) : TagHelper
5151
{
5252
[HtmlAttributeName("type")] public string Type { get; set; } = string.Empty; // gametracker|hlsw|steam
5353
[HtmlAttributeName("game")] public string? Game { get; set; }
@@ -60,7 +60,8 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
6060
switch (Type.ToLowerInvariant())
6161
{
6262
case "gametracker":
63-
output.Attributes.SetAttribute("href", $"https://www.gametracker.com/server_info/{Host}:{Port}");
63+
var gameTrackerBaseUrl = (configuration["GameTracker:ServerInfoBaseUrl"] ?? "https://www.gametracker.com/server_info/").TrimEnd('/') + "/";
64+
output.Attributes.SetAttribute("href", $"{gameTrackerBaseUrl}{Host}:{Port}");
6465
output.Attributes.SetAttribute("target", "_blank");
6566
output.Content.SetHtmlContent("<img src=\"/images/service-icons/gametracker.png\" alt=\"gametracker\"/>");
6667
break;

0 commit comments

Comments
 (0)