dbinbound.ts 5.3 KB

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