dbinbound.ts 5.7 KB

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