From f72a8aa59febe4ada9c5d461df4e9a6325f27c41 Mon Sep 17 00:00:00 2001 From: linmeideli <2558527373@qq.com> Date: Sun, 6 Sep 2026 14:00:49 +0800 Subject: [PATCH] Update --- marketplace/Empostor.Plugins.Monitor.dll | Bin 14848 -> 17408 bytes marketplace/Empostor.Plugins.Privacy.dll | Bin 18432 -> 19456 bytes .../MonitorController.cs | 142 ------------------ .../MonitorMiddleware.cs | 106 +++++++++++++ src/Empostor.Plugins.Monitor/MonitorModels.cs | 64 ++++++++ .../MonitorPluginStartup.cs | 4 +- .../PrivacyController.cs | 60 -------- .../PrivacyMiddleware.cs | 75 +++++++++ .../PrivacyPluginStartup.cs | 3 +- src/Empostor.Server/Plugins/PluginLoader.cs | 46 ++++++ src/Empostor.Server/Program.cs | 31 +++- 11 files changed, 323 insertions(+), 208 deletions(-) delete mode 100644 src/Empostor.Plugins.Monitor/MonitorController.cs create mode 100644 src/Empostor.Plugins.Monitor/MonitorMiddleware.cs create mode 100644 src/Empostor.Plugins.Monitor/MonitorModels.cs delete mode 100644 src/Empostor.Plugins.Privacy/PrivacyController.cs create mode 100644 src/Empostor.Plugins.Privacy/PrivacyMiddleware.cs diff --git a/marketplace/Empostor.Plugins.Monitor.dll b/marketplace/Empostor.Plugins.Monitor.dll index 84084cc90be7bd2bc6b32d2a814289c0c8039359..42bc88a5f4ca194c837d97dc5ed28918e927b5ae 100644 GIT binary patch literal 17408 zcmeHueRLevmFIm`-PNtuS9eRYg+E)iW!jeAmcN7zwy`YPSj4gok}w9dQma*#8(Lj* zcgsc=a@xbkiJ5o0^Mi1DX+QGnTbRXS1MaF$7w3z*yE8ohy8Kq*SA!jpTW4*1O;Dgo@sZoGOU^pdRdNvE62Dk7hM49rCV%IpgDaKWuB$*fkxeE zUq}1dO&HpM5nTw;Nm%MJv`IALG4x4v(_?rh(G`#3orLKgqhS(0@ff$UV3?vXy~QS? z&D<1p*0p-+A@E^sh@y+Z4MZ;mJc`CV(I~J=&02z66uob$(k+a8Ad144>MZ1p=1dp- zVbyTJHA~GpPuP>$3uYAI5wI|)P!d+r<)Ci1!ZzuheMvM1M%asz6|52lv{2ZaxruA8 z1P27lR+W||%F9-lmL*HdqH9oM)!Q~z;%3dT@wLoHfU&J@j3ee4zl?FX3bR;|M~L-&)Ud@GOXmqp0g?UOsd2QG}Z3ys-i7?Bx(>&T~l=W7lF~L|N8CVZd5s zYdMO*Ev>kbD*~V*mqamhTcSR$jo~n;&S;^KVULwYS7OYdIh1WjnHvWfOpXJHa~xn; zc;wL^2m-Dc_y5ZpN*J;QP7WZrypZLwJ3B4tjz!c!-D!ibPGVG&+5RfdAYGT zGXfu1y%ktY4o3c^N^+*66|EHDIl=Q4V@9w)B;jOrvO<`!756;a@npBLzIy8_+^Bhm zg_IJYJpAq9Yyg8iOBW#a1pxz^Y=$6Upf5H<5HKoSh9F=BT!tWEAO`Fzf`EbaXEOu= zquON%0tO=5mJkFC^v-4o0tR|+GXwzxj?t^dmrya$-2}ma$l)LAk z+z$uk9ylo5?x1Y7mc5!auxck`u>0yjiu(is1GB(p2m(ff%Mb(%%ne&Y5HM!C3_*V4 zw8;t`lvOz>D{)Ykb}c~?`xFl=Co#-74|1qZ2tQt?LnLX_lbPfxi@vr*yID3&=Nr}{ zgpA4-@-2mIU8`ah1d&joaA|<}|K+6HRUy!_7(JHdgOp_|6TH4?6A?fj03!-d8dg69;fLq|?#ny0 z8O;G%c{KmzLKd&q>?KGG>rwQeh2DG=)@c)5Sf$;@xAP5HE|k{b)P$}<=%P3Gv7l`) z&_B-JI#0;6{!h_;$LP!*T=P=st24qzadFBfz$$j2&KvgHJ=m(vbbAo;mUqEh=)!*$ z{cL-k_u_rvrQSKhAAZTGDa+i!Kgi#O`C z8+~hWonM=O-W~?=-}weaqq0XLdNU-&O_+zamo`g$v3|`ri8(9rh2-K1D>w-WyoKKC)h4*4QoD_BCBenjph$x2 ztyy&*?!Ty#)iLC8{8C@eFZjYZ9c#kEqeM^PUWJ^!uqT&EriZgEaT+o78p3>GUl08``hoR?7GBlY)rtF;xnFSn z!YwHaHp>t0S9@lDv9bb76a7xDCM0fDCuAfzl?XkSJu{Dy2Nyg@_uyjNbX-W^GJafE ziHqy-+oxvTBV$4ZZGp zHek>l9uMJ8-B=vg0Y?y6+i4Ni>yQm?2qNLAFM_3w+Xo)K@n6fyz0*W`Q)kbQH$8hWpKZ)KQnsmce#bY1N#cw@W&1Hfe9gW4L}052IdGqBHJLmyN< z3$LDmR|7O&#UA$6Jj=mDKVyKH!002`KMj1NPzux+Joi;?M4b4Dsg5912I7UOvpQ3C zh(4z7_up6bGBwg_p{4>{b_unS-V~(Yi_S}Ml+_{mxKMLwlThCiY7wgY9x8b!T2defmEshzLt>1~JlMy*0_ zeAl6-s7*D{2M+ZvJ*_v;M-KJ#;CwZMe8|l_V&{SiDLCj*zd_kdYIG=HNYPAca;V?c z%vX)H#G!&{uaQNC}U3#!wmFn-8c^r%xdsZW4<+@bE$gP@*qsJG#X z+4Nn9dJmqMO+Rp`BN&xA^dpD*6O76n`k6zq)pO~rL$TF!>6}CD!l=xne{`tpF)H)u zcQz%K&8HQ-)JUBDNS{xeg?fP&t2gur4!3^OP5AO65XK zH7@0{DYehD7}PAG?DiJZBBzWc7gJ+PQIgdw>cb+f(bthNk$12@+doA|vA!$h0xq4x zqN*boFo$u?!R=D6W!L?G1?#W@P-KaPJy^jNJtg+Y{Q%f0B6%9 z0$&7-&?~~RQt=ZyM@OrFK_9B^pnbO(-+reerj+(` zzzS_EU0hUg19gYYkeW>EYk1m0Ep; zuC;5&J$2e8YM^S77E+UX8(`480dR5c7VR4CNMJi~&Iuv4E3`=K(ypl5sqNKn2=r=k z?SCNuOi^oSR7+~V@Ey{|)xE*n0AH=X9h?kG$F(ED6WTveU)7&!aW$&$*AA+^>OtU- zVfVEURz0JAi@vOX7yQ4g_&(s1;7`$0o}X*Sv~gsua(B}bH(<#6!=sSQN^djJ=gx>?$E_ymBLCV`fw_x8k zKo}u!g8r0F0NzeVbW>oGo}@}6FQvCj>FvV51Mr0RPQi~uW~OmcWWFRaUlRUf;4d~# z3;&F?bw=8KUSysZndj--bk6%CBri9nL}p54&VjQN+rH0};U)DKuuUnpRw?#?E_j7{ zl5RAb1aA`lLcy1Sf56y`y4$^*rEa_M+eM~B@Gi(4GU9^Ag`WiHF5{SRjtS>DIQJPR zh4Y95x%J1uf7CcFoHGt&{)^!MwQ*KBQx0VQIq-jEkS6v!kogtblk|oW5l)i>nZE@5 zUmBZ*)9yg#cY*(&F(90{1DT%$|4}0^oMR4T{&DbsYn&9$BMxN#X~EApkU3`spK>5` zNM~y!0-JP>hs}bw3qBxtT=2Z$Ck37sct-eV1)ma}Jkoa$>xl^7B=}~*2L$E?o)mam z;2Dvb5=e+j*duVWzyX1IfhPr?7I;=586q$6w82q*Rv`Hp&jTLOPYNVt5ak^ZxCvSL zv&e5>Qje>b)O)I0TdLily`*Va2|T3Z1j{GVzMuzd!`A~9fL8}=03Qr6{QIgf;Ikpd zU-L6;_b|)}{!}f$2>D;cGVcT#uM9D)7yf6fX90g$XZc1i!@ESMEpsz;vJE;`PJZX4 zVTXy62lkRa;I&wj6fCO%9zs@7*z;8aujBIwR)cEb4Upo!SuOCHkmCIo9zS9Qs{=j@ zP(u!B06qs$!=7g*@Ogk5&4(0D3V<3lK}yjAK#dkcO3@-f4ZDW1^Md+meu4m7N`L<(yP0x* zFXEYZdEN8cucU6d96w7dmka7`)8vBsfbj76u<#4&cTbZ$uT@~z#QJpjNQYPW2?amX zh*o0Df_p<{7d=dUSPT0opyISt9mai<{)6hH|ERj?Yw7{|NIgXJwdd)p+N;2yrw{Dz zZrHTGzkh9iD{a^^mP`%xZqVeLp}b12!cX@7rzd)i8mk62^b-tkehbzl{3 z8%rl}8#Z(Od#ubYcs|r)=GxZKhOYD>>lU*;JDyH#8tU%{XHO=XGq28C=>oG2O4rkd zZfj^PWo{BB-R6ju8SfsXEHoB~4Vx0wGoH1=$zxhu^bPnkJ$NX+TV7U}lUQ8PWX&B~0#b6mb5 zmmD#(x%kNFreuHrwxpQ?4>S)-k>U1X(=Gk|TjB}!kR#fG?**oFuN_OX-suHA+!tZ* zPQ`QkVbk;q*O{3t+%&zQJ3cy{*<0w$G(-DF*`gjZVWo$%(`#N8ha0A|+c8Lo%nmD^ zc7|)Z$nK1lFtb@JbCK#!bWbyeTkEjkq>HFa#mCLe#}sb2vKOghcMWkein`M@a#VMl zxdWCR3_M^Gmx9=afOD0Z+aAvz5T+f82ts#qku#n=U$)27Ln)DGYFow{aX4M9i@L3J zGG}E3=awcr6V08y{imxfm}*?3B5mV>7;xs7%?BYC~T)azWfy7gH zJaHhIHVKoqjLvefxrJO8@1tw zuxVv~znD!tWs@m0Bk2K3huU*^ay>W(rM4+*;c) z1ZKL(-#Rji5Vtb6_ltbj;%#t=lM<#EcBMIo#5t|TQy(jZAlRQA9?OVaQOULD{vPDc z-1xOf<#;aaQ6A9LrpZ+)<3Rk0xVX$eo#N%DR9r(!KHQEp#2* zFPObJ?Z66~iKmKM>|n^ooZJ!{N~Nev)Y{pFJ)E&pPFHL*oCQTDpu4&d=a(M1mOA3; zgoz2mq0);LKJ6v|yD3rOctSL|xb)B-XqR=BnR8J$vdlf^Ai9!DCZqw;w!?A=XDmx% zl`s^gq&t5204>H-GUg7gt<4(k?-?7+W#Y1bxcK09n!L)J>BM+f`eu9ODh#x_-`RRl z7Ze{#Ca@B@yAk5{x`xwMhS$8IB>P7k&}r_EkEL=K79rzqy)|KCX@wj+XID?BnN4Jp zMNVPzmlj;uMY6+70z8w14X(AF$vCV*cqOv2lDW_B;w8)RypS?Bvf2n)8X$#oY#O9H zd}nzM2&CH)n_Hx~ELSLtkB*kOxjj3&)68|iMKN9rsLPHSB=Agb41)FoSk820Sl*R$ zT(BdVK3KX5yScx=cXvEf*n_#WI|JL(Lmep-0$uhV4r%95a>&eF+(6l4qp+QKusp@= zjeyJUDv8npk7u(>Y}l0R?;qrSI+T>nYPcZQ9Z$!xqLHlpE-{Q;@8Vq@sU+50mrRsV z&UT&pQ?hNxs%D26mgvl=g}G_saKP_s?fpeY2CU;ioC6G7 z-Bz5;fP{(uS)(6UY%fS=rcj=B_1FswJchG~nJphL-eY9NcpKx5iH%_{@P;T$Od>jS zj$C&#jm<_Dr<*nX{W*L%?AlpaSYkW|XopW*7c}QMvV50STz$%RQGAer(Iut!L9T>x z(PhV3!D8)$SvMPIOQu1wddyj%{s_s7B+iekbwM5|7jtHJF|*pfpLU#(;F-?l0NN!v z^a8&Y7tRD+{~ICj~4aET>&KEGHAmoJy9)Z3v6VW<3un^EQ3E?Nexf05 za&(JAK?>g=Ij7~!XK8C#Xk8AUvlPl9+TS{S6uH+t#!qOxjf%2dv zrDrSfF;iBsD`0;Dy@;zE->~49>%sa$tEP6dsx9 z^Mxlv`D0LiCu}+H+)sr1Liu~cK|I(EPXdJc@Z1|>VD|+gY|4i$9l{$*sWH!H%_A=~ z=Z_afP6*=!I$cu(%c{K*P5Id|euHh9M|uE$!XND5Vs-$|uq~+o@{ktJpU}9@oW{DK z(zx(LRFa~3Bs80SBH#!g57*BMYML#^rj7o21q5Pwv{A1t%7x<`@E)ksh;bY4M zu59pC_|e#@FkYqRPw_CF3g@4evb7@p9Lx^opR1`roZuchw%&u+!U#U-J_QeJfmTi9 zAP9;>M3uqeR#St}La5a#bTB*;J}BMIKV9SV`njq6nQ+u?s@;#?)Xbys-b&ITd#t?% zj;o59&oMZq8FRdfb1a6dPJlw>X>(+{ z-4uwhsRzSj;c-7{;UFV;wZptQ2tO88feMLk9@GFx$213J3VNXeeuv~KNRJsS$?h&fy7+YE^J6H-0|Zz z26<&8jqf(m-YR@jiho3xDOx4O@*(q3OMLCn(3*AewdT<3%i{5MYgZ;NYg@a2?Yfm~ z2Uo9IKNMfp+S<0RZRJV~09)LyXv4=f=|0vdK6`AL{@#H)6=zCAb`~)->OS-#eBc!rb+=Ra)|ZB8iW0Yzu^5j*f3QB-2BfF8sJ;pBpko1b?nrAYvCp|2}K*=6w?U`_g6(Q{`hNW1~ZZ)U&;P z)!H_4%=j@mW}vIfe&c(1?SiW2rPrPOr~3bn?zlhiUSJWID!;pb>S%NEus+k7N_EGR zX&T8UFw;$W@F%4oHABgTE-Bac|0Tum-4Qp~y_R=R<~8=r)A=m79oO{(MBlMr<1!2Q z8o=wQhx!4x;v4oJY;1SYPS9O|+w8x(rRO~Fo#!vpif^kqg0W8&$nS!SB@T-^fhcI@ zE#fw8AbB(0g-z^!Y#{l!NNV+>G!AYS<;9Jr2+%h?vJSzf9Hi`B{d7&gg((WXcK@xx z#vMLDU-%1x4z$MS1#V*;J&MaYg7t3|z9pM<-uTC`sZU`OJ1i|_L6;v4xXult)3u=! zJ<4Jm%(jgdHp&0Dx?8dQ@t4ZBr>=u;-dfumZ`QdA7PS^GwuQfhV2|cRmX9o{k{*82 z{>55Lo$vrv-V*03=wVCjbBd literal 14848 zcmeHOdw5*Mbw6`=_wH(Ct+Xq@EzAldv$pVBeuBYvte0&C%Qlv5UN+KdbuBMm?Opa^ ztU88uA@p-7U*iyx@Mvp7TA+l0zd{?5KVg+))0?}A zHYg8GId#jA-P%sj1<_j7MudM9aJcVm#50O}5I0eS;D+NjGuVH*4g-SEmxl&F%&Ppa z^3)`=a6JUOn;01+nqD@-41JY|CINl8m#E+6{XWlV*>%wlfL>o!E?%(q7MNa$JBpjz zs{IBgR}@~wbNO5nl=wCX#KgTAch$89!xhh2X&Z{{E0u7wtxIuNU2BLssz^b<$Nh?n zeKW{MlP)5-2}8%O>I@v=6^H#X8hYPMCWbY`Zw2B z8L-cT%GH`(DR0ka_WZ^+KMhH{DLgp`AF|W$tcOc2Q=RIA5o);+F){1U87%P5o!nY` zdMe}g`7rS5mT68SYa^=VMjz`jaP=5>^kPwH&%fS?HRFkH_UwlZFZu#1-uc`YK6(Px zd(amYy8ueT*c`x{!SKc|1XgS8<05to@E9~(CmH*=u#M3Q#xT^ab$JY$*^=u)E9^QB zlNPbj=!^Jr*MJ#|LDfd{Ac?5hJWw~-VTUwl&4|H|V8n!^l})1NR*IOp9b9q&I3OU4 zSId&)WeDU-xm1-bhR_b$jgC)fziCf#@I}nWc(Shu>7b2~XM73c7%kwYy%a>0r}qul z=?4!gL;-sl>tQ57)ZC09@wP0OHuq*2@t!s>2RpwU3N0m;#9*M+M*p*}jd>BY(Zg)z zW>i#Na3vR9Aq80yLpQg^FzH&k^G#~>hP`

tN+DeOlKc?mts4u;9djdHtTIf&S) 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 Dwtd4K diff --git a/marketplace/Empostor.Plugins.Privacy.dll b/marketplace/Empostor.Plugins.Privacy.dll index fd8927bc9571e690e1b6f309399a520188e6450e..9bb97c79471d9bdd2c4711d73e9d51204c543314 100644 GIT binary patch delta 8690 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 AX#fBK delta 7560 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 Yc_q?;-@Eyjz^!!$e9eCqJ1X`62d?T{{r~^~ diff --git a/src/Empostor.Plugins.Monitor/MonitorController.cs b/src/Empostor.Plugins.Monitor/MonitorController.cs deleted file mode 100644 index 2d31994..0000000 --- a/src/Empostor.Plugins.Monitor/MonitorController.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text.Json.Serialization; -using Empostor.Api.Games.Managers; -using Empostor.Api.Net.Manager; -using Microsoft.AspNetCore.Mvc; - -namespace Empostor.Plugins.Monitor; - -[ApiController] -public sealed class MonitorController : ControllerBase -{ - private static readonly DateTime StartTime = Process.GetCurrentProcess().StartTime.ToUniversalTime(); - - private readonly IGameManager _gameManager; - private readonly IClientManager _clientManager; - - public MonitorController(IGameManager gameManager, IClientManager clientManager) - { - _gameManager = gameManager; - _clientManager = clientManager; - } - - [HttpGet("/api/monitor/status")] - public IActionResult GetStatus() - { - var process = Process.GetCurrentProcess(); - var uptime = DateTime.UtcNow - StartTime; - - var games = _gameManager.Games.ToList(); - var clients = _clientManager.Clients.ToList(); - - var status = new MonitorStatus - { - Status = "running", - Timestamp = DateTime.UtcNow.ToString("o"), - UptimeSeconds = (long)uptime.TotalSeconds, - UptimeDisplay = $"{uptime.Days}d {uptime.Hours}h {uptime.Minutes}m", - Version = GetVersion(), - Runtime = RuntimeInformation.FrameworkDescription, - Platform = RuntimeInformation.OSDescription, - Processors = Environment.ProcessorCount, - MemoryMb = process.WorkingSet64 / 1024 / 1024, - GameCount = games.Count, - PlayerCount = clients.Count(c => c.Player != null), - ActiveConnections = clients.Count, - Games = games.Select(g => new MonitorGame - { - Code = g.Code.ToString(), - State = g.GameState.ToString(), - Map = g.Options.Map.ToString(), - PlayerCount = g.PlayerCount, - Host = g.Host?.Character?.PlayerInfo?.PlayerName ?? "(none)", - }).ToList(), - }; - - return new JsonResult(status); - } - - [HttpGet("/api/monitor/health")] - public IActionResult GetHealth() - { - return Ok(new { status = "healthy", timestamp = DateTime.UtcNow.ToString("o") }); - } - - private static string GetVersion() - { - try - { - var assembly = System.Reflection.Assembly.GetExecutingAssembly(); - var info = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); - return info.ProductVersion ?? "unknown"; - } - catch - { - return "unknown"; - } - } -} - -public sealed class MonitorStatus -{ - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; - - [JsonPropertyName("timestamp")] - public string Timestamp { get; set; } = string.Empty; - - [JsonPropertyName("uptime_seconds")] - public long UptimeSeconds { get; set; } - - [JsonPropertyName("uptime_display")] - public string UptimeDisplay { get; set; } = string.Empty; - - [JsonPropertyName("version")] - public string Version { get; set; } = string.Empty; - - [JsonPropertyName("runtime")] - public string Runtime { get; set; } = string.Empty; - - [JsonPropertyName("platform")] - public string Platform { get; set; } = string.Empty; - - [JsonPropertyName("processors")] - public int Processors { get; set; } - - [JsonPropertyName("memory_mb")] - public long MemoryMb { get; set; } - - [JsonPropertyName("game_count")] - public int GameCount { get; set; } - - [JsonPropertyName("player_count")] - public int PlayerCount { get; set; } - - [JsonPropertyName("active_connections")] - public int ActiveConnections { get; set; } - - [JsonPropertyName("games")] - public List Games { get; set; } = new(); -} - -public sealed class MonitorGame -{ - [JsonPropertyName("code")] - public string Code { get; set; } = string.Empty; - - [JsonPropertyName("state")] - public string State { get; set; } = string.Empty; - - [JsonPropertyName("map")] - public string Map { get; set; } = string.Empty; - - [JsonPropertyName("player_count")] - public int PlayerCount { get; set; } - - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; -} diff --git a/src/Empostor.Plugins.Monitor/MonitorMiddleware.cs b/src/Empostor.Plugins.Monitor/MonitorMiddleware.cs new file mode 100644 index 0000000..1e6061c --- /dev/null +++ b/src/Empostor.Plugins.Monitor/MonitorMiddleware.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Empostor.Api.Games.Managers; +using Empostor.Api.Net.Manager; +using Microsoft.AspNetCore.Http; + +namespace Empostor.Plugins.Monitor; + +public sealed class MonitorMiddleware +{ + private static readonly DateTime StartTime = Process.GetCurrentProcess().StartTime.ToUniversalTime(); + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNamingPolicy = null, + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + }; + + private readonly RequestDelegate _next; + + public MonitorMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, IGameManager gameManager, IClientManager clientManager) + { + var path = context.Request.Path; + + if (path.Equals("/api/monitor/status", StringComparison.OrdinalIgnoreCase) + && HttpMethods.IsGet(context.Request.Method)) + { + await WriteJsonAsync(context, BuildStatus(gameManager, clientManager)); + return; + } + + if (path.Equals("/api/monitor/health", StringComparison.OrdinalIgnoreCase) + && HttpMethods.IsGet(context.Request.Method)) + { + await WriteJsonAsync(context, new + { + status = "healthy", + timestamp = DateTime.UtcNow.ToString("o"), + }); + return; + } + + await _next(context); + } + + private static MonitorStatus BuildStatus(IGameManager gameManager, IClientManager clientManager) + { + var process = Process.GetCurrentProcess(); + var uptime = DateTime.UtcNow - StartTime; + var games = gameManager.Games.ToList(); + var clients = clientManager.Clients.ToList(); + + return new MonitorStatus + { + Status = "running", + Timestamp = DateTime.UtcNow.ToString("o"), + UptimeSeconds = (long)uptime.TotalSeconds, + UptimeDisplay = $"{uptime.Days}d {uptime.Hours}h {uptime.Minutes}m", + Version = GetVersion(), + Runtime = RuntimeInformation.FrameworkDescription, + Platform = RuntimeInformation.OSDescription, + Processors = Environment.ProcessorCount, + MemoryMb = process.WorkingSet64 / 1024 / 1024, + GameCount = games.Count, + PlayerCount = clients.Count(c => c.Player != null), + ActiveConnections = clients.Count, + Games = games.Select(g => new MonitorGame + { + Code = g.Code.ToString(), + State = g.GameState.ToString(), + Map = g.Options.Map.ToString(), + PlayerCount = g.PlayerCount, + Host = g.Host?.Character?.PlayerInfo?.PlayerName ?? "(none)", + }).ToList(), + }; + } + + private static async Task WriteJsonAsync(HttpContext context, object value) + { + context.Response.ContentType = "application/json; charset=utf-8"; + await context.Response.WriteAsync(JsonSerializer.Serialize(value, JsonOptions)); + } + + private static string GetVersion() + { + try + { + var assembly = typeof(MonitorMiddleware).Assembly; + var info = FileVersionInfo.GetVersionInfo(assembly.Location); + return info.ProductVersion ?? "unknown"; + } + catch + { + return "unknown"; + } + } +} diff --git a/src/Empostor.Plugins.Monitor/MonitorModels.cs b/src/Empostor.Plugins.Monitor/MonitorModels.cs new file mode 100644 index 0000000..4afdbe7 --- /dev/null +++ b/src/Empostor.Plugins.Monitor/MonitorModels.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Empostor.Plugins.Monitor; + +public sealed class MonitorStatus +{ + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; + + [JsonPropertyName("timestamp")] + public string Timestamp { get; set; } = string.Empty; + + [JsonPropertyName("uptime_seconds")] + public long UptimeSeconds { get; set; } + + [JsonPropertyName("uptime_display")] + public string UptimeDisplay { get; set; } = string.Empty; + + [JsonPropertyName("version")] + public string Version { get; set; } = string.Empty; + + [JsonPropertyName("runtime")] + public string Runtime { get; set; } = string.Empty; + + [JsonPropertyName("platform")] + public string Platform { get; set; } = string.Empty; + + [JsonPropertyName("processors")] + public int Processors { get; set; } + + [JsonPropertyName("memory_mb")] + public long MemoryMb { get; set; } + + [JsonPropertyName("game_count")] + public int GameCount { get; set; } + + [JsonPropertyName("player_count")] + public int PlayerCount { get; set; } + + [JsonPropertyName("active_connections")] + public int ActiveConnections { get; set; } + + [JsonPropertyName("games")] + public List Games { get; set; } = new(); +} + +public sealed class MonitorGame +{ + [JsonPropertyName("code")] + public string Code { get; set; } = string.Empty; + + [JsonPropertyName("state")] + public string State { get; set; } = string.Empty; + + [JsonPropertyName("map")] + public string Map { get; set; } = string.Empty; + + [JsonPropertyName("player_count")] + public int PlayerCount { get; set; } + + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; +} diff --git a/src/Empostor.Plugins.Monitor/MonitorPluginStartup.cs b/src/Empostor.Plugins.Monitor/MonitorPluginStartup.cs index 0a7d90e..cc9cf45 100644 --- a/src/Empostor.Plugins.Monitor/MonitorPluginStartup.cs +++ b/src/Empostor.Plugins.Monitor/MonitorPluginStartup.cs @@ -1,6 +1,5 @@ using Empostor.Api.Plugins; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -12,11 +11,10 @@ public sealed class MonitorPluginStartup : IPluginStartup, IPluginHttpStartup public void ConfigureServices(IServiceCollection services) { - services.AddControllers() - .AddApplicationPart(typeof(MonitorPluginStartup).Assembly); } public void ConfigureWebApplication(IApplicationBuilder app) { + app.UseMiddleware(); } } diff --git a/src/Empostor.Plugins.Privacy/PrivacyController.cs b/src/Empostor.Plugins.Privacy/PrivacyController.cs deleted file mode 100644 index 33c5f48..0000000 --- a/src/Empostor.Plugins.Privacy/PrivacyController.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.IO; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace Empostor.Plugins.Privacy; - -[ApiController] -[Route("/privacy")] -public sealed class PrivacyController : ControllerBase -{ - private readonly PrivacyStore _store; - private readonly ILogger _logger; - - public PrivacyController(PrivacyStore store, ILogger logger) - { - _store = store; - _logger = logger; - } - - [HttpGet] - public IActionResult GetPrivacy() - { - var html = _store.GetContent(); - return Content(html, "text/html; charset=utf-8"); - } - - [HttpPost] - [Route("/admin/api/privacy")] - public async Task UpdatePrivacy() - { - using var reader = new StreamReader(Request.Body); - var body = await reader.ReadToEndAsync(); - - try - { - var doc = JsonDocument.Parse(body); - var content = doc.RootElement.GetProperty("content").GetString() ?? string.Empty; - var token = doc.RootElement.GetProperty("token").GetString() ?? string.Empty; - - var adminToken = Environment.GetEnvironmentVariable("EMP_HTTP_TOKEN") - ?? Environment.GetEnvironmentVariable("EMP_ADMIN_TOKEN") - ?? "empostor"; - - if (token != adminToken) - { - return Unauthorized(new { error = "Invalid token." }); - } - - _store.SaveContent(content); - return Ok(new { success = true }); - } - catch (Exception ex) - { - return BadRequest(new { error = ex.Message }); - } - } -} diff --git a/src/Empostor.Plugins.Privacy/PrivacyMiddleware.cs b/src/Empostor.Plugins.Privacy/PrivacyMiddleware.cs new file mode 100644 index 0000000..1758b7f --- /dev/null +++ b/src/Empostor.Plugins.Privacy/PrivacyMiddleware.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Empostor.Plugins.Privacy; + +public sealed class PrivacyMiddleware +{ + private readonly RequestDelegate _next; + + public PrivacyMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, PrivacyStore store) + { + var path = context.Request.Path; + + if (path.Equals("/privacy", StringComparison.OrdinalIgnoreCase) + && HttpMethods.IsGet(context.Request.Method)) + { + context.Response.ContentType = "text/html; charset=utf-8"; + await context.Response.WriteAsync(store.GetContent()); + return; + } + + if (path.Equals("/admin/api/privacy", StringComparison.OrdinalIgnoreCase) + && HttpMethods.IsPost(context.Request.Method)) + { + await HandleUpdateAsync(context, store); + return; + } + + await _next(context); + } + + private static async Task HandleUpdateAsync(HttpContext context, PrivacyStore store) + { + using var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + + try + { + var doc = JsonDocument.Parse(body); + var content = doc.RootElement.GetProperty("content").GetString() ?? string.Empty; + var token = doc.RootElement.GetProperty("token").GetString() ?? string.Empty; + + var adminToken = Environment.GetEnvironmentVariable("EMP_HTTP_TOKEN") + ?? Environment.GetEnvironmentVariable("EMP_ADMIN_TOKEN") + ?? "empostor"; + + if (token != adminToken) + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("{\"error\":\"Invalid token.\"}"); + return; + } + + store.SaveContent(content); + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("{\"success\":true}"); + } + catch (Exception ex) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + context.Response.ContentType = "application/json"; + var error = JsonSerializer.Serialize(new { error = ex.Message }); + await context.Response.WriteAsync(error); + } + } +} diff --git a/src/Empostor.Plugins.Privacy/PrivacyPluginStartup.cs b/src/Empostor.Plugins.Privacy/PrivacyPluginStartup.cs index 821259b..6042f20 100644 --- a/src/Empostor.Plugins.Privacy/PrivacyPluginStartup.cs +++ b/src/Empostor.Plugins.Privacy/PrivacyPluginStartup.cs @@ -14,11 +14,10 @@ public sealed class PrivacyPluginStartup : IPluginStartup, IPluginHttpStartup { services.AddSingleton(); services.AddSingleton(); - services.AddControllers() - .AddApplicationPart(typeof(PrivacyPluginStartup).Assembly); } public void ConfigureWebApplication(IApplicationBuilder app) { + app.UseMiddleware(); } } diff --git a/src/Empostor.Server/Plugins/PluginLoader.cs b/src/Empostor.Server/Plugins/PluginLoader.cs index aed827f..7f7d975 100644 --- a/src/Empostor.Server/Plugins/PluginLoader.cs +++ b/src/Empostor.Server/Plugins/PluginLoader.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Loader; using Empostor.Api.Plugins; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.Hosting; @@ -16,6 +17,8 @@ namespace Empostor.Server.Plugins { private static readonly ILogger Logger = Log.ForContext(typeof(PluginLoader)); + internal static IReadOnlyList LoadedPluginAssemblies { get; private set; } = Array.Empty(); + public static IHostBuilder UsePluginLoader(this IHostBuilder builder, PluginConfig config) { var assemblyInfos = new List(); @@ -105,6 +108,10 @@ namespace Empostor.Server.Plugins } var orderedPlugins = LoadOrderPlugins(plugins); + LoadedPluginAssemblies = orderedPlugins + .Select(p => p.PluginType.Assembly) + .Distinct() + .ToList(); foreach (var plugin in orderedPlugins) { @@ -120,11 +127,50 @@ namespace Empostor.Server.Plugins { plugin.Startup?.ConfigureServices(services); } + + EnsureMvcApplicationParts(services, orderedPlugins); }); return builder; } + private static void EnsureMvcApplicationParts(IServiceCollection services, IReadOnlyList plugins) + { + var manager = services + .LastOrDefault(d => d.ServiceType == typeof(ApplicationPartManager)) + ?.ImplementationInstance as ApplicationPartManager; + if (manager == null) + { + return; + } + + EnsureAssemblyPart(manager, typeof(PluginLoader).Assembly, insertFirst: true); + foreach (var plugin in plugins) + { + EnsureAssemblyPart(manager, plugin.PluginType.Assembly, insertFirst: false); + } + } + + private static void EnsureAssemblyPart(ApplicationPartManager manager, Assembly assembly, bool insertFirst) + { + if (manager.ApplicationParts.OfType().Any(p => p.Assembly == assembly)) + { + return; + } + + var part = new AssemblyPart(assembly); + if (insertFirst) + { + manager.ApplicationParts.Insert(0, part); + } + else + { + manager.ApplicationParts.Add(part); + } + + Logger.Information("Registered MVC controllers from {Assembly}.", assembly.GetName().Name); + } + private static void CheckPaths(IEnumerable paths) { foreach (var path in paths) diff --git a/src/Empostor.Server/Program.cs b/src/Empostor.Server/Program.cs index 323de58..fa47f76 100644 --- a/src/Empostor.Server/Program.cs +++ b/src/Empostor.Server/Program.cs @@ -33,6 +33,7 @@ using Empostor.Server.Service.Firewall; using Empostor.Server.Utils; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -269,7 +270,8 @@ namespace Empostor.Server { builder.ConfigureServices(s => { - s.AddControllers(); + s.AddControllers() + .ConfigureApplicationPartManager(EnsureServerApplicationPart); }); builder.Configure(app => { @@ -289,5 +291,32 @@ namespace Empostor.Server return hostBuilder; } + + private static void EnsureServerApplicationPart(ApplicationPartManager manager) + { + EnsureAssemblyPart(manager, typeof(Program).Assembly, insertFirst: true); + foreach (var assembly in PluginLoader.LoadedPluginAssemblies) + { + EnsureAssemblyPart(manager, assembly, insertFirst: false); + } + } + + private static void EnsureAssemblyPart(ApplicationPartManager manager, Assembly assembly, bool insertFirst) + { + if (manager.ApplicationParts.OfType().Any(p => p.Assembly == assembly)) + { + return; + } + + var part = new AssemblyPart(assembly); + if (insertFirst) + { + manager.ApplicationParts.Insert(0, part); + } + else + { + manager.ApplicationParts.Add(part); + } + } } }