lampac/Shared/BaseController.cs
lampac-talks f843f04fd4 chore: initial commit 154.3
Signed-off-by: lampac-talks <lampac-talks@users.noreply.github.com>
2026-01-30 16:23:09 +03:00

1085 lines
38 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Shared.Engine;
using Shared.Models;
using Shared.Models.AppConf;
using Shared.Models.Base;
using Shared.Models.Events;
using Shared.Models.SISI.OnResult;
using System.Collections.Concurrent;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using IO = System.IO;
namespace Shared
{
public class BaseController : Controller
{
public static string appversion => "154";
public static string minorversion => "3";
protected static readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new();
protected ActionResult badInitMsg { get; set; }
#region hybridCache
private IHybridCache _hybridCache;
protected IHybridCache hybridCache
{
get
{
if (_hybridCache == null)
_hybridCache = IHybridCache.Get(requestInfo);
return _hybridCache;
}
}
#endregion
#region memoryCache
private IMemoryCache _memoryCache;
protected IMemoryCache memoryCache
{
get
{
if (_memoryCache != null)
return _memoryCache;
var httpContext = HttpContext;
if (httpContext == null)
throw new InvalidOperationException(
"HttpContext is not available. MemoryCache can only be accessed during an HTTP request.");
_memoryCache = httpContext.RequestServices
.GetRequiredService<IMemoryCache>();
return _memoryCache;
}
}
#endregion
#region requestInfo
private RequestModel _requestInfo;
protected RequestModel requestInfo
{
get
{
if (_requestInfo == null)
_requestInfo = HttpContext.Features.Get<RequestModel>();
return _requestInfo;
}
}
#endregion
#region host
private string _host;
public string host
{
get
{
if (_host == null)
_host = AppInit.Host(HttpContext);
return _host;
}
}
#endregion
#region mylocalip
static string lastMyIp = null;
async public ValueTask<string> mylocalip()
{
string key = "BaseController:mylocalip";
if (!memoryCache.TryGetValue(key, out string userIp))
{
if (InvkEvent.IsMyLocalIp())
userIp = await InvkEvent.MyLocalIp(new EventMyLocalIp(requestInfo, HttpContext.Request, HttpContext, hybridCache));
if (string.IsNullOrEmpty(userIp))
{
var myip = await Http.Get<JObject>("https://api.ipify.org/?format=json");
if (myip == null || string.IsNullOrEmpty(myip.Value<string>("ip")))
return lastMyIp;
userIp = myip.Value<string>("ip");
lastMyIp = userIp;
}
memoryCache.Set(key, userIp, DateTime.Now.AddMinutes(5));
}
return userIp;
}
#endregion
#region httpHeaders
public List<HeadersModel> httpHeaders(BaseSettings init, List<HeadersModel> _startHeaders = null)
{
var headers = HeadersModel.Init(_startHeaders);
if (init.headers == null)
return headers;
return httpHeaders(init.host, HeadersModel.Join(HeadersModel.Init(init.headers), headers));
}
public List<HeadersModel> httpHeaders(string site, Dictionary<string, string> headers)
{
return httpHeaders(site, HeadersModel.Init(headers));
}
public List<HeadersModel> httpHeaders(string site, List<HeadersModel> _headers)
{
if (_headers == null || _headers.Count == 0)
return _headers;
#region проверка на changeHeaders
bool changeHeaders = false;
foreach (var h in _headers)
{
if (string.IsNullOrEmpty(h.val) || string.IsNullOrEmpty(h.name))
continue;
if (h.val.Contains("{account_email}") ||
h.val.Contains("{ip}") ||
h.val.Contains("{host}") ||
h.val.Contains("{arg:") ||
h.val.Contains("{head:") ||
h.val.StartsWith("encrypt:"))
{
changeHeaders = true;
break;
}
}
#endregion
if (!changeHeaders && !InvkEvent.IsHttpHeaders())
return _headers;
var tempHeaders = new Dictionary<string, string>(_headers.Count, StringComparer.OrdinalIgnoreCase);
string ip = requestInfo.IP;
foreach (var h in _headers)
{
if (string.IsNullOrEmpty(h.val) || string.IsNullOrEmpty(h.name))
continue;
string val = h.val;
if (val.Contains("{account_email}"))
{
string account_email = HttpContext.Request.Query["account_email"].ToString()?.ToLowerAndTrim() ?? string.Empty;
val = val.Replace("{account_email}", account_email);
}
if (val.Contains("{ip}"))
val = val.Replace("{ip}", ip);
if (val.Contains("{host}"))
val = val.Replace("{host}", site);
if (val.StartsWith("encrypt:"))
val = BaseSettings.BaseDecrypt(val.AsSpan().Slice(8));
if (val.Contains("{arg:"))
{
foreach (Match m in Regex.Matches(val, "\\{arg:([^\\}]+)\\}"))
{
string _a = Regex.Match(HttpContext.Request.QueryString.Value, $"&{m.Groups[1].Value}=([^&]+)").Groups[1].Value;
val = val.Replace(m.Groups[0].Value, _a);
}
}
if (val.Contains("{head:"))
{
foreach (Match m in Regex.Matches(val, "\\{head:([^\\}]+)\\}"))
{
if (HttpContext.Request.Headers.TryGetValue(m.Groups[1].Value, out var _h))
{
val = val.Replace(m.Groups[0].Value, string.Join(" ", _h.ToString()));
}
else
{
val = val.Replace(m.Groups[0].Value, string.Empty);
}
string _a = Regex.Match(HttpContext.Request.QueryString.Value, $"&{m.Groups[1].Value}=([^&]+)").Groups[1].Value;
val = val.Replace(m.Groups[0].Value, _a);
}
}
tempHeaders[h.name] = val;
}
if (InvkEvent.IsHttpHeaders())
{
var eventHeaders = InvkEvent.HttpHeaders(new EventControllerHttpHeaders(site, tempHeaders, requestInfo, HttpContext.Request, HttpContext));
if (eventHeaders != null)
tempHeaders = eventHeaders;
}
return HeadersModel.InitOrNull(tempHeaders);
}
#endregion
#region HostImgProxy
public string HostImgProxy(string uri, int height = 0, List<HeadersModel> headers = null, string plugin = null)
{
if (!AppInit.conf.sisi.rsize || string.IsNullOrWhiteSpace(uri))
return uri;
var init = AppInit.conf.sisi;
int width = init.widthPicture;
height = height > 0 ? height : init.heightPicture;
if (plugin != null && init.proxyimg_disable != null && init.proxyimg_disable.Contains(plugin))
return uri;
if (InvkEvent.IsHostImgProxy())
{
string eventUri = InvkEvent.HostImgProxy(requestInfo, HttpContext, uri, height, headers, plugin);
if (eventUri != null)
return eventUri;
}
if ((width == 0 && height == 0) || (plugin != null && init.rsize_disable != null && init.rsize_disable.Contains(plugin)))
{
if (!string.IsNullOrEmpty(init.bypass_host))
{
string bypass_host = init.bypass_host.Replace("{sheme}", uri.StartsWith("https:") ? "https" : "http");
if (bypass_host.Contains("{uri}"))
bypass_host = bypass_host.Replace("{uri}", Regex.Replace(uri, "^https?://", ""));
if (bypass_host.Contains("{encrypt_uri}"))
bypass_host = bypass_host.Replace("{encrypt_uri}", ImgProxyToEncryptUri(HttpContext, uri, plugin, requestInfo.IP, headers));
return bypass_host;
}
return $"{host}/proxyimg/{ImgProxyToEncryptUri(HttpContext, uri, plugin, requestInfo.IP, headers)}";
}
if (!string.IsNullOrEmpty(init.rsize_host))
{
string rsize_host = init.rsize_host.Replace("{sheme}", uri.StartsWith("https:") ? "https" : "http");
if (rsize_host.Contains("{width}"))
rsize_host = rsize_host.Replace("{width}", width.ToString());
if (rsize_host.Contains("{height}"))
rsize_host = rsize_host.Replace("{height}", height.ToString());
if (rsize_host.Contains("{uri}"))
rsize_host = rsize_host.Replace("{uri}", Regex.Replace(uri, "^https?://", ""));
if (rsize_host.Contains("{encrypt_uri}"))
rsize_host = rsize_host.Replace("{encrypt_uri}", ImgProxyToEncryptUri(HttpContext, uri, plugin, requestInfo.IP, headers));
return rsize_host;
}
return $"{host}/proxyimg:{width}:{height}/{ImgProxyToEncryptUri(HttpContext, uri, plugin, requestInfo.IP, headers)}";
}
static string ImgProxyToEncryptUri(HttpContext httpContext, string uri, string plugin, string ip, List<HeadersModel> headers)
{
var _head = headers != null && headers.Count > 0 ? headers : null;
string encrypt_uri = ProxyLink.Encrypt(uri, ip, _head, plugin: plugin, verifyip: false, ex: DateTime.Now.AddMinutes(20), IsProxyImg: true);
if (AppInit.conf.accsdb.enable && !AppInit.conf.serverproxy.encrypt)
encrypt_uri = AccsDbInvk.Args(encrypt_uri, httpContext);
return encrypt_uri;
}
#endregion
#region HostStreamProxy
public string HostStreamProxy(BaseSettings conf, string uri, List<HeadersModel> headers = null, WebProxy proxy = null, bool force_streamproxy = false, RchClient rch = null)
{
if (!AppInit.conf.serverproxy.enable || string.IsNullOrEmpty(uri) || conf == null)
{
return uri != null && uri.Contains(" ")
? uri.Split(" ")[0].Trim()
: uri?.Trim();
}
if (InvkEvent.IsHostStreamProxy())
{
string _eventUri = InvkEvent.HostStreamProxy(new EventHostStreamProxy(conf, uri, headers, proxy, requestInfo, HttpContext, hybridCache));
if (_eventUri != null)
return _eventUri;
}
if (conf.rhub && !conf.rhub_streamproxy)
{
return uri.Contains(" ")
? uri.Split(" ")[0].Trim()
: uri.Trim();
}
bool streamproxy = conf.streamproxy || conf.apnstream || conf.useproxystream || force_streamproxy;
#region geostreamproxy
if (!streamproxy && conf.geostreamproxy != null && conf.geostreamproxy.Length > 0)
{
string country = requestInfo.Country;
if (!string.IsNullOrEmpty(country) && country.Length == 2)
{
if (conf.geostreamproxy.Contains("ALL") || conf.geostreamproxy.Contains(country))
streamproxy = true;
}
}
#endregion
#region rchstreamproxy
if (!streamproxy && conf.rchstreamproxy != null && rch != null)
{
var rchinfo = rch.InfoConnected();
if (rchinfo?.rchtype != null)
streamproxy = conf.rchstreamproxy.Contains(rchinfo.rchtype);
}
#endregion
if (streamproxy)
{
if (AppInit.conf.serverproxy.forced_apn || conf.apnstream)
{
if (!string.IsNullOrEmpty(conf.apn?.host) && conf.apn.host.StartsWith("http"))
return apnlink(conf.apn, uri, requestInfo.IP, headers);
if (!string.IsNullOrEmpty(AppInit.conf?.apn?.host) && AppInit.conf.apn.host.StartsWith("http"))
return apnlink(AppInit.conf.apn, uri, requestInfo.IP, headers);
return uri;
}
if (headers == null && conf.headers_stream != null && conf.headers_stream.Count > 0)
headers = HeadersModel.Init(conf.headers_stream);
uri = ProxyLink.Encrypt(uri, requestInfo.IP, httpHeaders(conf.host ?? conf.apihost, headers), conf != null && conf.useproxystream ? proxy : null, conf?.plugin);
if (AppInit.conf.accsdb.enable && !AppInit.conf.serverproxy.encrypt)
uri = AccsDbInvk.Args(uri, HttpContext);
return $"{host}/proxy/{uri}";
}
if (conf.url_reserve && !uri.Contains(" or ") && !uri.Contains("/proxy/") &&
!Regex.IsMatch(HttpContext.Request.QueryString.Value, "&play=true", RegexOptions.IgnoreCase))
{
string url_reserve = ProxyLink.Encrypt(uri, requestInfo.IP, httpHeaders(conf.host ?? conf.apihost, headers), conf != null && conf.useproxystream ? proxy : null, conf?.plugin);
if (AppInit.conf.accsdb.enable && !AppInit.conf.serverproxy.encrypt)
url_reserve = AccsDbInvk.Args(uri, HttpContext);
uri += $" or {host}/proxy/{url_reserve}";
}
return uri;
}
static string apnlink(ApnConf apn, string uri, string ip, List<HeadersModel> headers)
{
string link = uri.Contains(" ") || uri.Contains("#")
? uri.Split(" ")[0].Split("#")[0].Trim()
: uri.Trim();
if (apn.secure == "nginx")
{
using (MD5 md5 = MD5.Create())
{
long ex = ((DateTimeOffset)DateTime.Now.AddHours(12)).ToUnixTimeSeconds();
string hash = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes($"{ex}{ip} {apn.secret}"))).Replace("=", "").Replace("+", "-").Replace("/", "_");
return $"{apn.host}/{hash}:{ex}/{link}";
}
}
else if (apn.secure == "cf")
{
using (var sha1 = SHA1.Create())
{
var data = Encoding.UTF8.GetBytes($"{ip}{link}{apn.secret}");
return Convert.ToBase64String(sha1.ComputeHash(data));
}
}
else if (apn.secure == "lampac")
{
string aes = AesTo.Encrypt(System.Text.Json.JsonSerializer.Serialize(new
{
u = link,
i = ip,
v = true,
e = DateTime.Now.AddHours(36),
h = headers?.ToDictionary()
}));
if (uri.Contains(".m3u"))
aes += ".m3u8";
return $"{apn.host}/proxy/{aes}";
}
if (apn.host.Contains("{encode_uri}") || apn.host.Contains("{uri}"))
return apn.host.Replace("{encode_uri}", HttpUtility.UrlEncode(link)).Replace("{uri}", link);
return $"{apn.host}/{link}";
}
#endregion
#region InvokeBaseCache
async public ValueTask<T> InvokeBaseCache<T>(string key, TimeSpan time, RchClient rch, Func<Task<T>> onget, ProxyManager proxyManager = null, bool? memory = null)
{
var semaphore = new SemaphorManager(key, TimeSpan.FromSeconds(40));
try
{
if (rch?.enable != true)
await semaphore.WaitAsync();
if (hybridCache.TryGetValue(key, out T val, memory))
{
HttpContext.Response.Headers["X-Invoke-Cache"] = "HIT";
return val;
}
HttpContext.Response.Headers["X-Invoke-Cache"] = "MISS";
val = await onget.Invoke();
if (val == null || val.Equals(default(T)))
return default;
if (rch?.enable != true)
proxyManager?.Success();
hybridCache.Set(key, val, time, memory);
return val;
}
finally
{
semaphore.Release();
}
}
#endregion
#region InvokeBaseCacheResult
async public ValueTask<CacheResult<T>> InvokeBaseCacheResult<T>(string key, TimeSpan time, RchClient rch, ProxyManager proxyManager, Func<CacheResult<T>, Task<CacheResult<T>>> onget, bool? memory = null)
{
var semaphore = new SemaphorManager(key, TimeSpan.FromSeconds(40));
try
{
if (rch?.enable != true)
await semaphore.WaitAsync();
var entry = hybridCache.Entry<T>(key, memory);
if (entry.success)
{
HttpContext.Response.Headers["X-Invoke-Cache"] = "HIT";
return new CacheResult<T>()
{
IsSuccess = true,
ISingleCache = entry.singleCache,
Value = entry.value
};
}
HttpContext.Response.Headers["X-Invoke-Cache"] = "MISS";
var val = await onget.Invoke(new CacheResult<T>());
if (val == null || val.Value == null)
return new CacheResult<T>() { IsSuccess = false, ErrorMsg = "null" };
if (!val.IsSuccess)
{
if (val.refresh_proxy && rch?.enable != true)
proxyManager?.Refresh();
return val;
}
if (val.Value.Equals(default(T)))
{
if (val.refresh_proxy && rch?.enable != true)
proxyManager?.Refresh();
return val;
}
if (typeof(T) == typeof(string) && string.IsNullOrWhiteSpace(val.ToString()))
{
if (val.refresh_proxy && rch?.enable != true)
proxyManager?.Refresh();
return new CacheResult<T>() { IsSuccess = false, ErrorMsg = "empty" };
}
if (rch?.enable != true)
proxyManager?.Success();
hybridCache.Set(key, val.Value, time, memory);
return new CacheResult<T>() { IsSuccess = true, Value = val.Value };
}
finally
{
semaphore.Release();
}
}
#endregion
#region InvkSemaphore
async public Task<ActionResult> InvkSemaphore(string key, RchClient rch, Func<Task<ActionResult>> func)
{
if (rch?.enable == true)
return await func.Invoke();
var semaphore = new SemaphorManager(key, TimeSpan.FromSeconds(40));
try
{
await semaphore.WaitAsync();
return await func.Invoke();
}
finally
{
semaphore.Release();
}
}
#endregion
#region cacheTime
public TimeSpan cacheTimeBase(int multiaccess, int home = 5, int mikrotik = 2, BaseSettings init = null, int rhub = -1)
{
if (init != null && init.rhub && rhub != -1)
return TimeSpan.FromMinutes(rhub);
int ctime = AppInit.conf.mikrotik ? mikrotik : AppInit.conf.multiaccess ? init != null && init.cache_time > 0 ? init.cache_time : multiaccess : home;
if (ctime > multiaccess)
ctime = multiaccess;
return TimeSpan.FromMinutes(ctime);
}
#endregion
#region IsCacheError
public bool IsCacheError(BaseSettings init, RchClient rch)
{
if (!AppInit.conf.multiaccess || init.rhub)
return false;
if (rch?.enable == true)
return false;
if (memoryCache.TryGetValue(ResponseCache.ErrorKey(HttpContext), out object errorCache))
{
HttpContext.Response.Headers.TryAdd("X-RCache", "true");
if (errorCache is OnErrorResult)
{
HttpContext.Response.StatusCode = 503;
badInitMsg = Json(errorCache);
return true;
}
else if (errorCache is string)
{
string msg = errorCache.ToString();
}
badInitMsg = StatusCode(503);
return true;
}
return false;
}
#endregion
#region IsOverridehost
public bool IsOverridehost(BaseSettings init)
{
if (!string.IsNullOrEmpty(init.overridehost))
return true;
if (init.overridehosts != null && init.overridehosts.Length > 0)
return true;
return true;
}
async public Task<ActionResult> InvokeOverridehost(BaseSettings init)
{
string overridehost = null;
if (!string.IsNullOrEmpty(init.overridehost))
overridehost = init.overridehost;
if (string.IsNullOrEmpty(overridehost) && init.overridehosts != null && init.overridehosts.Length > 0)
overridehost = init.overridehosts[Random.Shared.Next(0, init.overridehosts.Length)];
if (string.IsNullOrEmpty(overridehost))
return null;
if (string.IsNullOrEmpty(init.overridepasswd))
{
if (overridehost.Contains("?"))
overridehost += "&" + HttpContext.Request.QueryString.Value.Remove(0, 1);
else
overridehost += HttpContext.Request.QueryString.Value;
return new RedirectResult(overridehost);
}
overridehost = Regex.Replace(overridehost, "^(https?://[^/]+)/.*", "$1");
string uri = overridehost + HttpContext.Request.Path.Value + HttpContext.Request.QueryString.Value;
string clientip = requestInfo.IP;
if (requestInfo.Country == null)
clientip = await mylocalip();
string html = await Http.Get(uri, timeoutSeconds: 10, headers: HeadersModel.Init
(
("localrequest", init.overridepasswd),
("x-client-ip", clientip)
));
if (html == null)
return new ContentResult() { StatusCode = 502, Content = string.Empty };
html = Regex.Replace(html, "\"(https?://[^/]+/proxy/)", "\"_tmp_ $1");
html = Regex.Replace(html, $"\"{overridehost}", $"\"{host}");
html = html.Replace("\"_tmp_ ", "\"");
return ContentTo(html);
}
#endregion
#region NoAccessGroup
public bool NoAccessGroup(Igroup init, out string error_msg)
{
error_msg = null;
if (init.group > 0)
{
var user = requestInfo.user;
if (user == null || init.group > user.group)
{
error_msg = AppInit.conf.accsdb.denyGroupMesage
.Replace("{account_email}", requestInfo.user_uid)
.Replace("{user_uid}", requestInfo.user_uid);
return true;
}
}
return false;
}
#endregion
#region accsArgs
public string accsArgs(string uri)
{
return AccsDbInvk.Args(uri, HttpContext);
}
#endregion
#region loadKit
public bool IsKitConf { get; private set; }
bool IsLoadKitConf()
{
var init = AppInit.conf.kit;
if (!init.enable || string.IsNullOrEmpty(init.path) || string.IsNullOrEmpty(requestInfo.user_uid))
return false;
return true;
}
async public ValueTask<JObject> loadKitConf()
{
if (IsLoadKitConf())
{
var kit_init = AppInit.conf.kit;
if (kit_init.path.StartsWith("http") && !kit_init.IsAllUsersPath)
return await loadHttpKitConf();
else
return loadFileKitConf();
}
return null;
}
JObject loadFileKitConf()
{
var kit = AppInit.conf.kit;
if (kit.IsAllUsersPath)
{
if (kit.allUsers != null && kit.allUsers.TryGetValue(requestInfo.user_uid, out JObject userInit))
return userInit;
return null;
}
else
{
try
{
DateTime lastWriteTimeUtc = default;
string memKey = $"loadFileKit:{requestInfo.user_uid}";
if (memoryCache.TryGetValue(memKey, out KitCacheEntry _cache))
{
if (_cache.lockTime >= DateTime.Now)
return _cache.init;
lastWriteTimeUtc = IO.File.GetLastWriteTimeUtc(_cache.infile);
if (_cache.lastWriteTimeUtc == lastWriteTimeUtc)
{
_cache.lockTime = DateTime.Now.AddSeconds(Math.Max(5, kit.configCheckIntervalSeconds));
return _cache.init;
}
}
_cache = new KitCacheEntry();
var lockTime = DateTime.Now.AddSeconds(Math.Max(5, kit.configCheckIntervalSeconds));
_cache.infile = $"{kit.path}/{CrypTo.md5(requestInfo.user_uid)}";
if (kit.AesGcm)
{
if (string.IsNullOrEmpty(requestInfo.AesGcmKey))
return null;
string _md5key = CrypTo.md5(requestInfo.AesGcmKey);
_cache.infile = $"{kit.path}/{_md5key[0]}/{_md5key}";
}
if (kit.eval_path != null)
_cache.infile = CSharpEval.Execute<string>(kit.eval_path, new KitConfEvalPath(kit.path, requestInfo.user_uid));
if (!IO.File.Exists(_cache.infile))
{
_cache.lockTime = lockTime;
memoryCache.Set(memKey, _cache, _cache.lockTime);
return null;
}
string json = null;
if (kit.AesGcm)
{
json = CryptoKit.ReadFile(requestInfo.AesGcmKey, _cache.infile);
}
else
{
json = IO.File.ReadAllText(_cache.infile);
}
if (string.IsNullOrWhiteSpace(json))
{
_cache.lockTime = lockTime;
memoryCache.Set(memKey, _cache, _cache.lockTime);
return null;
}
if (lastWriteTimeUtc == default)
lastWriteTimeUtc = IO.File.GetLastWriteTimeUtc(_cache.infile);
_cache.lastWriteTimeUtc = lastWriteTimeUtc;
_cache.lockTime = lockTime;
ReadOnlySpan<char> span = json.AsSpan();
int i = 0;
while (i < span.Length && char.IsWhiteSpace(span[i]))
i++;
if (i == span.Length || span[i] != '{')
json = "{" + json + "}";
_cache.init = JsonConvert.DeserializeObject<JObject>(json);
memoryCache.Set(memKey, _cache, DateTime.Now.AddSeconds(Math.Max(5, kit.cacheToSeconds)));
return _cache.init;
}
catch
{
return null;
}
}
}
async Task<JObject> loadHttpKitConf()
{
var kit = AppInit.conf.kit;
string memKey = $"loadHttpKit:{requestInfo.user_uid}";
if (!memoryCache.TryGetValue(memKey, out JObject appinit))
{
try
{
if (kit.AesGcm && string.IsNullOrEmpty(requestInfo.AesGcmKey))
return null;
string uri = kit.path.Replace("{uid}", HttpUtility.UrlEncode(requestInfo.user_uid));
string json = await Http.Get(uri, timeoutSeconds: 5);
if (json == null)
return null;
if (kit.AesGcm)
json = CryptoKit.Read(requestInfo.AesGcmKey, json);
if (!json.TrimStart().StartsWith("{"))
json = "{" + json + "}";
appinit = JsonConvert.DeserializeObject<JObject>(json);
}
catch { return null; }
memoryCache.Set(memKey, appinit, DateTime.Now.AddSeconds(Math.Max(5, kit.cacheToSeconds)));
}
return appinit;
}
public bool IsLoadKit<T>(T _init) where T : BaseSettings
{
if (InvkEvent.IsLoadKitInit() || InvkEvent.IsLoadKit())
return true;
if (IsLoadKitConf())
return _init.kit;
return false;
}
async public ValueTask<T> loadKit<T>(T _init, Func<JObject, T, T, T> func = null) where T : BaseSettings, ICloneable
{
var clone = _init.IsCloneable ? _init : (T)_init.Clone();
if (_init.kit == false)
{
if (InvkEvent.IsLoadKitInit())
InvkEvent.LoadKitInit(new EventLoadKit(null, clone, null, requestInfo, hybridCache));
return clone;
}
JObject appinit = null;
if (IsLoadKitConf())
{
var kit_init = AppInit.conf.kit;
if (kit_init.path.StartsWith("http") && !kit_init.IsAllUsersPath)
appinit = await loadHttpKitConf();
else
appinit = loadFileKitConf();
}
return loadKit(clone, appinit, func, false);
}
public T loadKit<T>(T _init, JObject appinit, Func<JObject, T, T, T> func = null, bool clone = true) where T : BaseSettings, ICloneable
{
var init = clone ? (T)_init.Clone() : _init;
init.IsKitConf = false;
init.IsCloneable = true;
var defaultinit = InvkEvent.IsLoadKitInit() || InvkEvent.IsLoadKit()
? (clone ? _init : (T)_init.Clone())
: null;
if (InvkEvent.IsLoadKitInit())
InvkEvent.LoadKitInit(new EventLoadKit(defaultinit, init, appinit, requestInfo, hybridCache));
if (!init.kit || appinit == null || string.IsNullOrEmpty(init.plugin) || !appinit.ContainsKey(init.plugin))
return init;
var userconf = appinit.Value<JObject>(init.plugin);
if (AppInit.conf.kit.absolute)
{
foreach (var prop in userconf.Properties())
{
try
{
var propertyInfo = typeof(T).GetProperty(prop.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (propertyInfo?.CanWrite != true)
continue;
var value = prop.Value.ToObject(propertyInfo.PropertyType);
propertyInfo.SetValue(init, value);
}
catch { }
}
}
else
{
void update<T2>(string key, Action<T2> updateAction)
{
if (userconf.ContainsKey(key))
updateAction(userconf.Value<T2>(key));
}
update<bool>("enable", v => init.enable = v);
if (userconf.ContainsKey("enable") && init.enable)
init.geo_hide = null;
update<string>("displayname", v => init.displayname = v);
update<int>("displayindex", v => init.displayindex = v);
update<string>("client_type", v => init.client_type = v);
update<string>("cookie", v => init.cookie = v);
update<string>("token", v => init.token = v);
update<string>("host", v => init.host = v);
update<string>("apihost", v => init.apihost = v);
update<string>("scheme", v => init.scheme = v);
update<bool>("hls", v => init.hls = v);
update<string>("overridehost", v => init.overridehost = v);
update<string>("overridepasswd", v => init.overridepasswd = v);
if (userconf.ContainsKey("overridehosts"))
init.overridehosts = userconf["overridehosts"].ToObject<string[]>();
if (userconf.ContainsKey("headers"))
init.headers = userconf["headers"].ToObject<Dictionary<string, string>>();
init.apnstream = true;
if (userconf.ContainsKey("apn"))
init.apn = userconf["apn"].ToObject<ApnConf>();
init.useproxystream = false;
update<bool>("streamproxy", v => init.streamproxy = v);
update<bool>("qualitys_proxy", v => init.qualitys_proxy = v);
if (userconf.ContainsKey("geostreamproxy"))
init.geostreamproxy = userconf["geostreamproxy"].ToObject<string[]>();
if (userconf.ContainsKey("proxy"))
{
init.proxy = userconf["proxy"].ToObject<ProxySettings>();
if (init?.proxy?.list != null && init.proxy.list.Length > 0)
update<bool>("useproxy", v => init.useproxy = v);
}
if (init.useproxy)
{
init.rhub = false;
init.rhub_fallback = true;
}
else if (AppInit.conf.kit.rhub_fallback || init.rhub_fallback)
{
update<bool>("rhub", v => init.rhub = v);
update<bool>("rhub_fallback", v => init.rhub_fallback = v);
}
else
{
init.rhub = true;
init.rhub_fallback = false;
}
if (init.rhub)
update<int>("cache_time", v => init.cache_time = v);
}
IsKitConf = true;
init.IsKitConf = true;
if (InvkEvent.IsLoadKit())
InvkEvent.LoadKit(new EventLoadKit(defaultinit, init, userconf, requestInfo, hybridCache));
if (func != null)
return func.Invoke(userconf, init, userconf.ToObject<T>());
return init;
}
#endregion
#region RedirectToPlay
public RedirectResult RedirectToPlay(string url)
{
if (!url.Contains(" "))
return new RedirectResult(url);
return new RedirectResult(url.Split(" ")[0].Trim());
}
#endregion
#region ContentTo
public ActionResult ContentTo(string html)
{
return Content(html, html.StartsWith("{") || html.StartsWith("[") ? "application/json; charset=utf-8" : "text/html; charset=utf-8");
}
#endregion
#region ipkey
public string ipkey(string key, ProxyManager proxy, RchClient rch)
{
if (rch != null)
return $"{key}:{(rch.enable ? requestInfo.IP : proxy?.CurrentProxyIp)}";
return $"{key}:{proxy?.CurrentProxyIp}";
}
public string ipkey(string key, RchClient rch) => rch?.enable == true ? $"{key}:{requestInfo.IP}" : key;
#endregion
#region headerKeys
static readonly object _lockHeaderKeys = new();
static readonly StringBuilder sbHeaderKeys = new StringBuilder(PoolInvk.rentLargeChunk);
public string headerKeys(string key, ProxyManager proxy, RchClient rch, params string[] headersKey)
{
if (rch?.enable != true)
return $"{key}:{proxy?.CurrentProxyIp}";
lock (_lockHeaderKeys)
{
sbHeaderKeys.Clear();
const char splitKey = ':';
sbHeaderKeys.Append(key);
sbHeaderKeys.Append(splitKey);
foreach (string hk in headersKey)
{
if (HttpContext.Request.Headers.TryGetValue(hk, out var headerValue))
{
sbHeaderKeys.Append(headerValue);
sbHeaderKeys.Append(splitKey);
}
}
return sbHeaderKeys.ToString();
}
}
#endregion
}
}