guardcoreapi 0.3.0.dev20251113215748__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ from .manager import GuardCoreApi
2
+
3
+ __all__ = ["GuardCoreApi"]
@@ -0,0 +1,17 @@
1
+ from .request import RequestCore
2
+ from .exceptions import (
3
+ GuardCoreApiException,
4
+ RequestAuthenticationError,
5
+ RequestConnectionError,
6
+ RequestResponseError,
7
+ RequestTimeoutError,
8
+ )
9
+
10
+ __all__ = [
11
+ "RequestCore",
12
+ "GuardCoreApiException",
13
+ "RequestAuthenticationError",
14
+ "RequestConnectionError",
15
+ "RequestResponseError",
16
+ "RequestTimeoutError",
17
+ ]
@@ -0,0 +1,28 @@
1
+ class GuardCoreApiException(Exception):
2
+ """Base class for request errors."""
3
+
4
+ pass
5
+
6
+
7
+ class RequestTimeoutError(GuardCoreApiException):
8
+ """Exception raised for request timeouts."""
9
+
10
+ pass
11
+
12
+
13
+ class RequestConnectionError(GuardCoreApiException):
14
+ """Exception raised for connection errors."""
15
+
16
+ pass
17
+
18
+
19
+ class RequestResponseError(GuardCoreApiException):
20
+ """Exception raised for invalid responses."""
21
+
22
+ pass
23
+
24
+
25
+ class RequestAuthenticationError(GuardCoreApiException):
26
+ """Exception raised for authentication failures."""
27
+
28
+ pass
@@ -0,0 +1,145 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ import aiohttp
5
+ from pydantic import BaseModel
6
+ from .exceptions import (
7
+ RequestAuthenticationError,
8
+ RequestConnectionError,
9
+ RequestResponseError,
10
+ RequestTimeoutError,
11
+ )
12
+
13
+
14
+ class RequestCore:
15
+ _BASE_URL = "https://core.erfjab.com"
16
+
17
+ @staticmethod
18
+ def generate_headers(
19
+ api_key: Optional[str] = None, access_token: Optional[str] = None
20
+ ) -> dict:
21
+ headers = {
22
+ "Accept": "application/json",
23
+ "Content-Type": "application/json",
24
+ }
25
+
26
+ if api_key:
27
+ headers["X-API-Key"] = api_key
28
+ if access_token:
29
+ headers["Authorization"] = f"Bearer {access_token}"
30
+ return headers
31
+
32
+ @staticmethod
33
+ async def fetch(
34
+ endpoint: str,
35
+ method: str = "GET",
36
+ headers: Optional[dict] = None,
37
+ params: Optional[dict] = None,
38
+ data: Optional[dict] = None,
39
+ json: Optional[dict] = None,
40
+ response_model: Optional[BaseModel] = None,
41
+ use_list: bool = False,
42
+ timeout: float = 10.0,
43
+ ) -> dict:
44
+ try:
45
+ async with aiohttp.ClientSession(
46
+ timeout=aiohttp.ClientTimeout(total=timeout)
47
+ ) as session:
48
+ async with session.request(
49
+ method=method,
50
+ url=RequestCore._BASE_URL + endpoint,
51
+ headers=headers,
52
+ params=params,
53
+ data=data,
54
+ json=json,
55
+ ) as response:
56
+ response.raise_for_status()
57
+ resp_json = await response.json()
58
+ if response_model:
59
+ if use_list:
60
+ return [response_model(**item) for item in resp_json]
61
+ return response_model(**resp_json)
62
+ return resp_json
63
+ except aiohttp.ClientResponseError as e:
64
+ if e.status == 401:
65
+ raise RequestAuthenticationError("Authentication failed") from e
66
+ else:
67
+ raise RequestResponseError(f"Invalid response: {e.status}") from e
68
+ except aiohttp.ClientConnectionError as e:
69
+ raise RequestConnectionError("Connection error occurred") from e
70
+ except asyncio.TimeoutError as e:
71
+ raise RequestTimeoutError("Request timed out") from e
72
+
73
+ @staticmethod
74
+ async def get(
75
+ endpoint: str,
76
+ headers: Optional[dict] = None,
77
+ params: Optional[dict] = None,
78
+ response_model: Optional[BaseModel] = None,
79
+ use_list: bool = False,
80
+ timeout: float = 10.0,
81
+ ) -> dict:
82
+ return await RequestCore.fetch(
83
+ endpoint=endpoint,
84
+ method="GET",
85
+ headers=headers,
86
+ params=params,
87
+ timeout=timeout,
88
+ use_list=use_list,
89
+ response_model=response_model,
90
+ )
91
+
92
+ @staticmethod
93
+ async def post(
94
+ endpoint: str,
95
+ headers: Optional[dict] = None,
96
+ data: Optional[dict] = None,
97
+ json: Optional[dict] = None,
98
+ timeout: float = 10.0,
99
+ response_model: Optional[BaseModel] = None,
100
+ use_list: bool = False,
101
+ ) -> dict:
102
+ return await RequestCore.fetch(
103
+ endpoint=endpoint,
104
+ method="POST",
105
+ headers=headers,
106
+ data=data,
107
+ json=json,
108
+ timeout=timeout,
109
+ response_model=response_model,
110
+ use_list=use_list,
111
+ )
112
+
113
+ @staticmethod
114
+ async def put(
115
+ endpoint: str,
116
+ headers: Optional[dict] = None,
117
+ data: Optional[dict] = None,
118
+ json: Optional[dict] = None,
119
+ timeout: float = 10.0,
120
+ response_model: Optional[BaseModel] = None,
121
+ use_list: bool = False,
122
+ ) -> dict:
123
+ return await RequestCore.fetch(
124
+ endpoint=endpoint,
125
+ method="PUT",
126
+ headers=headers,
127
+ data=data,
128
+ json=json,
129
+ timeout=timeout,
130
+ response_model=response_model,
131
+ use_list=use_list,
132
+ )
133
+
134
+ @staticmethod
135
+ async def delete(
136
+ endpoint: str,
137
+ headers: Optional[dict] = None,
138
+ timeout: float = 10.0,
139
+ ) -> dict:
140
+ return await RequestCore.fetch(
141
+ endpoint=endpoint,
142
+ method="DELETE",
143
+ headers=headers,
144
+ timeout=timeout,
145
+ )
@@ -0,0 +1,585 @@
1
+ from .core import RequestCore
2
+ from .types import (
3
+ AdminToken,
4
+ AdminResponse,
5
+ AdminCreate,
6
+ AdminCurrentUpdate,
7
+ AdminUsageLogsResponse,
8
+ AdminUpdate,
9
+ SubscriptionResponse,
10
+ SubscriptionCreate,
11
+ SubscriptionStatsResponse,
12
+ SubscriptionUpdate,
13
+ SubscriptionUsageLogsResponse,
14
+ NodeResponse,
15
+ NodeCreate,
16
+ NodeUpdate,
17
+ NodeStatsResponse,
18
+ ServiceCreate,
19
+ ServiceResponse,
20
+ ServiceUpdate,
21
+ StatsResponse,
22
+ AdminStatsResponseNew,
23
+ )
24
+
25
+
26
+ class GuardCoreApi:
27
+ @staticmethod
28
+ async def get_all_admin(
29
+ api_key: str | None = None, access_token: str | None = None
30
+ ) -> list[AdminResponse]:
31
+ return await RequestCore.get(
32
+ "/api/admins",
33
+ headers=RequestCore.generate_headers(api_key, access_token),
34
+ response_model=AdminResponse,
35
+ use_list=True,
36
+ )
37
+
38
+ @staticmethod
39
+ async def create_admin(
40
+ data: AdminCreate, api_key: str | None = None, access_token: str | None = None
41
+ ) -> AdminResponse:
42
+ return await RequestCore.post(
43
+ "/api/admins",
44
+ headers=RequestCore.generate_headers(api_key, access_token),
45
+ json=data.dict(),
46
+ response_model=AdminResponse,
47
+ )
48
+
49
+ @staticmethod
50
+ async def generate_admin_token(username: str, password: str) -> AdminToken:
51
+ return await RequestCore.post(
52
+ "/api/admins/token",
53
+ data={
54
+ "username": username,
55
+ "password": password,
56
+ },
57
+ response_model=AdminToken,
58
+ )
59
+
60
+ @staticmethod
61
+ async def get_current_admin(
62
+ api_key: str | None = None, access_token: str | None = None
63
+ ) -> AdminResponse:
64
+ return await RequestCore.get(
65
+ "/api/admins/current",
66
+ headers=RequestCore.generate_headers(api_key, access_token),
67
+ response_model=AdminResponse,
68
+ )
69
+
70
+ @staticmethod
71
+ async def update_current_admin(
72
+ data: AdminCurrentUpdate,
73
+ api_key: str | None = None,
74
+ access_token: str | None = None,
75
+ ) -> AdminResponse:
76
+ return await RequestCore.put(
77
+ "/api/admins/current",
78
+ headers=RequestCore.generate_headers(api_key, access_token),
79
+ json=data.dict(),
80
+ response_model=AdminResponse,
81
+ )
82
+
83
+ @staticmethod
84
+ async def get_current_admin_usages(
85
+ api_key: str | None = None, access_token: str | None = None
86
+ ) -> dict:
87
+ return await RequestCore.get(
88
+ "/api/admins/current/usages",
89
+ headers=RequestCore.generate_headers(api_key, access_token),
90
+ response_model=AdminUsageLogsResponse,
91
+ )
92
+
93
+ @staticmethod
94
+ async def get_admin(
95
+ username: str, api_key: str | None = None, access_token: str | None = None
96
+ ) -> AdminResponse:
97
+ return await RequestCore.get(
98
+ f"/api/admins/{username}",
99
+ headers=RequestCore.generate_headers(api_key, access_token),
100
+ response_model=AdminResponse,
101
+ )
102
+
103
+ @staticmethod
104
+ async def update_admin(
105
+ username: str,
106
+ data: AdminUpdate,
107
+ api_key: str | None = None,
108
+ access_token: str | None = None,
109
+ ) -> AdminResponse:
110
+ return await RequestCore.put(
111
+ f"/api/admins/{username}",
112
+ headers=RequestCore.generate_headers(api_key, access_token),
113
+ json=data.dict(),
114
+ response_model=AdminResponse,
115
+ )
116
+
117
+ @staticmethod
118
+ async def delete_admin(
119
+ username: str, api_key: str | None = None, access_token: str | None = None
120
+ ) -> dict:
121
+ return await RequestCore.delete(
122
+ f"/api/admins/{username}",
123
+ headers=RequestCore.generate_headers(api_key, access_token),
124
+ )
125
+
126
+ @staticmethod
127
+ async def get_admin_usages(
128
+ username: str, api_key: str | None = None, access_token: str | None = None
129
+ ) -> dict:
130
+ return await RequestCore.get(
131
+ f"/api/admins/{username}/usages",
132
+ headers=RequestCore.generate_headers(api_key, access_token),
133
+ response_model=AdminUsageLogsResponse,
134
+ )
135
+
136
+ @staticmethod
137
+ async def enable_admin(
138
+ username: str, api_key: str | None = None, access_token: str | None = None
139
+ ) -> AdminResponse:
140
+ return await RequestCore.post(
141
+ f"/api/admins/{username}/enable",
142
+ headers=RequestCore.generate_headers(api_key, access_token),
143
+ response_model=AdminResponse,
144
+ )
145
+
146
+ @staticmethod
147
+ async def disable_admin(
148
+ username: str, api_key: str | None = None, access_token: str | None = None
149
+ ) -> AdminResponse:
150
+ return await RequestCore.post(
151
+ f"/api/admins/{username}/disable",
152
+ headers=RequestCore.generate_headers(api_key, access_token),
153
+ response_model=AdminResponse,
154
+ )
155
+
156
+ @staticmethod
157
+ async def revoke_admin_api_key(
158
+ username: str, api_key: str | None = None, access_token: str | None = None
159
+ ) -> AdminResponse:
160
+ return await RequestCore.post(
161
+ f"/api/admins/{username}/revoke",
162
+ headers=RequestCore.generate_headers(api_key, access_token),
163
+ response_model=AdminResponse,
164
+ )
165
+
166
+ @staticmethod
167
+ async def get_admin_subscriptions(
168
+ username: str, api_key: str | None = None, access_token: str | None = None
169
+ ) -> list[SubscriptionResponse]:
170
+ return await RequestCore.get(
171
+ f"/api/admins/{username}/subscriptions",
172
+ headers=RequestCore.generate_headers(api_key, access_token),
173
+ response_model=SubscriptionResponse,
174
+ use_list=True,
175
+ )
176
+
177
+ @staticmethod
178
+ async def revoke_admin(
179
+ username: str, api_key: str | None = None, access_token: str | None = None
180
+ ) -> dict:
181
+ return await RequestCore.post(
182
+ f"/api/admins/{username}/revoke",
183
+ headers=RequestCore.generate_headers(api_key, access_token),
184
+ )
185
+
186
+ @staticmethod
187
+ async def delete_admin_subscriptions(
188
+ username: str, api_key: str | None = None, access_token: str | None = None
189
+ ) -> dict:
190
+ return await RequestCore.delete(
191
+ f"/api/admins/{username}/subscriptions",
192
+ headers=RequestCore.generate_headers(api_key, access_token),
193
+ )
194
+
195
+ @staticmethod
196
+ async def activate_admin_subscriptions(
197
+ username: str, api_key: str | None = None, access_token: str | None = None
198
+ ) -> dict:
199
+ return await RequestCore.post(
200
+ f"/api/admins/{username}/subscriptions/activate",
201
+ headers=RequestCore.generate_headers(api_key, access_token),
202
+ )
203
+
204
+ @staticmethod
205
+ async def deactivate_admin_subscriptions(
206
+ username: str, api_key: str | None = None, access_token: str | None = None
207
+ ) -> dict:
208
+ return await RequestCore.post(
209
+ f"/api/admins/{username}/subscriptions/deactivate",
210
+ headers=RequestCore.generate_headers(api_key, access_token),
211
+ )
212
+
213
+ @staticmethod
214
+ async def get_all_subscriptions(
215
+ api_key: str | None = None,
216
+ access_token: str | None = None,
217
+ limited: bool | None = None,
218
+ expired: bool | None = None,
219
+ is_active: bool | None = None,
220
+ enabled: bool | None = None,
221
+ search: str | None = None,
222
+ online: bool | None = None,
223
+ order_by: str | None = None,
224
+ page: int | None = 1,
225
+ size: int | None = 10,
226
+ ) -> list[SubscriptionResponse]:
227
+ params = {}
228
+ if limited is not None:
229
+ params["limited"] = limited
230
+ if expired is not None:
231
+ params["expired"] = expired
232
+ if is_active is not None:
233
+ params["is_active"] = is_active
234
+ if enabled is not None:
235
+ params["enabled"] = enabled
236
+ if search is not None:
237
+ params["search"] = search
238
+ if online is not None:
239
+ params["online"] = online
240
+ if order_by is not None:
241
+ params["order_by"] = order_by
242
+ if page is not None:
243
+ params["page"] = page
244
+ if size is not None:
245
+ params["size"] = size
246
+
247
+ return await RequestCore.get(
248
+ "/api/subscriptions",
249
+ headers=RequestCore.generate_headers(api_key, access_token),
250
+ params=params,
251
+ response_model=SubscriptionResponse,
252
+ use_list=True,
253
+ )
254
+
255
+ @staticmethod
256
+ async def create_subscription(
257
+ data: list[SubscriptionCreate],
258
+ api_key: str | None = None,
259
+ access_token: str | None = None,
260
+ ) -> SubscriptionResponse:
261
+ return await RequestCore.post(
262
+ "/api/subscriptions",
263
+ headers=RequestCore.generate_headers(api_key, access_token),
264
+ json=[item.dict() for item in data],
265
+ response_model=SubscriptionResponse,
266
+ use_list=True,
267
+ )
268
+
269
+ @staticmethod
270
+ async def get_subscription_count(
271
+ api_key: str | None = None,
272
+ access_token: str | None = None,
273
+ limited: bool | None = None,
274
+ expired: bool | None = None,
275
+ is_active: bool | None = None,
276
+ enabled: bool | None = None,
277
+ online: bool | None = None,
278
+ ) -> int:
279
+ params = {}
280
+ if limited is not None:
281
+ params["limited"] = limited
282
+ if expired is not None:
283
+ params["expired"] = expired
284
+ if is_active is not None:
285
+ params["is_active"] = is_active
286
+ if enabled is not None:
287
+ params["enabled"] = enabled
288
+ if online is not None:
289
+ params["online"] = online
290
+
291
+ return await RequestCore.get(
292
+ "/api/subscriptions/count",
293
+ headers=RequestCore.generate_headers(api_key, access_token),
294
+ params=params,
295
+ )
296
+
297
+ @staticmethod
298
+ async def get_subscription_stats(
299
+ api_key: str | None = None, access_token: str | None = None
300
+ ) -> SubscriptionStatsResponse:
301
+ return await RequestCore.get(
302
+ "/api/subscriptions/stats",
303
+ headers=RequestCore.generate_headers(api_key, access_token),
304
+ response_model=SubscriptionStatsResponse,
305
+ )
306
+
307
+ @staticmethod
308
+ async def get_subscription(
309
+ username: str, api_key: str | None = None, access_token: str | None = None
310
+ ) -> SubscriptionResponse:
311
+ return await RequestCore.get(
312
+ f"/api/subscriptions/{username}",
313
+ headers=RequestCore.generate_headers(api_key, access_token),
314
+ response_model=SubscriptionResponse,
315
+ )
316
+
317
+ @staticmethod
318
+ async def update_subscription(
319
+ username: str,
320
+ data: SubscriptionUpdate,
321
+ api_key: str | None = None,
322
+ access_token: str | None = None,
323
+ ) -> SubscriptionResponse:
324
+ return await RequestCore.put(
325
+ f"/api/subscriptions/{username}",
326
+ headers=RequestCore.generate_headers(api_key, access_token),
327
+ json=data.dict(),
328
+ response_model=SubscriptionResponse,
329
+ )
330
+
331
+ @staticmethod
332
+ async def delete_subscription(
333
+ username: str, api_key: str | None = None, access_token: str | None = None
334
+ ) -> dict:
335
+ return await RequestCore.delete(
336
+ f"/api/subscriptions/{username}",
337
+ headers=RequestCore.generate_headers(api_key, access_token),
338
+ )
339
+
340
+ @staticmethod
341
+ async def get_subscription_usages(
342
+ username: str, api_key: str | None = None, access_token: str | None = None
343
+ ) -> SubscriptionUsageLogsResponse:
344
+ return await RequestCore.get(
345
+ f"/api/subscriptions/{username}/usages",
346
+ headers=RequestCore.generate_headers(api_key, access_token),
347
+ response_model=SubscriptionUsageLogsResponse,
348
+ )
349
+
350
+ @staticmethod
351
+ async def enable_subscription(
352
+ username: str, api_key: str | None = None, access_token: str | None = None
353
+ ) -> SubscriptionResponse:
354
+ return await RequestCore.post(
355
+ f"/api/subscriptions/{username}/enable",
356
+ headers=RequestCore.generate_headers(api_key, access_token),
357
+ response_model=SubscriptionResponse,
358
+ )
359
+
360
+ @staticmethod
361
+ async def disable_subscription(
362
+ username: str, api_key: str | None = None, access_token: str | None = None
363
+ ) -> SubscriptionResponse:
364
+ return await RequestCore.post(
365
+ f"/api/subscriptions/{username}/disable",
366
+ headers=RequestCore.generate_headers(api_key, access_token),
367
+ response_model=SubscriptionResponse,
368
+ )
369
+
370
+ @staticmethod
371
+ async def revoke_subscription(
372
+ username: str, api_key: str | None = None, access_token: str | None = None
373
+ ) -> SubscriptionResponse:
374
+ return await RequestCore.post(
375
+ f"/api/subscriptions/{username}/revoke",
376
+ headers=RequestCore.generate_headers(api_key, access_token),
377
+ response_model=SubscriptionResponse,
378
+ )
379
+
380
+ @staticmethod
381
+ async def reset_subscription(
382
+ username: str, api_key: str | None = None, access_token: str | None = None
383
+ ) -> SubscriptionResponse:
384
+ return await RequestCore.post(
385
+ f"/api/subscriptions/{username}/reset",
386
+ headers=RequestCore.generate_headers(api_key, access_token),
387
+ response_model=SubscriptionResponse,
388
+ )
389
+
390
+ @staticmethod
391
+ async def bulk_add_service(
392
+ service_id: int, api_key: str | None = None, access_token: str | None = None
393
+ ) -> dict:
394
+ return await RequestCore.post(
395
+ f"/api/subscriptions/services/{service_id}",
396
+ headers=RequestCore.generate_headers(api_key, access_token),
397
+ )
398
+
399
+ @staticmethod
400
+ async def bulk_remove_service(
401
+ service_id: int, api_key: str | None = None, access_token: str | None = None
402
+ ) -> dict:
403
+ return await RequestCore.delete(
404
+ f"/api/subscriptions/services/{service_id}",
405
+ headers=RequestCore.generate_headers(api_key, access_token),
406
+ )
407
+
408
+ @staticmethod
409
+ async def get_nodes(
410
+ api_key: str | None = None, access_token: str | None = None
411
+ ) -> list[NodeResponse]:
412
+ return await RequestCore.get(
413
+ "/api/nodes",
414
+ headers=RequestCore.generate_headers(api_key, access_token),
415
+ response_model=NodeResponse,
416
+ use_list=True,
417
+ )
418
+
419
+ @staticmethod
420
+ async def create_node(
421
+ data: NodeCreate, api_key: str | None = None, access_token: str | None = None
422
+ ) -> NodeResponse:
423
+ return await RequestCore.post(
424
+ "/api/nodes",
425
+ headers=RequestCore.generate_headers(api_key, access_token),
426
+ json=data.dict(),
427
+ response_model=NodeResponse,
428
+ )
429
+
430
+ @staticmethod
431
+ async def get_node_stats(
432
+ api_key: str | None = None, access_token: str | None = None
433
+ ) -> NodeStatsResponse:
434
+ return await RequestCore.get(
435
+ "/api/nodes/stats",
436
+ headers=RequestCore.generate_headers(api_key, access_token),
437
+ response_model=NodeStatsResponse,
438
+ )
439
+
440
+ @staticmethod
441
+ async def get_node(
442
+ node_id: int, api_key: str | None = None, access_token: str | None = None
443
+ ) -> NodeResponse:
444
+ return await RequestCore.get(
445
+ f"/api/nodes/{node_id}",
446
+ headers=RequestCore.generate_headers(api_key, access_token),
447
+ response_model=NodeResponse,
448
+ )
449
+
450
+ @staticmethod
451
+ async def update_node(
452
+ node_id: int,
453
+ data: NodeUpdate,
454
+ api_key: str | None = None,
455
+ access_token: str | None = None,
456
+ ) -> NodeResponse:
457
+ return await RequestCore.put(
458
+ f"/api/nodes/{node_id}",
459
+ headers=RequestCore.generate_headers(api_key, access_token),
460
+ json=data.dict(),
461
+ response_model=NodeResponse,
462
+ )
463
+
464
+ @staticmethod
465
+ async def delete_node(
466
+ node_id: int, api_key: str | None = None, access_token: str | None = None
467
+ ) -> dict:
468
+ return await RequestCore.delete(
469
+ f"/api/nodes/{node_id}",
470
+ headers=RequestCore.generate_headers(api_key, access_token),
471
+ )
472
+
473
+ @staticmethod
474
+ async def enable_node(
475
+ node_id: int, api_key: str | None = None, access_token: str | None = None
476
+ ) -> NodeResponse:
477
+ return await RequestCore.post(
478
+ f"/api/nodes/{node_id}/enable",
479
+ headers=RequestCore.generate_headers(api_key, access_token),
480
+ response_model=NodeResponse,
481
+ )
482
+
483
+ @staticmethod
484
+ async def disable_node(
485
+ node_id: int, api_key: str | None = None, access_token: str | None = None
486
+ ) -> NodeResponse:
487
+ return await RequestCore.post(
488
+ f"/api/nodes/{node_id}/disable",
489
+ headers=RequestCore.generate_headers(api_key, access_token),
490
+ response_model=NodeResponse,
491
+ )
492
+
493
+ @staticmethod
494
+ async def get_services(
495
+ api_key: str | None = None, access_token: str | None = None
496
+ ) -> list[ServiceResponse]:
497
+ return await RequestCore.get(
498
+ "/api/services",
499
+ headers=RequestCore.generate_headers(api_key, access_token),
500
+ response_model=ServiceResponse,
501
+ use_list=True,
502
+ )
503
+
504
+ @staticmethod
505
+ async def create_service(
506
+ data: ServiceCreate, api_key: str | None = None, access_token: str | None = None
507
+ ) -> ServiceResponse:
508
+ return await RequestCore.post(
509
+ "/api/services",
510
+ headers=RequestCore.generate_headers(api_key, access_token),
511
+ json=data.dict(),
512
+ response_model=ServiceResponse,
513
+ )
514
+
515
+ @staticmethod
516
+ async def get_service(
517
+ service_id: int, api_key: str | None = None, access_token: str | None = None
518
+ ) -> ServiceResponse:
519
+ return await RequestCore.get(
520
+ f"/api/services/{service_id}",
521
+ headers=RequestCore.generate_headers(api_key, access_token),
522
+ response_model=ServiceResponse,
523
+ )
524
+
525
+ @staticmethod
526
+ async def update_service(
527
+ service_id: int,
528
+ data: ServiceUpdate,
529
+ api_key: str | None = None,
530
+ access_token: str | None = None,
531
+ ) -> ServiceResponse:
532
+ return await RequestCore.put(
533
+ f"/api/services/{service_id}",
534
+ headers=RequestCore.generate_headers(api_key, access_token),
535
+ json=data.dict(),
536
+ response_model=ServiceResponse,
537
+ )
538
+
539
+ @staticmethod
540
+ async def delete_service(
541
+ service_id: int, api_key: str | None = None, access_token: str | None = None
542
+ ) -> dict:
543
+ return await RequestCore.delete(
544
+ f"/api/services/{service_id}",
545
+ headers=RequestCore.generate_headers(api_key, access_token),
546
+ )
547
+
548
+ @staticmethod
549
+ async def get_guard(secret: str) -> list[str]:
550
+ return await RequestCore.get(
551
+ f"/guards/{secret}",
552
+ )
553
+
554
+ @staticmethod
555
+ async def get_guard_info(secret: str) -> SubscriptionResponse:
556
+ return await RequestCore.get(
557
+ f"/guards/{secret}/info",
558
+ )
559
+
560
+ @staticmethod
561
+ async def get_guard_usage_logs(secret: str) -> SubscriptionUsageLogsResponse:
562
+ return await RequestCore.get(
563
+ f"/guards/{secret}/usages",
564
+ response_model=SubscriptionUsageLogsResponse,
565
+ )
566
+
567
+ @staticmethod
568
+ async def get_stats(
569
+ api_key: str | None = None, access_token: str | None = None
570
+ ) -> StatsResponse:
571
+ return await RequestCore.get(
572
+ "/api/stats",
573
+ headers=RequestCore.generate_headers(api_key, access_token),
574
+ response_model=StatsResponse,
575
+ )
576
+
577
+ @staticmethod
578
+ async def get_admin_stats_combined(
579
+ api_key: str | None = None, access_token: str | None = None
580
+ ) -> AdminStatsResponseNew:
581
+ return await RequestCore.get(
582
+ "/api/stats/admin",
583
+ headers=RequestCore.generate_headers(api_key, access_token),
584
+ response_model=AdminStatsResponseNew,
585
+ )
@@ -0,0 +1,71 @@
1
+ from .admins import (
2
+ AdminPlaceHolderCategory,
3
+ AdminPlaceHolder,
4
+ AdminRole,
5
+ AdminToken,
6
+ AdminResponse,
7
+ ADMIN_PLACEHOLDER_REMARK_FORMATS,
8
+ AdminCreate,
9
+ AdminCurrentUpdate,
10
+ AdminUpdate,
11
+ AdminUsageLog,
12
+ AdminUsageLogsResponse,
13
+ )
14
+ from .subscriptions import (
15
+ SubscriptionCreate,
16
+ SubscriptionResponse,
17
+ SubscriptionUpdate,
18
+ SubscriptionUsageLog,
19
+ SubscriptionUsageLogsResponse,
20
+ SubscriptionStatsResponse,
21
+ )
22
+ from .nodes import (
23
+ NodeCategory,
24
+ NodeResponse,
25
+ NodeCreate,
26
+ NodeUpdate,
27
+ NodeStatsResponse,
28
+ )
29
+ from .services import ServiceResponse, ServiceCreate, ServiceUpdate
30
+ from .stats import (
31
+ StatsResponse,
32
+ UsageDetailStats,
33
+ CountDetailStats,
34
+ TopSubDetailStats,
35
+ ExpireSubDetailStats,
36
+ AdminStatsResponseNew,
37
+ )
38
+
39
+ __all__ = [
40
+ "AdminPlaceHolderCategory",
41
+ "AdminPlaceHolder",
42
+ "AdminRole",
43
+ "AdminToken",
44
+ "AdminResponse",
45
+ "ADMIN_PLACEHOLDER_REMARK_FORMATS",
46
+ "AdminCreate",
47
+ "AdminCurrentUpdate",
48
+ "AdminUpdate",
49
+ "AdminUsageLog",
50
+ "AdminUsageLogsResponse",
51
+ "SubscriptionCreate",
52
+ "SubscriptionResponse",
53
+ "SubscriptionUpdate",
54
+ "SubscriptionUsageLog",
55
+ "SubscriptionUsageLogsResponse",
56
+ "SubscriptionStatsResponse",
57
+ "NodeCategory",
58
+ "NodeResponse",
59
+ "NodeCreate",
60
+ "NodeUpdate",
61
+ "NodeStatsResponse",
62
+ "ServiceResponse",
63
+ "ServiceCreate",
64
+ "ServiceUpdate",
65
+ "StatsResponse",
66
+ "UsageDetailStats",
67
+ "CountDetailStats",
68
+ "TopSubDetailStats",
69
+ "ExpireSubDetailStats",
70
+ "AdminStatsResponseNew",
71
+ ]
@@ -0,0 +1,125 @@
1
+ from enum import StrEnum
2
+ from typing import Optional
3
+ from datetime import datetime
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class AdminPlaceHolderCategory(StrEnum):
8
+ INFO = "info"
9
+ LIMITED = "limited"
10
+ EXPIRED = "expired"
11
+ DISABLED = "disabled"
12
+
13
+
14
+ ADMIN_PLACEHOLDER_REMARK_FORMATS = [
15
+ "id",
16
+ "username",
17
+ "owner_username",
18
+ "enabled",
19
+ "activated",
20
+ "limited",
21
+ "expired",
22
+ "is_active",
23
+ "limit_usage",
24
+ "current_usage",
25
+ "left_usage",
26
+ "expire_date",
27
+ "expire_in",
28
+ "expire_in_days",
29
+ ]
30
+
31
+
32
+ class AdminPlaceHolder(BaseModel):
33
+ remark: str
34
+ categories: list[AdminPlaceHolderCategory]
35
+
36
+
37
+ class AdminRole(StrEnum):
38
+ OWNER = "owner"
39
+ SELLER = "seller"
40
+ RESELLER = "reseller"
41
+
42
+
43
+ class AdminToken(BaseModel):
44
+ access_token: str
45
+ token_type: str = "bearer"
46
+
47
+
48
+ class AdminResponse(BaseModel):
49
+ id: int
50
+ enabled: bool
51
+ username: str
52
+ role: AdminRole
53
+ service_ids: list[int]
54
+ create_access: Optional[bool]
55
+ update_access: Optional[bool]
56
+ remove_access: Optional[bool]
57
+ count_limit: Optional[int]
58
+ current_count: Optional[int]
59
+ left_count: Optional[int]
60
+ reached_count_limit: Optional[bool]
61
+ usage_limit: Optional[int]
62
+ current_usage: Optional[int]
63
+ left_usage: Optional[int]
64
+ reached_usage_limit: Optional[bool]
65
+ placeholders: Optional[list[AdminPlaceHolder]]
66
+ max_links: Optional[int]
67
+ shuffle_links: Optional[bool]
68
+ api_key: str
69
+ last_login_at: Optional[datetime]
70
+ last_online_at: Optional[datetime]
71
+ access_title: Optional[str] = None
72
+ access_description: Optional[str] = None
73
+ created_at: datetime
74
+ updated_at: datetime
75
+
76
+ class Config:
77
+ from_attributes = True
78
+
79
+
80
+ class AdminCreate(BaseModel):
81
+ username: str
82
+ password: str
83
+ role: AdminRole
84
+ service_ids: Optional[list[int]]
85
+ create_access: Optional[bool] = False
86
+ update_access: Optional[bool] = False
87
+ remove_access: Optional[bool] = False
88
+ count_limit: Optional[int] = None
89
+ usage_limit: Optional[int] = None
90
+ access_prefix: Optional[str] = None
91
+
92
+
93
+ class AdminUpdate(BaseModel):
94
+ password: Optional[str] = None
95
+ create_access: Optional[bool] = None
96
+ update_access: Optional[bool] = None
97
+ remove_access: Optional[bool] = None
98
+ count_limit: Optional[int] = None
99
+ usage_limit: Optional[int] = None
100
+ service_ids: Optional[list[int]] = None
101
+ placeholders: Optional[list[AdminPlaceHolder]] = None
102
+ max_links: Optional[int] = None
103
+ shuffle_links: Optional[bool] = None
104
+ access_prefix: Optional[str] = None
105
+ access_title: Optional[str] = None
106
+ access_description: Optional[str] = None
107
+
108
+
109
+ class AdminCurrentUpdate(BaseModel):
110
+ password: Optional[str] = None
111
+ placeholders: Optional[list[AdminPlaceHolder]] = None
112
+ max_links: Optional[int] = None
113
+ shuffle_links: Optional[bool] = None
114
+ access_title: Optional[str] = None
115
+ access_description: Optional[str] = None
116
+
117
+
118
+ class AdminUsageLog(BaseModel):
119
+ usage: int
120
+ created_at: datetime
121
+
122
+
123
+ class AdminUsageLogsResponse(BaseModel):
124
+ admin: AdminResponse
125
+ usages: list[AdminUsageLog]
@@ -0,0 +1,55 @@
1
+ from enum import StrEnum
2
+ from datetime import datetime
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class NodeCategory(StrEnum):
7
+ marzban = "marzban"
8
+ marzneshin = "marzneshin"
9
+
10
+
11
+ class NodeResponse(BaseModel):
12
+ id: int
13
+ enabled: bool
14
+ remark: str
15
+ category: NodeCategory
16
+ username: str
17
+ password: str
18
+ host: str
19
+ current_usage: int
20
+ last_used_at: datetime | None
21
+ offset_link: int
22
+ batch_size: int
23
+ created_at: datetime
24
+ updated_at: datetime
25
+
26
+ class Config:
27
+ from_attributes = True
28
+
29
+
30
+ class NodeCreate(BaseModel):
31
+ remark: str
32
+ category: NodeCategory
33
+ username: str
34
+ password: str
35
+ host: str
36
+ offset_link: int = 0
37
+ batch_size: int = 1
38
+
39
+
40
+ class NodeUpdate(BaseModel):
41
+ remark: str | None = None
42
+ username: str | None = None
43
+ password: str | None = None
44
+ host: str | None = None
45
+ offset_link: int | None = None
46
+ batch_size: int | None = None
47
+
48
+
49
+ class NodeStatsResponse(BaseModel):
50
+ total_nodes: int
51
+ active_nodes: int
52
+ inactive_nodes: int
53
+
54
+ class Config:
55
+ from_attributes = True
@@ -0,0 +1,21 @@
1
+ from typing import List
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class ServiceResponse(BaseModel):
6
+ id: int
7
+ remark: str
8
+ node_ids: List[int]
9
+
10
+ class Config:
11
+ from_attributes = True
12
+
13
+
14
+ class ServiceCreate(BaseModel):
15
+ remark: str
16
+ node_ids: List[int]
17
+
18
+
19
+ class ServiceUpdate(BaseModel):
20
+ remark: str | None = None
21
+ node_ids: List[int] | None = None
@@ -0,0 +1,96 @@
1
+ from datetime import datetime
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class UsageDetailStats(BaseModel):
6
+ start: datetime | None = None
7
+ end: datetime | None = None
8
+ remark: str | None = None
9
+ step: int | None = None
10
+ usage: int
11
+
12
+
13
+ class CountDetailStats(BaseModel):
14
+ start: datetime | None = None
15
+ end: datetime | None = None
16
+ count: int
17
+
18
+
19
+ class TopSubDetailStats(BaseModel):
20
+ username: str
21
+ is_active: bool
22
+ usage: int
23
+
24
+
25
+ class ExpireSubDetailStats(BaseModel):
26
+ username: str
27
+ is_active: bool
28
+ expire: int
29
+
30
+
31
+ class StatsResponse(BaseModel):
32
+ total_subscriptions: int
33
+ active_subscriptions: int
34
+ inactive_subscriptions: int
35
+ online_subscriptions: int
36
+ most_usage_subscription: str | None = None
37
+ most_usage_subscriptions: list[UsageDetailStats]
38
+
39
+ total_admins: int
40
+ active_admins: int
41
+ inactive_admins: int
42
+ most_usage_admins: list[UsageDetailStats]
43
+
44
+ total_nodes: int
45
+ active_nodes: int
46
+ inactive_nodes: int
47
+ most_usage_nodes: list[UsageDetailStats]
48
+
49
+ total_lifetime_usages: int
50
+ total_day_usages: int
51
+ total_week_usages: int
52
+ last_24h_usages: list[UsageDetailStats]
53
+ last_7d_usages: list[UsageDetailStats]
54
+
55
+
56
+ class AdminStatsResponseNew(BaseModel):
57
+ usage_limit: int | None
58
+ current_usage: int
59
+ left_usage: int | None
60
+ lifetime_usage: int
61
+ current_day_usage: int
62
+ current_week_usage: int
63
+ yesterday_usage: int
64
+ last_week_usage: int
65
+ last_24h_usages: list[UsageDetailStats]
66
+ last_7d_usages: list[UsageDetailStats]
67
+ last_1m_usages: list[UsageDetailStats]
68
+ last_3m_usages: list[UsageDetailStats]
69
+ last_1y_usages: list[UsageDetailStats]
70
+ today_top_10_usage_subscriptions: list[TopSubDetailStats]
71
+ week_top_10_usage_subscriptions: list[TopSubDetailStats]
72
+ month_top_10_usage_subscriptions: list[TopSubDetailStats]
73
+ last_24h_counts: list[CountDetailStats]
74
+ last_7d_counts: list[CountDetailStats]
75
+ last_1m_counts: list[CountDetailStats]
76
+ last_3m_counts: list[CountDetailStats]
77
+ last_1y_counts: list[CountDetailStats]
78
+ limit_count: int
79
+ current_count: int
80
+ left_count: int
81
+ total_subscriptions: int
82
+ active_subscriptions: int
83
+ inactive_subscriptions: int
84
+ disabled_subscriptions: int
85
+ expired_subscriptions: int
86
+ limited_subscriptions: int
87
+ today_new_subscriptions: int
88
+ yesterday_new_subscriptions: int
89
+ today_requested_subscriptions: int
90
+ today_revoked_subscriptions: int
91
+ today_reseted_subscriptions: int
92
+ today_expire_soon_subscriptions: list[ExpireSubDetailStats]
93
+ week_expire_soon_subscriptions: list[ExpireSubDetailStats]
94
+ today_removed_subscriptions: int
95
+ yesterday_removed_subscriptions: int
96
+ total_removed_subscriptions: int
@@ -0,0 +1,76 @@
1
+ from typing import Optional
2
+ from datetime import datetime
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class SubscriptionResponse(BaseModel):
7
+ id: int
8
+ username: str
9
+ owner_username: str
10
+ access_key: str
11
+
12
+ enabled: bool
13
+ activated: bool
14
+ reached: bool
15
+ limited: bool
16
+ expired: bool
17
+ is_active: bool
18
+ is_online: bool
19
+
20
+ link: str
21
+
22
+ limit_usage: int
23
+ reset_usage: int
24
+ total_usage: int
25
+ current_usage: int
26
+ limit_expire: int
27
+
28
+ service_ids: list[int]
29
+
30
+ online_at: Optional[datetime]
31
+ last_reset_at: Optional[datetime]
32
+ last_revoke_at: Optional[datetime]
33
+ last_request_at: Optional[datetime]
34
+ last_client_agent: Optional[str]
35
+ created_at: datetime
36
+ updated_at: datetime
37
+
38
+ class Config:
39
+ from_attributes = True
40
+
41
+
42
+ class SubscriptionCreate(BaseModel):
43
+ username: str
44
+ limit_usage: int
45
+ limit_expire: int
46
+ service_ids: list[int]
47
+ access_key: Optional[str] = None
48
+
49
+
50
+ class SubscriptionUpdate(BaseModel):
51
+ limit_usage: Optional[int] = None
52
+ limit_expire: Optional[int] = None
53
+ service_ids: Optional[list[int]] = None
54
+
55
+
56
+ class SubscriptionUsageLog(BaseModel):
57
+ usage: int
58
+ created_at: datetime
59
+
60
+
61
+ class SubscriptionUsageLogsResponse(BaseModel):
62
+ subscription: SubscriptionResponse
63
+ usages: list[SubscriptionUsageLog]
64
+
65
+
66
+ class SubscriptionStatsResponse(BaseModel):
67
+ total: int
68
+ active: int
69
+ inactive: int
70
+ disabled: int
71
+ expired: int
72
+ limited: int
73
+ has_revoked: int
74
+ has_reseted: int
75
+ total_removed: int
76
+ total_usage: int
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: guardcoreapi
3
+ Version: 0.3.0.dev20251113215748
4
+ Summary: Guard Management Core Api Python lib
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: aiohttp>=3.13.1
9
+ Requires-Dist: pydantic>=2.12.3
10
+ Dynamic: license-file
@@ -0,0 +1,16 @@
1
+ guardcoreapi/__init__.py,sha256=GohrmPFFH_Bfa5Ex3n0u-c4RjU47KbmoBYi38k8S_NI,62
2
+ guardcoreapi/manager.py,sha256=kpQmyathhP6Ye7-AOC08tfOQJ7ST22kzEnwCST1lQTc,20017
3
+ guardcoreapi/core/__init__.py,sha256=QJhZCPZui7zDpnQJbmWZsEijiAAP3rglUZb_w6E61_M,381
4
+ guardcoreapi/core/exceptions.py,sha256=KbmdV8p31gSlKXjmJoW3lR9wGOQSvaaZKl9oGb7qlFI,555
5
+ guardcoreapi/core/request.py,sha256=YHW2CB5MBA0geBsad4XtjcuhaZORzr2dE1Z5j_MfjQQ,4436
6
+ guardcoreapi/types/__init__.py,sha256=z_PjXR-tCAOhJhNNa7s6S3l7lGglxNJ76wvTqrDC4fM,1587
7
+ guardcoreapi/types/admins.py,sha256=trhhEAw2PbKYlkPL41vwglaRLrarDCYSzoiMibZnmRM,3107
8
+ guardcoreapi/types/nodes.py,sha256=U4uCTm2TYETysceN_5ap4dP2if6sIUyEnavCvzlNpUo,1075
9
+ guardcoreapi/types/services.py,sha256=3lpP76Q9iij2zSEXutDg1IatIslLokQ-DNPvSdckRCQ,369
10
+ guardcoreapi/types/stats.py,sha256=gFeIgRB-8dtqbdsLwI5NZ3PeDnNWsR-LLg9vjZQljFk,2738
11
+ guardcoreapi/types/subscriptions.py,sha256=gWoOwm_sARjVvd6Wut8zb4uVGaZCaUcATFRdcdo2VrY,1544
12
+ guardcoreapi-0.3.0.dev20251113215748.dist-info/licenses/LICENSE,sha256=BPTJfvegcaek6E2PQ2rViLbxbfB1AbjIauxVQWo5Od4,1062
13
+ guardcoreapi-0.3.0.dev20251113215748.dist-info/METADATA,sha256=BQbPkvLyq-UcO9SWL55z5Zhg7gnG_TOOg_hX3AvF6BY,291
14
+ guardcoreapi-0.3.0.dev20251113215748.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ guardcoreapi-0.3.0.dev20251113215748.dist-info/top_level.txt,sha256=ArZR_iOVG8XvUCTMHioVgKI8CkJOX_yH8t7898OXBF8,13
16
+ guardcoreapi-0.3.0.dev20251113215748.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Erfan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ guardcoreapi