dbinbound.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import dayjs, { type Dayjs } from 'dayjs';
  2. import { ObjectUtil, NumberFormatter, SizeFormatter } from '@/utils';
  3. import { Protocols } from '@/schemas/primitives';
  4. export type RawJsonField = string | Record<string, unknown> | unknown[];
  5. export interface ClientStats {
  6. email: string;
  7. up: number;
  8. down: number;
  9. total: number;
  10. expiryTime: number;
  11. enable?: boolean;
  12. inboundId?: number;
  13. reset?: number;
  14. }
  15. export interface FallbackParentRef {
  16. masterId: number;
  17. path: string;
  18. }
  19. export type DBInboundInit = Partial<{
  20. id: number;
  21. userId: number;
  22. up: number;
  23. down: number;
  24. total: number;
  25. remark: string;
  26. enable: boolean;
  27. expiryTime: number;
  28. trafficReset: string;
  29. trafficResetDay: number;
  30. lastTrafficResetTime: number;
  31. listen: string;
  32. port: number;
  33. protocol: string;
  34. settings: RawJsonField;
  35. streamSettings: RawJsonField;
  36. tag: string;
  37. sniffing: RawJsonField;
  38. clientStats: ClientStats[];
  39. nodeId: number | null;
  40. shareAddrStrategy: string;
  41. shareAddr: string;
  42. subSortIndex: number;
  43. originNodeGuid: string;
  44. fallbackParent: FallbackParentRef | null;
  45. }>;
  46. export function coerceInboundJsonField(value: unknown): Record<string, unknown> {
  47. if (value == null) return {};
  48. if (typeof value === 'object' && !Array.isArray(value)) {
  49. return value as Record<string, unknown>;
  50. }
  51. if (typeof value !== 'string') return {};
  52. const trimmed = value.trim();
  53. if (trimmed === '') return {};
  54. try {
  55. const parsed = JSON.parse(trimmed);
  56. if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
  57. return parsed as Record<string, unknown>;
  58. }
  59. return {};
  60. } catch {
  61. return {};
  62. }
  63. }
  64. export class DBInbound {
  65. id: number;
  66. userId: number;
  67. up: number;
  68. down: number;
  69. total: number;
  70. remark: string;
  71. enable: boolean;
  72. expiryTime: number;
  73. trafficReset: string;
  74. trafficResetDay: number;
  75. lastTrafficResetTime: number;
  76. listen: string;
  77. port: number;
  78. protocol: string;
  79. settings: RawJsonField;
  80. streamSettings: RawJsonField;
  81. tag: string;
  82. sniffing: RawJsonField;
  83. clientStats: ClientStats[];
  84. nodeId: number | null;
  85. shareAddrStrategy: string;
  86. shareAddr: string;
  87. subSortIndex: number;
  88. originNodeGuid: string;
  89. fallbackParent: FallbackParentRef | null;
  90. private _clientStatsMap: Map<string, ClientStats> | null = null;
  91. constructor(data?: DBInboundInit) {
  92. this.id = 0;
  93. this.userId = 0;
  94. this.up = 0;
  95. this.down = 0;
  96. this.total = 0;
  97. this.remark = "";
  98. this.enable = true;
  99. this.expiryTime = 0;
  100. this.trafficReset = "never";
  101. this.trafficResetDay = 1;
  102. this.lastTrafficResetTime = 0;
  103. this.listen = "";
  104. this.port = 0;
  105. this.protocol = "";
  106. this.settings = "";
  107. this.streamSettings = "";
  108. this.tag = "";
  109. this.sniffing = "";
  110. this.clientStats = [];
  111. this.nodeId = null;
  112. this.shareAddrStrategy = "node";
  113. this.shareAddr = "";
  114. this.subSortIndex = 1;
  115. this.originNodeGuid = "";
  116. this.fallbackParent = null;
  117. if (data == null) {
  118. return;
  119. }
  120. ObjectUtil.cloneProps(this, data);
  121. }
  122. get totalGB(): number {
  123. return NumberFormatter.toFixed(this.total / SizeFormatter.ONE_GB, 2);
  124. }
  125. set totalGB(gb: number) {
  126. this.total = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
  127. }
  128. get isVMess() {
  129. return this.protocol === Protocols.VMESS;
  130. }
  131. get isVLess() {
  132. return this.protocol === Protocols.VLESS;
  133. }
  134. get isTrojan() {
  135. return this.protocol === Protocols.TROJAN;
  136. }
  137. get isSS() {
  138. return this.protocol === Protocols.SHADOWSOCKS;
  139. }
  140. get isMixed() {
  141. return this.protocol === Protocols.MIXED;
  142. }
  143. get isHTTP() {
  144. return this.protocol === Protocols.HTTP;
  145. }
  146. get isWireguard() {
  147. return this.protocol === Protocols.WIREGUARD;
  148. }
  149. get isHysteria() {
  150. return this.protocol === Protocols.HYSTERIA;
  151. }
  152. get isTunnel() {
  153. return this.protocol === Protocols.TUNNEL;
  154. }
  155. get address(): string {
  156. let address = location.hostname;
  157. if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
  158. address = this.listen;
  159. }
  160. return address;
  161. }
  162. get _expiryTime(): Dayjs | null {
  163. if (this.expiryTime === 0) {
  164. return null;
  165. }
  166. return dayjs(this.expiryTime);
  167. }
  168. set _expiryTime(t: Dayjs | null | undefined) {
  169. if (t == null) {
  170. this.expiryTime = 0;
  171. } else {
  172. this.expiryTime = t.valueOf();
  173. }
  174. }
  175. get isExpiry(): boolean {
  176. return this.expiryTime < new Date().getTime();
  177. }
  178. invalidateCache(): void {
  179. this._clientStatsMap = null;
  180. }
  181. toJSON(): Record<string, unknown> {
  182. const out: Record<string, unknown> = { ...(this as unknown as Record<string, unknown>) };
  183. delete out._clientStatsMap;
  184. return out;
  185. }
  186. getClientStats(email: string): ClientStats | undefined {
  187. if (!this._clientStatsMap) {
  188. this._clientStatsMap = new Map();
  189. if (Array.isArray(this.clientStats)) {
  190. for (const stats of this.clientStats) {
  191. if (stats && stats.email) {
  192. this._clientStatsMap.set(stats.email, stats);
  193. }
  194. }
  195. }
  196. }
  197. return this._clientStatsMap.get(email);
  198. }
  199. }