add support for plugin generated pages (interactions). add disallow vpn command
This commit is contained in:
parent
3295315339
commit
3367c5c22f
@ -24,7 +24,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jint" Version="3.0.0-beta-2038" />
|
<PackageReference Include="Jint" Version="3.0.0-beta-2041" />
|
||||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
@ -85,7 +85,7 @@ namespace IW4MAdmin.Application
|
|||||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
||||||
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory,
|
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory,
|
||||||
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
|
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
|
||||||
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager)
|
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager, IInteractionRegistration interactionRegistration)
|
||||||
{
|
{
|
||||||
MiddlewareActionHandler = actionHandler;
|
MiddlewareActionHandler = actionHandler;
|
||||||
_servers = new ConcurrentBag<Server>();
|
_servers = new ConcurrentBag<Server>();
|
||||||
@ -115,9 +115,11 @@ namespace IW4MAdmin.Application
|
|||||||
_changeHistoryService = changeHistoryService;
|
_changeHistoryService = changeHistoryService;
|
||||||
_appConfig = appConfig;
|
_appConfig = appConfig;
|
||||||
Plugins = plugins;
|
Plugins = plugins;
|
||||||
|
InteractionRegistration = interactionRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IPlugin> Plugins { get; }
|
public IEnumerable<IPlugin> Plugins { get; }
|
||||||
|
public IInteractionRegistration InteractionRegistration { get; }
|
||||||
|
|
||||||
public async Task ExecuteEvent(GameEvent newEvent)
|
public async Task ExecuteEvent(GameEvent newEvent)
|
||||||
{
|
{
|
||||||
|
21
Application/Extensions/ScriptPluginExtensions.cs
Normal file
21
Application/Extensions/ScriptPluginExtensions.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Extensions;
|
||||||
|
|
||||||
|
public static class ScriptPluginExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<object> GetClientsBasicData(
|
||||||
|
this DbSet<Data.Models.Client.EFClient> set, int[] clientIds)
|
||||||
|
{
|
||||||
|
return set.Where(client => clientIds.Contains(client.ClientId))
|
||||||
|
.Select(client => new
|
||||||
|
{
|
||||||
|
client.ClientId,
|
||||||
|
client.CurrentAlias,
|
||||||
|
client.Level,
|
||||||
|
client.NetworkId
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
@ -70,23 +70,11 @@ public class InteractionRegistration : IInteractionRegistration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<IInteractionData>> GetInteractions(int? clientId = null,
|
public async Task<IEnumerable<IInteractionData>> GetInteractions(string interactionPrefix = null,
|
||||||
|
int? clientId = null,
|
||||||
Reference.Game? game = null, CancellationToken token = default)
|
Reference.Game? game = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return (await Task.WhenAll(_interactions.Select(async kvp =>
|
return await GetInteractionsPrivate(interactionPrefix, clientId, game, token);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await kvp.Value(clientId, game, token);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex,
|
|
||||||
"Could not get interaction for interaction {InteractionName} and ClientId {ClientId}", kvp.Key,
|
|
||||||
clientId);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}))).Where(interaction => interaction is not null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> ProcessInteraction(string interactionId, int originId, int? targetId = null,
|
public async Task<string> ProcessInteraction(string interactionId, int originId, int? targetId = null,
|
||||||
@ -115,17 +103,40 @@ public class InteractionRegistration : IInteractionRegistration
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return scriptPlugin.ExecuteAction<string>(interaction.ScriptAction, originId, targetId, game, meta, token);
|
return scriptPlugin.ExecuteAction<string>(interaction.ScriptAction, originId, targetId, game, meta,
|
||||||
|
token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex,
|
_logger.LogWarning(ex,
|
||||||
"Could not process interaction for interaction {InteractionName} and OriginId {ClientId}",
|
"Could not process interaction for {InteractionName} and OriginId {ClientId}",
|
||||||
interactionId, originId);
|
interactionId, originId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<IInteractionData>> GetInteractionsPrivate(string prefix = null, int? clientId = null,
|
||||||
|
Reference.Game? game = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return (await Task.WhenAll(_interactions
|
||||||
|
.Where(interaction => string.IsNullOrWhiteSpace(prefix) || interaction.Key.StartsWith(prefix)).Select(
|
||||||
|
async kvp =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await kvp.Value(clientId, game, token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex,
|
||||||
|
"Could not get interaction for {InteractionName} and ClientId {ClientId}",
|
||||||
|
kvp.Key,
|
||||||
|
clientId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}))).Where(interaction => interaction is not null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using IW4MAdmin.Application.Extensions;
|
||||||
using Jint.Runtime.Interop;
|
using Jint.Runtime.Interop;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
@ -112,7 +113,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
}
|
}
|
||||||
|
|
||||||
_scriptEngine = new Engine(cfg =>
|
_scriptEngine = new Engine(cfg =>
|
||||||
cfg.AddExtensionMethods(typeof(Utilities), typeof(Enumerable), typeof(Queryable))
|
cfg.AddExtensionMethods(typeof(Utilities), typeof(Enumerable), typeof(Queryable), typeof(ScriptPluginExtensions))
|
||||||
.AllowClr(new[]
|
.AllowClr(new[]
|
||||||
{
|
{
|
||||||
typeof(System.Net.Http.HttpClient).Assembly,
|
typeof(System.Net.Http.HttpClient).Assembly,
|
||||||
|
@ -22,7 +22,7 @@ public class Plugin : IPlugin
|
|||||||
private static readonly string[] DisabledCommands = {nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"};
|
private static readonly string[] DisabledCommands = {nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"};
|
||||||
private readonly IInteractionRegistration _interactionRegistration;
|
private readonly IInteractionRegistration _interactionRegistration;
|
||||||
private readonly IRemoteCommandService _remoteCommandService;
|
private readonly IRemoteCommandService _remoteCommandService;
|
||||||
private static readonly string MuteInteraction = nameof(MuteInteraction);
|
private static readonly string MuteInteraction = "Webfront::Profile::Mute";
|
||||||
|
|
||||||
public Plugin(ILogger<Plugin> logger, IMetaServiceV2 metaService, IInteractionRegistration interactionRegistration,
|
public Plugin(ILogger<Plugin> logger, IMetaServiceV2 metaService, IInteractionRegistration interactionRegistration,
|
||||||
ITranslationLookup translationLookup, IRemoteCommandService remoteCommandService)
|
ITranslationLookup translationLookup, IRemoteCommandService remoteCommandService)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
let vpnExceptionIds = [];
|
let vpnExceptionIds = [];
|
||||||
|
const vpnAllowListKey = 'Webfront::Nav::Admin::VPNAllowList';
|
||||||
|
const vpnWhitelistKey = 'Webfront::Profile::VPNWhitelist';
|
||||||
|
|
||||||
const commands = [{
|
const commands = [{
|
||||||
name: 'whitelistvpn',
|
name: 'whitelistvpn',
|
||||||
description: 'whitelists a player\'s client id from VPN detection',
|
description: 'whitelists a player\'s client id from VPN detection',
|
||||||
@ -15,8 +18,35 @@ const commands = [{
|
|||||||
|
|
||||||
gameEvent.Origin.Tell(`Successfully whitelisted ${gameEvent.Target.Name}`);
|
gameEvent.Origin.Tell(`Successfully whitelisted ${gameEvent.Target.Name}`);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'disallowvpn',
|
||||||
|
description: 'disallows a player from connecting with a VPN',
|
||||||
|
alias: 'dv',
|
||||||
|
permission: 'SeniorAdmin',
|
||||||
|
targetRequired: true,
|
||||||
|
arguments: [{
|
||||||
|
name: 'player',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
execute: (gameEvent) => {
|
||||||
|
vpnExceptionIds = vpnExceptionIds.filter(exception => parseInt(exception) !== parseInt(gameEvent.Target.ClientId));
|
||||||
|
plugin.configHandler.SetValue('vpnExceptionIds', vpnExceptionIds);
|
||||||
|
|
||||||
|
gameEvent.Origin.Tell(`Successfully disallowed ${gameEvent.Target.Name} from connecting with VPN`);
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
const getClientsData = (clientIds) => {
|
||||||
|
const contextFactory = _serviceResolver.ResolveService('IDatabaseContextFactory');
|
||||||
|
const context = contextFactory.CreateContext(false);
|
||||||
|
const clientSet = context.Clients;
|
||||||
|
const clients = clientSet.GetClientsBasicData(clientIds);
|
||||||
|
context.Dispose();
|
||||||
|
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
|
||||||
const plugin = {
|
const plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 1.5,
|
version: 1.5,
|
||||||
@ -28,7 +58,7 @@ const plugin = {
|
|||||||
let exempt = false;
|
let exempt = false;
|
||||||
// prevent players that are exempt from being kicked
|
// prevent players that are exempt from being kicked
|
||||||
vpnExceptionIds.forEach(function (id) {
|
vpnExceptionIds.forEach(function (id) {
|
||||||
if (id == origin.ClientId) { // when loaded from the config the "id" type is not the same as the ClientId type
|
if (parseInt(id) === parseInt(origin.ClientId)) {
|
||||||
exempt = true;
|
exempt = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -83,33 +113,99 @@ const plugin = {
|
|||||||
this.logger.WriteInfo(`Loaded ${vpnExceptionIds.length} ids into whitelist`);
|
this.logger.WriteInfo(`Loaded ${vpnExceptionIds.length} ids into whitelist`);
|
||||||
|
|
||||||
this.interactionRegistration = _serviceResolver.ResolveService('IInteractionRegistration');
|
this.interactionRegistration = _serviceResolver.ResolveService('IInteractionRegistration');
|
||||||
this.interactionRegistration.RegisterScriptInteraction('WhitelistVPN', this.name, (targetId, game, token) => {
|
|
||||||
if (vpnExceptionIds.includes(targetId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// registers the profile action
|
||||||
|
this.interactionRegistration.RegisterScriptInteraction(vpnWhitelistKey, this.name, (targetId, game, token) => {
|
||||||
const helpers = importNamespace('SharedLibraryCore.Helpers');
|
const helpers = importNamespace('SharedLibraryCore.Helpers');
|
||||||
const interactionData = new helpers.InteractionData();
|
const interactionData = new helpers.InteractionData();
|
||||||
|
|
||||||
interactionData.EntityId = targetId;
|
|
||||||
interactionData.Name = 'Whitelist VPN';
|
|
||||||
interactionData.DisplayMeta = 'oi-circle-check';
|
|
||||||
|
|
||||||
interactionData.ActionMeta.Add('InteractionId', 'command');
|
|
||||||
interactionData.ActionMeta.Add('Data', `whitelistvpn`);
|
|
||||||
interactionData.ActionMeta.Add('ActionButtonLabel', 'Allow');
|
|
||||||
interactionData.ActionMeta.Add('Name', 'Allow VPN Connection');
|
|
||||||
interactionData.ActionMeta.Add('ShouldRefresh', true.toString());
|
|
||||||
|
|
||||||
interactionData.ActionPath = 'DynamicAction';
|
interactionData.ActionPath = 'DynamicAction';
|
||||||
|
interactionData.InteractionId = vpnWhitelistKey;
|
||||||
|
interactionData.EntityId = targetId;
|
||||||
interactionData.MinimumPermission = 3;
|
interactionData.MinimumPermission = 3;
|
||||||
interactionData.Source = this.name;
|
interactionData.Source = this.name;
|
||||||
|
interactionData.ActionMeta.Add('InteractionId', 'command'); // indicate we're wanting to execute a command
|
||||||
|
interactionData.ActionMeta.Add('ShouldRefresh', true.toString()); // indicates that the page should refresh after performing the action
|
||||||
|
|
||||||
|
if (vpnExceptionIds.includes(targetId)) {
|
||||||
|
interactionData.Name = _localization.LocalizationIndex['WEBFRONT_VPN_BUTTON_DISALLOW']; // text for the profile button
|
||||||
|
interactionData.DisplayMeta = 'oi-circle-x';
|
||||||
|
|
||||||
|
interactionData.ActionMeta.Add('Data', `disallowvpn`); // command to execute
|
||||||
|
interactionData.ActionMeta.Add('ActionButtonLabel', _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_DISALLOW_CONFIRM']); // confirm button on the dialog
|
||||||
|
interactionData.ActionMeta.Add('Name', _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_DISALLOW_TITLE']); // title on the confirm dialog
|
||||||
|
} else {
|
||||||
|
interactionData.Name = _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_ALLOW']; // text for the profile button
|
||||||
|
interactionData.DisplayMeta = 'oi-circle-check';
|
||||||
|
|
||||||
|
interactionData.ActionMeta.Add('Data', `whitelistvpn`); // command to execute
|
||||||
|
interactionData.ActionMeta.Add('ActionButtonLabel', _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_ALLOW_CONFIRM']); // confirm button on the dialog
|
||||||
|
interactionData.ActionMeta.Add('Name', _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_ALLOW_TITLE']); // title on the confirm dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
return interactionData;
|
||||||
|
});
|
||||||
|
|
||||||
|
// registers the navigation/page
|
||||||
|
this.interactionRegistration.RegisterScriptInteraction(vpnAllowListKey, this.name, (targetId, game, token) => {
|
||||||
|
|
||||||
|
const helpers = importNamespace('SharedLibraryCore.Helpers');
|
||||||
|
const interactionData = new helpers.InteractionData();
|
||||||
|
|
||||||
|
interactionData.Name = _localization.LocalizationIndex['WEBFRONT_NAV_VPN_TITLE']; // navigation link name
|
||||||
|
interactionData.Description = _localization.LocalizationIndex['WEBFRONT_NAV_VPN_DESC']; // alt and title
|
||||||
|
interactionData.DisplayMeta = 'oi-circle-check'; // nav icon
|
||||||
|
interactionData.InteractionId = vpnAllowListKey;
|
||||||
|
interactionData.MinimumPermission = 3; // moderator
|
||||||
|
interactionData.InteractionType = 2; // 1 is RawContent for apis etc..., 2 is
|
||||||
|
interactionData.Source = this.name;
|
||||||
|
|
||||||
|
interactionData.ScriptAction = (sourceId, targetId, game, meta, token) => {
|
||||||
|
const clientsData = getClientsData(vpnExceptionIds);
|
||||||
|
|
||||||
|
let table = '<table class="table bg-dark-dm bg-light-lm">';
|
||||||
|
|
||||||
|
const disallowInteraction = {
|
||||||
|
InteractionId: 'command',
|
||||||
|
Data: 'disallowvpn',
|
||||||
|
ActionButtonLabel: _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_DISALLOW_CONFIRM'],
|
||||||
|
Name: _localization.LocalizationIndex['WEBFRONT_VPN_ACTION_DISALLOW_TITLE']
|
||||||
|
};
|
||||||
|
|
||||||
|
if (clientsData.length === 0)
|
||||||
|
{
|
||||||
|
table += `<tr><td>No players are whitelisted.</td></tr>`
|
||||||
|
}
|
||||||
|
|
||||||
|
clientsData.forEach(client => {
|
||||||
|
table += `<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/Client/Profile/${client.ClientId}" class="level-color-${client.Level.toLowerCase()} no-decoration">${client.CurrentAlias.Name.StripColors()}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="profile-action no-decoration float-right" data-action="DynamicAction" data-action-id="${client.ClientId}"
|
||||||
|
data-action-meta="${encodeURI(JSON.stringify(disallowInteraction))}">
|
||||||
|
<div class="btn">
|
||||||
|
<i class="oi oi-circle-x mr-5 font-size-12"></i>
|
||||||
|
<span class="text-truncate">${_localization.LocalizationIndex['WEBFRONT_VPN_BUTTON_DISALLOW']}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
table += '</table>';
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
return interactionData;
|
return interactionData;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync: function () {
|
onUnloadAsync: function () {
|
||||||
this.interactionRegistration.UnregisterInteraction('WhitelistVPN');
|
this.interactionRegistration.UnregisterInteraction(vpnWhitelistKey);
|
||||||
|
this.interactionRegistration.UnregisterInteraction(vpnAllowListKey);
|
||||||
},
|
},
|
||||||
|
|
||||||
onTickAsync: function (server) {
|
onTickAsync: function (server) {
|
||||||
|
@ -19,6 +19,7 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
public class BaseController : Controller
|
public class BaseController : Controller
|
||||||
{
|
{
|
||||||
|
protected readonly IInteractionRegistration InteractionRegistration;
|
||||||
protected readonly IAlertManager AlertManager;
|
protected readonly IAlertManager AlertManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -41,6 +42,7 @@ namespace SharedLibraryCore
|
|||||||
|
|
||||||
public BaseController(IManager manager)
|
public BaseController(IManager manager)
|
||||||
{
|
{
|
||||||
|
InteractionRegistration = manager.InteractionRegistration;
|
||||||
AlertManager = manager.AlertManager;
|
AlertManager = manager.AlertManager;
|
||||||
Manager = manager;
|
Manager = manager;
|
||||||
Localization = Utilities.CurrentLocalization.LocalizationIndex;
|
Localization = Utilities.CurrentLocalization.LocalizationIndex;
|
||||||
@ -71,9 +73,7 @@ namespace SharedLibraryCore
|
|||||||
CurrentAlias = new EFAlias { Name = "Webfront Guest" }
|
CurrentAlias = new EFAlias { Name = "Webfront Guest" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected async Task SignInAsync(ClaimsPrincipal claimsPrinciple)
|
protected async Task SignInAsync(ClaimsPrincipal claimsPrinciple)
|
||||||
{
|
{
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrinciple,
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrinciple,
|
||||||
@ -86,7 +86,7 @@ namespace SharedLibraryCore
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
public override async void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
|
if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
|
||||||
{
|
{
|
||||||
@ -154,6 +154,7 @@ namespace SharedLibraryCore
|
|||||||
&& !communityName.Contains("IW4MAdmin")
|
&& !communityName.Contains("IW4MAdmin")
|
||||||
&& AppConfig.CommunityInformation.IsEnabled;
|
&& AppConfig.CommunityInformation.IsEnabled;
|
||||||
|
|
||||||
|
ViewBag.Interactions = await InteractionRegistration.GetInteractions("Webfront::Nav");
|
||||||
ViewBag.Authorized = Authorized;
|
ViewBag.Authorized = Authorized;
|
||||||
ViewBag.Url = AppConfig.WebfrontUrl;
|
ViewBag.Url = AppConfig.WebfrontUrl;
|
||||||
ViewBag.User = Client;
|
ViewBag.User = Client;
|
||||||
|
@ -10,6 +10,8 @@ namespace SharedLibraryCore.Helpers;
|
|||||||
public class InteractionData : IInteractionData
|
public class InteractionData : IInteractionData
|
||||||
{
|
{
|
||||||
public int? EntityId { get; set; }
|
public int? EntityId { get; set; }
|
||||||
|
public string InteractionId { get; set; }
|
||||||
|
public InteractionType InteractionType { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
@ -8,6 +8,8 @@ namespace SharedLibraryCore.Interfaces;
|
|||||||
public interface IInteractionData
|
public interface IInteractionData
|
||||||
{
|
{
|
||||||
int? EntityId { get; }
|
int? EntityId { get; }
|
||||||
|
string InteractionId { get; }
|
||||||
|
InteractionType InteractionType { get; }
|
||||||
bool Enabled { get; }
|
bool Enabled { get; }
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
string Description { get; }
|
string Description { get; }
|
||||||
@ -22,3 +24,10 @@ public interface IInteractionData
|
|||||||
InteractionCallback Action { get; }
|
InteractionCallback Action { get; }
|
||||||
Delegate ScriptAction { get; }
|
Delegate ScriptAction { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum InteractionType
|
||||||
|
{
|
||||||
|
ActionButton,
|
||||||
|
RawContent,
|
||||||
|
TemplateContent
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ public interface IInteractionRegistration
|
|||||||
void RegisterScriptInteraction(string interactionName, string source, Delegate interactionRegistration);
|
void RegisterScriptInteraction(string interactionName, string source, Delegate interactionRegistration);
|
||||||
void RegisterInteraction(string interactionName, Func<int?, Reference.Game?, CancellationToken, Task<IInteractionData>> interactionRegistration);
|
void RegisterInteraction(string interactionName, Func<int?, Reference.Game?, CancellationToken, Task<IInteractionData>> interactionRegistration);
|
||||||
void UnregisterInteraction(string interactionName);
|
void UnregisterInteraction(string interactionName);
|
||||||
Task<IEnumerable<IInteractionData>> GetInteractions(int? clientId = null,
|
Task<IEnumerable<IInteractionData>> GetInteractions(string interactionPrefix = null, int? clientId = null,
|
||||||
Reference.Game? game = null, CancellationToken token = default);
|
Reference.Game? game = null, CancellationToken token = default);
|
||||||
Task<string> ProcessInteraction(string interactionId, int originId, int? targetId = null, Reference.Game? game = null, IDictionary<string, string> meta = null, CancellationToken token = default);
|
Task<string> ProcessInteraction(string interactionId, int originId, int? targetId = null, Reference.Game? game = null, IDictionary<string, string> meta = null, CancellationToken token = default);
|
||||||
}
|
}
|
||||||
|
@ -105,5 +105,6 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
event EventHandler<GameEvent> OnGameEventExecuted;
|
event EventHandler<GameEvent> OnGameEventExecuted;
|
||||||
|
|
||||||
IAlertManager AlertManager { get; }
|
IAlertManager AlertManager { get; }
|
||||||
|
IInteractionRegistration InteractionRegistration { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ namespace WebfrontCore.Controllers
|
|||||||
private readonly IMetaServiceV2 _metaService;
|
private readonly IMetaServiceV2 _metaService;
|
||||||
private readonly IInteractionRegistration _interactionRegistration;
|
private readonly IInteractionRegistration _interactionRegistration;
|
||||||
private readonly IRemoteCommandService _remoteCommandService;
|
private readonly IRemoteCommandService _remoteCommandService;
|
||||||
|
private readonly ITranslationLookup _translationLookup;
|
||||||
private readonly string _banCommandName;
|
private readonly string _banCommandName;
|
||||||
private readonly string _tempbanCommandName;
|
private readonly string _tempbanCommandName;
|
||||||
private readonly string _unbanCommandName;
|
private readonly string _unbanCommandName;
|
||||||
@ -41,12 +42,14 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
public ActionController(IManager manager, IEnumerable<IManagerCommand> registeredCommands,
|
public ActionController(IManager manager, IEnumerable<IManagerCommand> registeredCommands,
|
||||||
ApplicationConfiguration appConfig, IMetaServiceV2 metaService,
|
ApplicationConfiguration appConfig, IMetaServiceV2 metaService,
|
||||||
IInteractionRegistration interactionRegistration, IRemoteCommandService remoteCommandService) : base(manager)
|
IInteractionRegistration interactionRegistration, IRemoteCommandService remoteCommandService,
|
||||||
|
ITranslationLookup translationLookup) : base(manager)
|
||||||
{
|
{
|
||||||
_appConfig = appConfig;
|
_appConfig = appConfig;
|
||||||
_metaService = metaService;
|
_metaService = metaService;
|
||||||
_interactionRegistration = interactionRegistration;
|
_interactionRegistration = interactionRegistration;
|
||||||
_remoteCommandService = remoteCommandService;
|
_remoteCommandService = remoteCommandService;
|
||||||
|
_translationLookup = translationLookup;
|
||||||
|
|
||||||
foreach (var cmd in registeredCommands)
|
foreach (var cmd in registeredCommands)
|
||||||
{
|
{
|
||||||
@ -94,7 +97,18 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
public IActionResult DynamicActionForm(int? id, string meta)
|
public IActionResult DynamicActionForm(int? id, string meta)
|
||||||
{
|
{
|
||||||
var metaDict = JsonSerializer.Deserialize<Dictionary<string, string>>(meta);
|
if (Client.ClientId < 1)
|
||||||
|
{
|
||||||
|
return Ok(new[]
|
||||||
|
{
|
||||||
|
new CommandResponseInfo
|
||||||
|
{
|
||||||
|
Response = _translationLookup["SERVER_COMMANDS_INTERCEPTED"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var metaDict = JsonSerializer.Deserialize<Dictionary<string, string>>(meta.TrimEnd('"').TrimStart('"'));
|
||||||
|
|
||||||
if (metaDict is null)
|
if (metaDict is null)
|
||||||
{
|
{
|
||||||
@ -170,6 +184,17 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
public async Task<IActionResult> DynamicActionAsync(CancellationToken token = default)
|
public async Task<IActionResult> DynamicActionAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
if (Client.ClientId < 1)
|
||||||
|
{
|
||||||
|
return Ok(new[]
|
||||||
|
{
|
||||||
|
new CommandResponseInfo
|
||||||
|
{
|
||||||
|
Response = _translationLookup["SERVER_COMMANDS_INTERCEPTED"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
HttpContext.Request.Query.TryGetValue("InteractionId", out var interactionId);
|
HttpContext.Request.Query.TryGetValue("InteractionId", out var interactionId);
|
||||||
HttpContext.Request.Query.TryGetValue("CustomInputKeys", out var inputKeys);
|
HttpContext.Request.Query.TryGetValue("CustomInputKeys", out var inputKeys);
|
||||||
HttpContext.Request.Query.TryGetValue("Data", out var data);
|
HttpContext.Request.Query.TryGetValue("Data", out var data);
|
||||||
|
@ -77,7 +77,8 @@ namespace WebfrontCore.Controllers
|
|||||||
note.OriginEntityName = await _clientService.GetClientNameById(note.OriginEntityId);
|
note.OriginEntityName = await _clientService.GetClientNameById(note.OriginEntityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var interactions = await _interactionRegistration.GetInteractions(id, client.GameName, token);
|
var interactions =
|
||||||
|
await _interactionRegistration.GetInteractions("Webfront::Profile", id, client.GameName, token);
|
||||||
|
|
||||||
// even though we haven't set their level to "banned" yet
|
// even though we haven't set their level to "banned" yet
|
||||||
// (ie they haven't reconnected with the infringing player identifier)
|
// (ie they haven't reconnected with the infringing player identifier)
|
||||||
|
37
WebfrontCore/Controllers/InteractionController.cs
Normal file
37
WebfrontCore/Controllers/InteractionController.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace WebfrontCore.Controllers;
|
||||||
|
|
||||||
|
public class InteractionController : BaseController
|
||||||
|
{
|
||||||
|
private readonly IInteractionRegistration _interactionRegistration;
|
||||||
|
|
||||||
|
public InteractionController(IManager manager, IInteractionRegistration interactionRegistration) : base(manager)
|
||||||
|
{
|
||||||
|
_interactionRegistration = interactionRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("[controller]/[action]/{interactionName}")]
|
||||||
|
public async Task<IActionResult> Render([FromRoute]string interactionName, CancellationToken token)
|
||||||
|
{
|
||||||
|
var interactionData = (await _interactionRegistration.GetInteractions(interactionName, token: token)).FirstOrDefault();
|
||||||
|
|
||||||
|
if (interactionData is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewBag.Title = interactionData.Description;
|
||||||
|
|
||||||
|
var result = await _interactionRegistration.ProcessInteraction(interactionName, Client.ClientId, token: token);
|
||||||
|
|
||||||
|
return interactionData.InteractionType == InteractionType.TemplateContent
|
||||||
|
? View("Render", result ?? "")
|
||||||
|
: Ok(result);
|
||||||
|
}
|
||||||
|
}
|
8
WebfrontCore/Views/Interaction/Render.cshtml
Normal file
8
WebfrontCore/Views/Interaction/Render.cshtml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@model string
|
||||||
|
|
||||||
|
<div class="content text-wrap mt-20">
|
||||||
|
<h2 class="content-title">
|
||||||
|
<color-code value="@ViewBag.Title"></color-code>
|
||||||
|
</h2>
|
||||||
|
@Html.Raw(Model)
|
||||||
|
</div>
|
@ -1,6 +1,7 @@
|
|||||||
@using SharedLibraryCore.Configuration
|
@using SharedLibraryCore.Configuration
|
||||||
@using SharedLibraryCore.Dtos
|
@using SharedLibraryCore.Dtos
|
||||||
@using Data.Models.Client
|
@using Data.Models.Client
|
||||||
|
@using SharedLibraryCore.Interfaces
|
||||||
|
|
||||||
<!-- left side navigation -->
|
<!-- left side navigation -->
|
||||||
<div class="sidebar-overlay" onclick="halfmoon.toggleSidebar()"></div>
|
<div class="sidebar-overlay" onclick="halfmoon.toggleSidebar()"></div>
|
||||||
@ -43,6 +44,23 @@
|
|||||||
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_HELP"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_HELP"]</span>
|
||||||
</a>
|
</a>
|
||||||
</has-permission>
|
</has-permission>
|
||||||
|
|
||||||
|
@foreach (IInteractionData interactionData in ViewBag.Interactions)
|
||||||
|
{
|
||||||
|
if (!interactionData.InteractionId.StartsWith("Webfront::Nav::Main"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewBag.User.Level >= interactionData.MinimumPermission)
|
||||||
|
{
|
||||||
|
<a asp-controller="Interaction" asp-action="Render" asp-route-interactionName="@interactionData.InteractionId" class="sidebar-link">
|
||||||
|
<i class="oi @interactionData.DisplayMeta mr-5"></i>
|
||||||
|
<span class="name">@interactionData.Name</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<!-- profile -->
|
<!-- profile -->
|
||||||
<has-permission entity="ProfilePage" required-permission="Read">
|
<has-permission entity="ProfilePage" required-permission="Read">
|
||||||
<a asp-controller="Client" asp-action="Profile" asp-route-id="@ViewBag.User.ClientId" class="sidebar-link">
|
<a asp-controller="Client" asp-action="Profile" asp-route-id="@ViewBag.User.ClientId" class="sidebar-link">
|
||||||
@ -104,6 +122,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@foreach (IInteractionData interactionData in ViewBag.Interactions)
|
||||||
|
{
|
||||||
|
if (!interactionData.InteractionId.StartsWith("Webfront::Nav::Social"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewBag.User.Level >= interactionData.MinimumPermission)
|
||||||
|
{
|
||||||
|
<a asp-controller="Interaction" asp-action="Render" asp-route-interactionName="@interactionData.InteractionId" class="sidebar-link">
|
||||||
|
<i class="oi @interactionData.DisplayMeta mr-5"></i>
|
||||||
|
<span class="name">@interactionData.Name</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<!-- admin -->
|
<!-- admin -->
|
||||||
@ -142,6 +177,22 @@
|
|||||||
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]</span>
|
||||||
</a>
|
</a>
|
||||||
</has-permission>
|
</has-permission>
|
||||||
|
|
||||||
|
@foreach (IInteractionData interactionData in ViewBag.Interactions)
|
||||||
|
{
|
||||||
|
if (!interactionData.InteractionId.StartsWith("Webfront::Nav::Admin"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewBag.User.Level >= interactionData.MinimumPermission)
|
||||||
|
{
|
||||||
|
<a asp-controller="Interaction" asp-action="Render" asp-route-interactionName="@interactionData.InteractionId" class="sidebar-link">
|
||||||
|
<i class="oi @interactionData.DisplayMeta mr-5"></i>
|
||||||
|
<span class="name">@interactionData.Name</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@if (ViewBag.Authorized)
|
@if (ViewBag.Authorized)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user