Skip to content

API Reference

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

apps

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

get_app_service() -> AppService async

Get app service :return AppService:

Source code in sharkservers/apps/dependencies.py
 8
 9
10
11
12
13
async def get_app_service() -> AppService:
    """
    Get app service
    :return AppService:
    """
    return AppService()

get_valid_apps(apps_id: int, apps_service: AppService = Depends(get_app_service)) -> ormar.Model async

Get valid apps :param app_service: :param apps_id: :return Apps:

Source code in sharkservers/apps/dependencies.py
16
17
18
19
20
21
22
23
24
25
26
async def get_valid_apps(
    apps_id: int,
    apps_service: AppService = Depends(get_app_service),
) -> ormar.Model:
    """
    Get valid apps
    :param app_service:
    :param apps_id:
    :return Apps:
    """
    return await apps_service.get_one(id=apps_id)

enums

AppsEventsEnum

Bases: str, Enum

RApps events enum.

Source code in sharkservers/apps/enums.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
class AppsEventsEnum(str, Enum):
    """RApps events enum."""

    GET_ALL = "APPS_GET_ALL"
    GET_ONE = "APPS_GET_ONE"
    CREATE = "APPS_CREATE"
    UPDATE = "APPS_UPDATE"
    DELETE = "APPS_DELETE"
    ADMIN_GET_ALL = "APPS_ADMIN_GET_ALL"
    ADMIN_GET_ONE = "APPS_ADMIN_GET_ONE"
    ADMIN_CREATE = "APPS_ADMIN_CREATE"
    ADMIN_UPDATE = "APPS_ADMIN_UPDATE"
    ADMIN_DELETE = "APPS_ADMIN_DELETE"

models

services

auth

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Dependencies for auth.


get_access_token_service: Get access token service
get_refresh_token_service: Get refresh token service
get_auth_service: Get auth service
get_current_user: Get current user
get_current_active_user: Get current active user
get_admin_user: Get admin user
get_application: Get application
get_activation_account_code_service: Get activation account code service
get_change_account_email_code_service: Get change account email code service
get_reset_account_password_code_service: Get reset account password code service
get_steam_auth_service: Get steam auth service

get_access_token_service(settings: Settings = Depends(get_settings)) -> JWTService async

Get the access token service.


settings (Settings): The application settings.

JWTService: The access token service.
Source code in sharkservers/auth/dependencies.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
async def get_access_token_service(
    settings: Settings = Depends(get_settings),
) -> JWTService:
    """
    Get the access token service.

    Args:
    ----
        settings (Settings): The application settings.

    Returns:
    -------
        JWTService: The access token service.

    """
    return JWTService(
        secret_key=settings.SECRET_KEY,
        expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRES),
    )

get_refresh_token_service(settings: Settings = Depends(get_settings)) -> JWTService async

Retrieve the JWTService instance for handling refresh tokens.

settings (Settings): The application settings.

JWTService: The JWTService instance for handling refresh tokens.
Source code in sharkservers/auth/dependencies.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
async def get_refresh_token_service(
    settings: Settings = Depends(get_settings),
) -> JWTService:
    """
    # Retrieve the JWTService instance for handling refresh tokens.

    Args:
    ----
        settings (Settings): The application settings.

    Returns:
    -------
        JWTService: The JWTService instance for handling refresh tokens.
    """
    return JWTService(
        secret_key=settings.REFRESH_SECRET_KEY,
        expires_delta=timedelta(minutes=settings.REFRESH_TOKEN_EXPIRES),
    )

get_auth_service(users_service: UserService = Depends(get_users_service), roles_service: RoleService = Depends(get_roles_service), scopes_service: ScopeService = Depends(get_scopes_service)) -> AuthService async

Get the authentication service with the required dependencies.


users_service (UserService): The user service dependency.
roles_service (RoleService): The role service dependency.
scopes_service (ScopeService): The scope service dependency.
users_sessions_service (UserSessionService): The user session service dependency.

AuthService: The authentication service instance.
Source code in sharkservers/auth/dependencies.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
async def get_auth_service(
    users_service: UserService = Depends(get_users_service),
    roles_service: RoleService = Depends(get_roles_service),
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> AuthService:
    """
    Get the authentication service with the required dependencies.

    Args:
    ----
        users_service (UserService): The user service dependency.
        roles_service (RoleService): The role service dependency.
        scopes_service (ScopeService): The scope service dependency.
        users_sessions_service (UserSessionService): The user session service dependency.

    Returns:
    -------
        AuthService: The authentication service instance.
    """
    return AuthService(
        users_service=users_service,
        roles_service=roles_service,
        scopes_service=scopes_service,
    )

get_current_user(security_scopes: SecurityScopes, token: str = Depends(AuthService.oauth2_scheme), access_token_service: JWTService = Depends(get_access_token_service), users_service: UserService = Depends(get_users_service)) -> User async

Retrieve the current user based on the provided security scopes and token.


security_scopes (SecurityScopes): The security scopes required for the user.
token (str): The access token used for authentication.
access_token_service (JWTService): The service used for decoding the access token.
users_service (UserService): The service used for retrieving user information.
users_sessions_service (UserSessionService): The service used for retrieving user session information.

User: The current user.

InvalidCredentialsException: If the credentials are invalid.
NoPermissionsException: If the user does not have the required permissions.
Source code in sharkservers/auth/dependencies.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
async def get_current_user(
    security_scopes: SecurityScopes,
    token: str = Depends(AuthService.oauth2_scheme),
    access_token_service: JWTService = Depends(get_access_token_service),
    users_service: UserService = Depends(get_users_service),
) -> User:
    """
    Retrieve the current user based on the provided security scopes and token.

    Args:
    ----
        security_scopes (SecurityScopes): The security scopes required for the user.
        token (str): The access token used for authentication.
        access_token_service (JWTService): The service used for decoding the access token.
        users_service (UserService): The service used for retrieving user information.
        users_sessions_service (UserSessionService): The service used for retrieving user session information.

    Returns:
    -------
        User: The current user.

    Raises:
    ------
        InvalidCredentialsException: If the credentials are invalid.
        NoPermissionsException: If the user does not have the required permissions.
    """
    token_data = access_token_service.decode_token(token)
    for scope in security_scopes.scopes:
        if scope not in token_data.scopes:
            raise no_permissions_exception
    user: User = await users_service.get_one(
        id=token_data.user_id,
        related=[
            "roles",
            "display_role",
            "roles__scopes",
            "player",
            "player__steamrep_profile",
            "sessions",
        ],
    )
    if user.secret_salt != token_data.secret:
        raise invalid_credentials_exception

    return user

get_current_active_user(current_user: User = Depends(get_current_user)) -> User async

Retrieve the current active user.

This function checks if the current user is activated. If not, it raises an exception.


current_user (User): The current user.

User: The current active user.

inactivate_user_exception: If the current user is not activated.
Source code in sharkservers/auth/dependencies.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
async def get_current_active_user(
    current_user: User = Depends(get_current_user),
) -> User:
    """
    Retrieve the current active user.

    This function checks if the current user is activated. If not, it raises an exception.

    Args:
    ----
        current_user (User): The current user.

    Returns:
    -------
        User: The current active user.

    Raises:
    ------
        inactivate_user_exception: If the current user is not activated.
    """
    if not current_user.is_activated:
        raise inactivate_user_exception
    return current_user

get_admin_user(user: User = Depends(get_current_active_user)) -> User async

Retrieve the admin user.


user (User): The current user.

User: The admin user.

NotAdminUserException: If the user is not a superuser.
Source code in sharkservers/auth/dependencies.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
async def get_admin_user(user: User = Depends(get_current_active_user)) -> User:
    """
    Retrieve the admin user.

    Args:
    ----
        user (User): The current user.

    Returns:
    -------
        User: The admin user.

    Raises:
    ------
        NotAdminUserException: If the user is not a superuser.
    """
    if not user.is_superuser:
        raise not_admin_user_exception
    return user

get_activation_account_code_service(redis: Redis = Depends(get_redis)) -> CodeService async

Get the activation account code service.

This function returns an instance of the CodeService class that is used for generating and validating activation codes for user accounts.


redis: The Redis connection object.

The CodeService instance.
Source code in sharkservers/auth/dependencies.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
async def get_activation_account_code_service(
    redis: Redis = Depends(get_redis),
) -> CodeService:
    """
    Get the activation account code service.

    This function returns an instance of the CodeService class that is used for generating
    and validating activation codes for user accounts.

    Args:
    ----
        redis: The Redis connection object.

    Returns:
    -------
        The CodeService instance.

    """
    return CodeService(redis=redis, key=RedisAuthKeyEnum.ACTIVATE_USER.value)

get_change_account_email_code_service(redis: Redis = Depends(get_redis)) -> CodeService async

Retrieves the CodeService instance for changing the account email.


redis (Redis): The Redis instance.

CodeService: The CodeService instance for changing the account email.
Source code in sharkservers/auth/dependencies.py
232
233
234
235
236
237
238
239
240
241
242
243
244
async def get_change_account_email_code_service(
    redis: Redis = Depends(get_redis),
) -> CodeService:
    """
    Retrieves the CodeService instance for changing the account email.

    Args:
    ----
        redis (Redis): The Redis instance.

        CodeService: The CodeService instance for changing the account email.
    """  # noqa: D401
    return CodeService(redis=redis, key=RedisAuthKeyEnum.CHANGE_EMAIL.value)

get_reset_account_password_code_service(redis: Redis = Depends(get_redis)) -> CodeService async

Retrieve the CodeService instance for resetting account password.


redis (Redis): The Redis instance used for storing the reset password code.

CodeService: The CodeService instance for resetting account password.
Source code in sharkservers/auth/dependencies.py
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
async def get_reset_account_password_code_service(
    redis: Redis = Depends(get_redis),
) -> CodeService:
    """
    Retrieve the CodeService instance for resetting account password.

    Args:
    ----
        redis (Redis): The Redis instance used for storing the reset password code.

    Returns:
    -------
        CodeService: The CodeService instance for resetting account password.
    """
    return CodeService(redis=redis, key=RedisAuthKeyEnum.RESET_PASSWORD.value)

get_steam_auth_service(users_service: UserService = Depends(get_users_service), players_service: PlayerService = Depends(get_players_service)) -> SteamAuthService async

Get the SteamAuthService instance with the provided dependencies.


users_service (UserService): The UserService instance.
players_service (PlayerService): The PlayerSer>

SteamAuthService: The SteamAuthService instance.
Source code in sharkservers/auth/dependencies.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
async def get_steam_auth_service(
    users_service: UserService = Depends(get_users_service),
    players_service: PlayerService = Depends(get_players_service),
) -> SteamAuthService:
    """
    Get the SteamAuthService instance with the provided dependencies.

    Args:
    ----
        users_service (UserService): The UserService instance.
        players_service (PlayerService): The PlayerSer>
    -------
        SteamAuthService: The SteamAuthService instance.
    """
    return SteamAuthService(users_service, players_service)

enums

Enums for auth module.

Enums: - RedisAuthKeyEnum: The keys for the redis auth codes. - AuthExceptionsDetailEnum: The detail messages for the auth exceptions. - AuthEventsEnum: The events for the auth module.

RedisAuthKeyEnum

Bases: str, Enum

Enum class representing the keys used in Redis for authentication purposes.

Source code in sharkservers/auth/enums.py
12
13
14
15
16
17
class RedisAuthKeyEnum(str, Enum):
    """Enum class representing the keys used in Redis for authentication purposes."""

    ACTIVATE_USER = "activate-user-code"
    RESET_PASSWORD = "reset-password-code"  # noqa: S105
    CHANGE_EMAIL = "change-email-code"

AuthExceptionsDetailEnum

Bases: str, Enum

Enum class that defines the detail messages for various authentication exceptions.

Source code in sharkservers/auth/enums.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class AuthExceptionsDetailEnum(str, Enum):
    """Enum class that defines the detail messages for various authentication exceptions."""

    EMAIL_TAKEN = "Email is taken"
    USERNAME_TAKEN = "Username is taken"
    INVALID_CODE = "Invalid code"
    USER_ACTIVATED = "User is already activated"
    INVALID_CREDENTIALS = "Invalid credentials"
    TOKEN_EXPIRED = "Token has expired"  # noqa: S105
    INACTIVE_USER = "Inactive user"
    NOT_ADMIN_USER = "Not admin user"
    INCORRECT_USERNAME_PASSWORD = "Incorrect username or password"  # noqa: S105
    NO_PERMISSIONS = "Not enough permissions"
    USER_EXISTS = "Email or username already exists"

AuthEventsEnum

Bases: str, Enum

Enum class that defines the events for the auth module.

Source code in sharkservers/auth/enums.py
36
37
38
39
40
41
42
43
44
45
46
class AuthEventsEnum(str, Enum):
    """Enum class that defines the events for the auth module."""

    REGISTERED_PRE = "USER_REGISTERED_PRE"
    REGISTERED_POST = "USER_REGISTERED_POST"
    ACTIVATED_PRE = "USER_ACTIVATED_PRE"
    ACTIVATED_POST = "USER_ACTIVATED_POST"
    ACCESS_TOKEN_PRE = "ACCESS_TOKEN_PRE"  # noqa: S105
    ACCESS_TOKEN_POST = "ACCESS_TOKEN_POST"  # noqa: S105
    REFRESH_TOKEN_PRE = "REFRESH_TOKEN_PRE"  # noqa: S105
    REFRESH_TOKEN_POST = "REFRESH_TOKEN"  # noqa: S105

exceptions

Exceptions for the auth module.

Exceptions:
  • username_taken_exception: Raised when a username is taken.
  • email_taken_exception: Raised when an email is taken.
  • invalid_activation_code_exception: Raised when an activation code is invalid.
  • user_activated_exception: Raised when a user is already activated.
  • not_admin_user_exception: Raised when a user is not an admin.
  • incorrect_username_password_exception: Raised when the username or password is incorrect.
  • no_permissions_exception: Raised when a user has no permissions.
  • invalid_credentials_exception: Raised when the credentials are invalid.
  • inactivate_user_exception: Raised when a user is inactive.
  • user_exists_exception: Raised when a user exists.
  • invalid_activation_code_exception: Raised when an activation code is invalid.
  • token_expired_exception: Raised when a token is expired.

schemas

Schemas for the auth module.

Schemas: - UsernameRegex: The schema for the username regex. - PasswordSchema: The schema for the password. - RegisterUserSchema: The schema for registering a user. - TokenDetailsSchema: The schema for the token details. - TokenSchema: The schema for the tokens. - TokenDataSchema: The schema for the token data. - RefreshTokenSchema: The schema for the refresh token. - ActivateUserCodeSchema: The schema for the activation code. - ResendActivationCodeSchema: The schema for resending the activation code. - UserActivatedSchema: The schema for the user activated. - EmailConfirmSchema: The schema for the email confirmation. - ResetPasswordSchema: The schema for resetting the password. - SteamAuthSchema: The schema for the steam auth.

UsernameRegex

Bases: BaseModel

Schema for the username regex.

Source code in sharkservers/auth/schemas.py
26
27
28
29
30
31
32
33
34
35
class UsernameRegex(BaseModel):
    """Schema for the username regex."""

    username: str = Field(
        min_length=3,
        max_length=32,
        regex=r"^[a-zA-Z0-9_-]+$",
        strip_whitespace=True,
        default="username",
    )

PasswordSchema

Bases: BaseModel

Schema for validating password fields.

Source code in sharkservers/auth/schemas.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class PasswordSchema(BaseModel):
    """Schema for validating password fields."""

    password: str = Field(min_length=8, max_length=255)
    password2: str = Field(min_length=8, max_length=255)

    @validator("password2")
    def passwords_match(
        cls,
        value,
        values,
        **kwargs,  # noqa: ANN001, N805, ARG002, ANN003
    ) -> None:
        """
        Check if the 'password2' field matches the 'password' field.

        Raises
        ------
            ValueError: If the passwords do not match.
        """
        if "password" in values and value != values["password"]:
            msg = "Passwords do not match"
            raise ValueError(msg)
passwords_match(value, values, **kwargs) -> None

Check if the 'password2' field matches the 'password' field.

Raises
ValueError: If the passwords do not match.
Source code in sharkservers/auth/schemas.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@validator("password2")
def passwords_match(
    cls,
    value,
    values,
    **kwargs,  # noqa: ANN001, N805, ARG002, ANN003
) -> None:
    """
    Check if the 'password2' field matches the 'password' field.

    Raises
    ------
        ValueError: If the passwords do not match.
    """
    if "password" in values and value != values["password"]:
        msg = "Passwords do not match"
        raise ValueError(msg)

RegisterUserSchema

Bases: UsernameRegex, PasswordSchema

Schema for registering a user.

Source code in sharkservers/auth/schemas.py
63
64
65
66
class RegisterUserSchema(UsernameRegex, PasswordSchema):
    """Schema for registering a user."""

    email: EmailStr

TokenDetailsSchema

Bases: BaseModel

Schema for the token details.

Source code in sharkservers/auth/schemas.py
69
70
71
72
73
74
class TokenDetailsSchema(BaseModel):
    """Schema for the token details."""

    token: str
    token_type: str
    exp: datetime.datetime

TokenSchema

Bases: BaseModel

Schema for the tokens.

Source code in sharkservers/auth/schemas.py
77
78
79
80
81
class TokenSchema(BaseModel):
    """Schema for the tokens."""

    access_token: TokenDetailsSchema
    refresh_token: TokenDetailsSchema

TokenDataSchema

Bases: BaseModel

Schema for the token data.

Source code in sharkservers/auth/schemas.py
84
85
86
87
88
89
90
class TokenDataSchema(BaseModel):
    """Schema for the token data."""

    user_id: int | None = None
    secret: str
    scopes: list[str] = []
    session_id: str | None

RefreshTokenSchema

Bases: BaseModel

Schema for the refresh token.

Source code in sharkservers/auth/schemas.py
93
94
95
96
class RefreshTokenSchema(BaseModel):
    """Schema for the refresh token."""

    refresh_token: str

ActivateUserCodeSchema

Bases: BaseModel

Schema for the activation code.

Source code in sharkservers/auth/schemas.py
 99
100
101
102
class ActivateUserCodeSchema(BaseModel):
    """Schema for the activation code."""

    code: str = Field(max_length=5)

ResendActivationCodeSchema

Bases: BaseModel

Schema for resending the activation code.

Source code in sharkservers/auth/schemas.py
105
106
107
108
class ResendActivationCodeSchema(BaseModel):
    """Schema for resending the activation code."""

    email: EmailStr

UserActivatedSchema

Bases: BaseModel

Schema for the user activated.

Source code in sharkservers/auth/schemas.py
111
112
113
114
115
class UserActivatedSchema(BaseModel):
    """Schema for the user activated."""

    id: int
    is_activated: bool

EmailConfirmSchema

Bases: BaseModel

Schema for the email confirmation.

Source code in sharkservers/auth/schemas.py
118
119
120
121
122
123
class EmailConfirmSchema(BaseModel):
    """Schema for the email confirmation."""

    old_email: EmailStr
    new_email: EmailStr
    is_confirmed: bool

ResetPasswordSchema

Bases: ActivateUserCodeSchema, PasswordSchema

Schema for resetting the password.

Source code in sharkservers/auth/schemas.py
126
127
class ResetPasswordSchema(ActivateUserCodeSchema, PasswordSchema):
    """Schema for resetting the password."""

SteamAuthSchema

Bases: BaseModel

Schema for the steam auth.

Source code in sharkservers/auth/schemas.py
130
131
132
133
134
135
136
137
138
139
140
141
142
class SteamAuthSchema(BaseModel):
    """Schema for the steam auth."""

    openid_ns: str
    openid_mode: str
    openid_op_endpoint: str
    openid_claimed_id: str
    openid_identity: str
    openid_return_to: str
    openid_response_nonce: str
    openid_assoc_handle: str
    openid_signed: str
    openid_sig: str

services

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

auth

Auth service.

Module responsible for authentication and authorization.

Classes: - OAuth2ClientSecretRequestForm: Represents a form for requesting OAuth2 client secret. - AuthService: Represents the service responsible for authentication and authorization operations.

OAuth2ClientSecretRequestForm

Represents a form for requesting OAuth2 client secret.


client_id (str, optional): The client ID. Defaults to None.
client_secret (str, optional): The client secret. Defaults to None.
Source code in sharkservers/auth/services/auth.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class OAuth2ClientSecretRequestForm:
    """
    Represents a form for requesting OAuth2 client secret.

    Args:
    ----
        client_id (str, optional): The client ID. Defaults to None.
        client_secret (str, optional): The client secret. Defaults to None.
    """

    def __init__(
        self,
        client_id: str | None = Form(default=None),
        client_secret: str | None = Form(default=None),
    ) -> None:
        """Initializes an instance of the Auth class."""  # noqa: D401
        self.client_id = client_id
        self.client_secret = client_secret
__init__(client_id: str | None = Form(default=None), client_secret: str | None = Form(default=None)) -> None

Initializes an instance of the Auth class.

Source code in sharkservers/auth/services/auth.py
64
65
66
67
68
69
70
71
def __init__(
    self,
    client_id: str | None = Form(default=None),
    client_secret: str | None = Form(default=None),
) -> None:
    """Initializes an instance of the Auth class."""  # noqa: D401
    self.client_id = client_id
    self.client_secret = client_secret
AuthService

Service class for authentication-related operations.

Attributes
users_service (UserService): The service for managing user-related operations.
roles_service (RoleService): The service for managing role-related operations.
scopes_service (ScopeService): The service for managing scope-related operations.
users_sessions_service (UserSessionService): The service for managing user session-related operations.
Methods
authenticate_user: Authenticates a user based on the provided username and password.
register: Register a new user.
login: Authenticate a user and generates access and refresh tokens.
create_access_token_from_refresh_token: Create an access token from a refresh token.
logout: Log out the specified user by generating a new secret salt and updating it in the user's record.
generate_code: Generate a random code consisting of digits.
generate_secret_salt: Generate a secret salt consisting of random alphanumeric characters.
resend_activation_code: Resend the activation code to the specified email address.
activate_user: Activate a user based on the provided activation code.
confirm_change_email: Confirm the change of email for a user.
reset_password: Reset the password for a user.
get_user_agent: Get the User-Agent header from the request.
Source code in sharkservers/auth/services/auth.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
class AuthService:
    """
    Service class for authentication-related operations.

    Attributes
    ----------
        users_service (UserService): The service for managing user-related operations.
        roles_service (RoleService): The service for managing role-related operations.
        scopes_service (ScopeService): The service for managing scope-related operations.
        users_sessions_service (UserSessionService): The service for managing user session-related operations.

    Methods
    -------
        authenticate_user: Authenticates a user based on the provided username and password.
        register: Register a new user.
        login: Authenticate a user and generates access and refresh tokens.
        create_access_token_from_refresh_token: Create an access token from a refresh token.
        logout: Log out the specified user by generating a new secret salt and updating it in the user's record.
        generate_code: Generate a random code consisting of digits.
        generate_secret_salt: Generate a secret salt consisting of random alphanumeric characters.
        resend_activation_code: Resend the activation code to the specified email address.
        activate_user: Activate a user based on the provided activation code.
        confirm_change_email: Confirm the change of email for a user.
        reset_password: Reset the password for a user.
        get_user_agent: Get the User-Agent header from the request.
    """

    oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v1/auth/token")

    def __init__(
        self,
        users_service: UserService,
        roles_service: RoleService,
        scopes_service: ScopeService,
    ) -> None:
        """
        Initialize the Auth service.

        Args:
        ----
            users_service (UserService): The service for managing users.
            roles_service (RoleService): The service for managing roles.
            scopes_service (ScopeService): The service for managing scopes.
            users_sessions_service (UserSessionService): The service for managing user sessions.
        """
        self.users_service = users_service
        self.roles_service = roles_service
        self.scopes_service = scopes_service

    async def authenticate_user(
        self,
        username: str,
        password: str,
        user_ip: str | None,  # noqa: ARG002
        user_agent: str | None,  # noqa: ARG002
    ) -> User | bool:
        """
        Authenticates a user based on the provided username and password.

        Args:
        ----
            username (str): The username of the user.
            password (str): The password of the user.
            user_ip (str): The IP address of the user.
            user_agent (str): The user agent string of the user's browser.

        Returns:
        -------
            Union[User, bool]: The authenticated user object if successful, False otherwise.
        """  # noqa: D401
        try:
            user: User = await self.users_service.get_one(
                username=username,
                related=["roles", "roles__scopes", "sessions"],
            )
            if not verify_password(password, user.password) or not user.is_activated:
                return False
        except NoMatch:
            return False
        return user

    async def register(  # noqa: PLR0913
        self,
        user_data: RegisterUserSchema,
        is_activated: bool = False,  # noqa: FBT001, FBT002
        is_superuser: bool = False,  # noqa: FBT001, FBT002
        request: Request = None,  # noqa: ARG002
        settings: Settings = None,
    ) -> User:
        """
        Register a new user.

        Args:
        ----
            user_data (RegisterUserSchema): The user data for registration.
            is_activated (bool, optional): Whether the user is activated. Defaults to False.
            is_superuser (bool, optional): Whether the user is a superuser. Defaults to False.
            request (Request, optional): The request object. Defaults to None.
            settings (Settings, optional): The settings object. Defaults to None.

        Returns:
        -------
            User: The registered user.

        Raises:
        ------
            user_exists_exception: If the user already exists.
        """
        try:
            password = get_password_hash(user_data.password)
            secret_salt = self.generate_secret_salt()

            user_role = await self.roles_service.get_one(
                tag=ProtectedDefaultRolesTagEnum.USER.value,
            )
            role = user_role
            if is_superuser:
                role = await self.roles_service.get_one(
                    tag=ProtectedDefaultRolesTagEnum.ADMIN.value,
                )

            avatar_url = (
                f"{settings.SITE_URL}/static/images/default_avatar.png"
                if settings
                else "http://localhost/static/images/default_avatar.png"
            )
            registered_user = await self.users_service.create(
                username=user_data.username,
                email=user_data.email,
                password=password,
                display_role=role,
                avatar=str(avatar_url),
                secret_salt=secret_salt,
                is_activated=is_activated,
                is_superuser=is_superuser,
            )
            if not is_superuser:
                await registered_user.roles.add(role)
            else:
                await registered_user.roles.add(user_role)
                await registered_user.roles.add(role)
            return registered_user  # noqa: TRY300
        except (
            IntegrityError,
            SQLIntegrityError,
            UniqueViolationError,
            HTTPException,
        ) as err:
            raise user_exists_exception from err

    async def login(  # noqa: PLR0913
        self,
        form_data: OAuth2PasswordRequestForm,
        jwt_access_token_service: JWTService,
        jwt_refresh_token_service: JWTService,
        user_ip: str | None = None,
        user_agent: str | None = None,
    ) -> tuple[TokenSchema, User]:
        """
        Authenticate a user and generates access and refresh tokens.

        Args:
        ----
            form_data (OAuth2PasswordRequestForm): The form data containing the username and password.
            jwt_access_token_service (JWTService): The service used to encode the access token.
            jwt_refresh_token_service (JWTService): The service used to encode the refresh token.
            user_ip (str): The IP address of the user.
            user_agent (str): The user agent string.

        Returns:
        -------
            tuple[TokenSchema, User]: A tuple containing the access and refresh tokens, and the authenticated user.
        """
        user = await self.authenticate_user(
            form_data.username,
            form_data.password,
            user_ip=user_ip,
            user_agent=user_agent,
        )
        if not user:
            raise incorrect_username_password_exception

        scopes = await self.scopes_service.get_scopes_list(user.roles)
        access_token, access_token_exp = jwt_access_token_service.encode(
            data={
                "sub": str(user.id),
                "scopes": scopes,
                "secret": user.secret_salt,
            },
        )
        refresh_token, refresh_toke_exp = jwt_refresh_token_service.encode(
            data={"sub": str(user.id), "secret": user.secret_salt},
        )
        await user.update(last_online=now_datetime())
        return (
            TokenSchema(
                access_token=TokenDetailsSchema(
                    token=access_token,
                    exp=access_token_exp,
                    token_type="bearer",  # noqa: S106
                ),
                refresh_token=TokenDetailsSchema(
                    token=refresh_token,
                    exp=refresh_toke_exp,
                    token_type="bearer",  # noqa: S106
                ),
            ),
            user,
        )

    async def create_access_token_from_refresh_token(
        self,
        token_data: RefreshTokenSchema,
        jwt_access_token_service: JWTService,
        jwt_refresh_token_service: JWTService,
    ) -> tuple[TokenSchema, User]:
        """
        Create an access token from a refresh token.

        Args:
        ----
            token_data (RefreshTokenSchema): The refresh token data.
            jwt_access_token_service (JWTService): The JWT service for access tokens.
            jwt_refresh_token_service (JWTService): The JWT service for refresh tokens.

        Returns:
        -------
            tuple[TokenSchema, User]: A tuple containing the access token schema and the user object.
        """
        try:
            payload = jwt_refresh_token_service.decode(token_data.refresh_token)
            refresh_token_exp = payload.get("exp", None)
            # TODO(Qwizi): replace with timezone  # noqa: TD003
            if (
                datetime.fromtimestamp(refresh_token_exp)
                < now_datetime()  # noqa: DTZ006
            ):
                raise token_expired_exception
            user_id = int(payload.get("sub"))
            secret: str = payload.get("secret")
            user = await self.users_service.get_one(
                id=user_id,
                related=["roles", "roles__scopes"],
            )
            if not user or user.secret_salt != secret:
                raise invalid_credentials_exception
            scopes = await self.scopes_service.get_scopes_list(user.roles)
            access_token, access_token_exp = jwt_access_token_service.encode(
                data={
                    "sub": str(user.id),
                    "scopes": scopes,
                    "secret": user.secret_salt,
                },
            )
            # TODO(Qwizi): replace with timezone  # noqa: TD003
            await user.update(last_login=datetime.utcnow())  # noqa: DTZ003
            return (
                TokenSchema(
                    access_token=TokenDetailsSchema(
                        token=access_token,
                        exp=access_token_exp,
                        token_type="bearer",  # noqa: S106
                    ),
                    refresh_token=TokenDetailsSchema(
                        token=token_data.refresh_token,
                        exp=refresh_token_exp,
                        token_type="bearer",  # noqa: S106
                    ),
                ),
                user,
            )
        except JWTError as err:
            raise invalid_credentials_exception from err

    async def logout(self, user: User) -> User:
        """
        Log out the specified user by generating a new secret salt and updating it in the user's record.

        Args:
        ----
            user (User): The user to log out.

        Returns:
        -------
            User: The updated user object.
        """
        secret = self.generate_secret_salt()
        await user.update(secret_salt=secret)
        return user

    @staticmethod
    def generate_code(number: int = 8) -> str:
        """
        Generate a random code consisting of digits.

        Args:
        ----
            number (int): The length of the generated code. Default is 8.

        Returns:
        -------
            str: The generated code.
        """
        # TODO(Qwizi): replace with secrets (secrets.token_hex(number)[:number])  # noqa: TD003
        return "".join(
            random.choice(string.digits) for _ in range(number)
        )  # noqa: S311

    @staticmethod
    def generate_secret_salt() -> str:
        """
        Generate a secret salt consisting of random alphanumeric characters.

        Returns
        -------
            str: The generated secret salt.
        """
        # TODO(Qwizi): replace with secrets (secrets.token_hex(number)[:number])  # noqa: TD003
        return "".join(
            random.choice(string.ascii_letters + string.digits)  # noqa: S311
            for _ in range(32)
        )

    async def resend_activation_code(
        self,
        email: EmailStr,
        code_service: CodeService,
        email_service: EmailService,
    ) -> dict[str, str]:
        """
        Resend the activation code to the specified email address.

        Args:
        ----
            email (EmailStr): The email address to send the activation code to.
            code_service (CodeService): The code service used to create the activation code.
            email_service (EmailService): The email service used to send the activation email.

        Returns:
        -------
            dict: A dictionary with a message indicating that the activation code will be sent if the email is correct.
        """
        msg = {
            "msg": "If email is correct, you will receive an email with activation code",
        }
        try:
            user = await self.users_service.get_one(email=email, is_activated=False)
            code, code_data = await code_service.create(
                data=int(user.id),
                code_len=5,
                expire=900,
            )
            await email_service.send_confirmation_email(
                ActivationEmailTypeEnum.ACCOUNT,
                user.email,
                code,
            )

            logger.info(f"Activation code: {code}")
            return msg  # noqa: TRY300
        except Exception:  # noqa: BLE001
            return msg

    async def activate_user(
        self,
        code: str,
        code_service: CodeService,
    ) -> tuple[bool, User]:
        """
        Activate a user based on the provided activation code.

        Args:
        ----
            code (str): The activation code.
            code_service (CodeService): The code service used to retrieve the user ID.

        Returns:
        -------
            Tuple[bool, Union[bool, User]]: A tuple containing a boolean indicating the success of the activation,
            and either a boolean indicating if the user was already activated or the activated user object.
        """
        user_id = await code_service.get(code)
        if not user_id:
            return False, False
        user = await self.users_service.get_one(id=int(user_id))
        if user.is_activated:
            await code_service.delete(code)
            raise user_activated_exception
        await user.update(is_activated=True)
        return True, user

    async def confirm_change_email(self, code_service: CodeService, code: str) -> User:
        """
        Confirm the change of email for a user.

        Args:
        ----
            code_service (CodeService): The code service used to retrieve the user data.
            code (str): The activation code.

        Returns:
        -------
            User: The updated user object.

        Raises:
        ------
            InvalidActivationCodeException: If the activation code is invalid.
        """
        user_data: dict = await code_service.get(code)
        if not user_data:
            raise invalid_activation_code_exception
        user_id = int(user_data.get("user_id"))
        email = user_data.get("email")
        user = await self.users_service.get_one(id=user_id)
        await user.update(email=email)
        return user

    async def reset_password(
        self,
        reset_password_data: ResetPasswordSchema,
        code_service: CodeService,
    ) -> bool:
        """
        Reset the password for a user.

        Args:
        ----
            reset_password_data (ResetPasswordSchema): The data required to reset the password.
            code_service (CodeService): The service used to validate the activation code.

        Returns:
        -------
            bool: True if the password was successfully reset, False otherwise.
        """
        email = await code_service.get(reset_password_data.code)
        if not email:
            raise invalid_activation_code_exception
        user = await self.users_service.get_one(email=email)
        new_password = get_password_hash(reset_password_data.password)
        await user.update(
            password=new_password,
            secret_salt=self.generate_secret_salt(),
        )
        await code_service.delete(reset_password_data.code)
        return True

    async def get_user_agent(self, request: Request) -> str:
        """
        Get the User-Agent header from the request.

        Args:
        ----
            request (Request): The incoming request.

        Returns:
        -------
            str: The User-Agent header value, or None if not found.
        """
        return request.headers.get("User-Agent", None)
__init__(users_service: UserService, roles_service: RoleService, scopes_service: ScopeService) -> None

Initialize the Auth service.


users_service (UserService): The service for managing users.
roles_service (RoleService): The service for managing roles.
scopes_service (ScopeService): The service for managing scopes.
users_sessions_service (UserSessionService): The service for managing user sessions.
Source code in sharkservers/auth/services/auth.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def __init__(
    self,
    users_service: UserService,
    roles_service: RoleService,
    scopes_service: ScopeService,
) -> None:
    """
    Initialize the Auth service.

    Args:
    ----
        users_service (UserService): The service for managing users.
        roles_service (RoleService): The service for managing roles.
        scopes_service (ScopeService): The service for managing scopes.
        users_sessions_service (UserSessionService): The service for managing user sessions.
    """
    self.users_service = users_service
    self.roles_service = roles_service
    self.scopes_service = scopes_service
authenticate_user(username: str, password: str, user_ip: str | None, user_agent: str | None) -> User | bool async

Authenticates a user based on the provided username and password.


username (str): The username of the user.
password (str): The password of the user.
user_ip (str): The IP address of the user.
user_agent (str): The user agent string of the user's browser.

Union[User, bool]: The authenticated user object if successful, False otherwise.
Source code in sharkservers/auth/services/auth.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
async def authenticate_user(
    self,
    username: str,
    password: str,
    user_ip: str | None,  # noqa: ARG002
    user_agent: str | None,  # noqa: ARG002
) -> User | bool:
    """
    Authenticates a user based on the provided username and password.

    Args:
    ----
        username (str): The username of the user.
        password (str): The password of the user.
        user_ip (str): The IP address of the user.
        user_agent (str): The user agent string of the user's browser.

    Returns:
    -------
        Union[User, bool]: The authenticated user object if successful, False otherwise.
    """  # noqa: D401
    try:
        user: User = await self.users_service.get_one(
            username=username,
            related=["roles", "roles__scopes", "sessions"],
        )
        if not verify_password(password, user.password) or not user.is_activated:
            return False
    except NoMatch:
        return False
    return user
register(user_data: RegisterUserSchema, is_activated: bool = False, is_superuser: bool = False, request: Request = None, settings: Settings = None) -> User async

Register a new user.


user_data (RegisterUserSchema): The user data for registration.
is_activated (bool, optional): Whether the user is activated. Defaults to False.
is_superuser (bool, optional): Whether the user is a superuser. Defaults to False.
request (Request, optional): The request object. Defaults to None.
settings (Settings, optional): The settings object. Defaults to None.

User: The registered user.

user_exists_exception: If the user already exists.
Source code in sharkservers/auth/services/auth.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
async def register(  # noqa: PLR0913
    self,
    user_data: RegisterUserSchema,
    is_activated: bool = False,  # noqa: FBT001, FBT002
    is_superuser: bool = False,  # noqa: FBT001, FBT002
    request: Request = None,  # noqa: ARG002
    settings: Settings = None,
) -> User:
    """
    Register a new user.

    Args:
    ----
        user_data (RegisterUserSchema): The user data for registration.
        is_activated (bool, optional): Whether the user is activated. Defaults to False.
        is_superuser (bool, optional): Whether the user is a superuser. Defaults to False.
        request (Request, optional): The request object. Defaults to None.
        settings (Settings, optional): The settings object. Defaults to None.

    Returns:
    -------
        User: The registered user.

    Raises:
    ------
        user_exists_exception: If the user already exists.
    """
    try:
        password = get_password_hash(user_data.password)
        secret_salt = self.generate_secret_salt()

        user_role = await self.roles_service.get_one(
            tag=ProtectedDefaultRolesTagEnum.USER.value,
        )
        role = user_role
        if is_superuser:
            role = await self.roles_service.get_one(
                tag=ProtectedDefaultRolesTagEnum.ADMIN.value,
            )

        avatar_url = (
            f"{settings.SITE_URL}/static/images/default_avatar.png"
            if settings
            else "http://localhost/static/images/default_avatar.png"
        )
        registered_user = await self.users_service.create(
            username=user_data.username,
            email=user_data.email,
            password=password,
            display_role=role,
            avatar=str(avatar_url),
            secret_salt=secret_salt,
            is_activated=is_activated,
            is_superuser=is_superuser,
        )
        if not is_superuser:
            await registered_user.roles.add(role)
        else:
            await registered_user.roles.add(user_role)
            await registered_user.roles.add(role)
        return registered_user  # noqa: TRY300
    except (
        IntegrityError,
        SQLIntegrityError,
        UniqueViolationError,
        HTTPException,
    ) as err:
        raise user_exists_exception from err
login(form_data: OAuth2PasswordRequestForm, jwt_access_token_service: JWTService, jwt_refresh_token_service: JWTService, user_ip: str | None = None, user_agent: str | None = None) -> tuple[TokenSchema, User] async

Authenticate a user and generates access and refresh tokens.


form_data (OAuth2PasswordRequestForm): The form data containing the username and password.
jwt_access_token_service (JWTService): The service used to encode the access token.
jwt_refresh_token_service (JWTService): The service used to encode the refresh token.
user_ip (str): The IP address of the user.
user_agent (str): The user agent string.

tuple[TokenSchema, User]: A tuple containing the access and refresh tokens, and the authenticated user.
Source code in sharkservers/auth/services/auth.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
async def login(  # noqa: PLR0913
    self,
    form_data: OAuth2PasswordRequestForm,
    jwt_access_token_service: JWTService,
    jwt_refresh_token_service: JWTService,
    user_ip: str | None = None,
    user_agent: str | None = None,
) -> tuple[TokenSchema, User]:
    """
    Authenticate a user and generates access and refresh tokens.

    Args:
    ----
        form_data (OAuth2PasswordRequestForm): The form data containing the username and password.
        jwt_access_token_service (JWTService): The service used to encode the access token.
        jwt_refresh_token_service (JWTService): The service used to encode the refresh token.
        user_ip (str): The IP address of the user.
        user_agent (str): The user agent string.

    Returns:
    -------
        tuple[TokenSchema, User]: A tuple containing the access and refresh tokens, and the authenticated user.
    """
    user = await self.authenticate_user(
        form_data.username,
        form_data.password,
        user_ip=user_ip,
        user_agent=user_agent,
    )
    if not user:
        raise incorrect_username_password_exception

    scopes = await self.scopes_service.get_scopes_list(user.roles)
    access_token, access_token_exp = jwt_access_token_service.encode(
        data={
            "sub": str(user.id),
            "scopes": scopes,
            "secret": user.secret_salt,
        },
    )
    refresh_token, refresh_toke_exp = jwt_refresh_token_service.encode(
        data={"sub": str(user.id), "secret": user.secret_salt},
    )
    await user.update(last_online=now_datetime())
    return (
        TokenSchema(
            access_token=TokenDetailsSchema(
                token=access_token,
                exp=access_token_exp,
                token_type="bearer",  # noqa: S106
            ),
            refresh_token=TokenDetailsSchema(
                token=refresh_token,
                exp=refresh_toke_exp,
                token_type="bearer",  # noqa: S106
            ),
        ),
        user,
    )
create_access_token_from_refresh_token(token_data: RefreshTokenSchema, jwt_access_token_service: JWTService, jwt_refresh_token_service: JWTService) -> tuple[TokenSchema, User] async

Create an access token from a refresh token.


token_data (RefreshTokenSchema): The refresh token data.
jwt_access_token_service (JWTService): The JWT service for access tokens.
jwt_refresh_token_service (JWTService): The JWT service for refresh tokens.

tuple[TokenSchema, User]: A tuple containing the access token schema and the user object.
Source code in sharkservers/auth/services/auth.py
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
async def create_access_token_from_refresh_token(
    self,
    token_data: RefreshTokenSchema,
    jwt_access_token_service: JWTService,
    jwt_refresh_token_service: JWTService,
) -> tuple[TokenSchema, User]:
    """
    Create an access token from a refresh token.

    Args:
    ----
        token_data (RefreshTokenSchema): The refresh token data.
        jwt_access_token_service (JWTService): The JWT service for access tokens.
        jwt_refresh_token_service (JWTService): The JWT service for refresh tokens.

    Returns:
    -------
        tuple[TokenSchema, User]: A tuple containing the access token schema and the user object.
    """
    try:
        payload = jwt_refresh_token_service.decode(token_data.refresh_token)
        refresh_token_exp = payload.get("exp", None)
        # TODO(Qwizi): replace with timezone  # noqa: TD003
        if (
            datetime.fromtimestamp(refresh_token_exp)
            < now_datetime()  # noqa: DTZ006
        ):
            raise token_expired_exception
        user_id = int(payload.get("sub"))
        secret: str = payload.get("secret")
        user = await self.users_service.get_one(
            id=user_id,
            related=["roles", "roles__scopes"],
        )
        if not user or user.secret_salt != secret:
            raise invalid_credentials_exception
        scopes = await self.scopes_service.get_scopes_list(user.roles)
        access_token, access_token_exp = jwt_access_token_service.encode(
            data={
                "sub": str(user.id),
                "scopes": scopes,
                "secret": user.secret_salt,
            },
        )
        # TODO(Qwizi): replace with timezone  # noqa: TD003
        await user.update(last_login=datetime.utcnow())  # noqa: DTZ003
        return (
            TokenSchema(
                access_token=TokenDetailsSchema(
                    token=access_token,
                    exp=access_token_exp,
                    token_type="bearer",  # noqa: S106
                ),
                refresh_token=TokenDetailsSchema(
                    token=token_data.refresh_token,
                    exp=refresh_token_exp,
                    token_type="bearer",  # noqa: S106
                ),
            ),
            user,
        )
    except JWTError as err:
        raise invalid_credentials_exception from err
logout(user: User) -> User async

Log out the specified user by generating a new secret salt and updating it in the user's record.


user (User): The user to log out.

User: The updated user object.
Source code in sharkservers/auth/services/auth.py
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
async def logout(self, user: User) -> User:
    """
    Log out the specified user by generating a new secret salt and updating it in the user's record.

    Args:
    ----
        user (User): The user to log out.

    Returns:
    -------
        User: The updated user object.
    """
    secret = self.generate_secret_salt()
    await user.update(secret_salt=secret)
    return user
generate_code(number: int = 8) -> str staticmethod

Generate a random code consisting of digits.


number (int): The length of the generated code. Default is 8.

str: The generated code.
Source code in sharkservers/auth/services/auth.py
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
@staticmethod
def generate_code(number: int = 8) -> str:
    """
    Generate a random code consisting of digits.

    Args:
    ----
        number (int): The length of the generated code. Default is 8.

    Returns:
    -------
        str: The generated code.
    """
    # TODO(Qwizi): replace with secrets (secrets.token_hex(number)[:number])  # noqa: TD003
    return "".join(
        random.choice(string.digits) for _ in range(number)
    )  # noqa: S311
generate_secret_salt() -> str staticmethod

Generate a secret salt consisting of random alphanumeric characters.

Returns
str: The generated secret salt.
Source code in sharkservers/auth/services/auth.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
@staticmethod
def generate_secret_salt() -> str:
    """
    Generate a secret salt consisting of random alphanumeric characters.

    Returns
    -------
        str: The generated secret salt.
    """
    # TODO(Qwizi): replace with secrets (secrets.token_hex(number)[:number])  # noqa: TD003
    return "".join(
        random.choice(string.ascii_letters + string.digits)  # noqa: S311
        for _ in range(32)
    )
resend_activation_code(email: EmailStr, code_service: CodeService, email_service: EmailService) -> dict[str, str] async

Resend the activation code to the specified email address.


email (EmailStr): The email address to send the activation code to.
code_service (CodeService): The code service used to create the activation code.
email_service (EmailService): The email service used to send the activation email.

dict: A dictionary with a message indicating that the activation code will be sent if the email is correct.
Source code in sharkservers/auth/services/auth.py
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
async def resend_activation_code(
    self,
    email: EmailStr,
    code_service: CodeService,
    email_service: EmailService,
) -> dict[str, str]:
    """
    Resend the activation code to the specified email address.

    Args:
    ----
        email (EmailStr): The email address to send the activation code to.
        code_service (CodeService): The code service used to create the activation code.
        email_service (EmailService): The email service used to send the activation email.

    Returns:
    -------
        dict: A dictionary with a message indicating that the activation code will be sent if the email is correct.
    """
    msg = {
        "msg": "If email is correct, you will receive an email with activation code",
    }
    try:
        user = await self.users_service.get_one(email=email, is_activated=False)
        code, code_data = await code_service.create(
            data=int(user.id),
            code_len=5,
            expire=900,
        )
        await email_service.send_confirmation_email(
            ActivationEmailTypeEnum.ACCOUNT,
            user.email,
            code,
        )

        logger.info(f"Activation code: {code}")
        return msg  # noqa: TRY300
    except Exception:  # noqa: BLE001
        return msg
activate_user(code: str, code_service: CodeService) -> tuple[bool, User] async

Activate a user based on the provided activation code.


code (str): The activation code.
code_service (CodeService): The code service used to retrieve the user ID.

Tuple[bool, Union[bool, User]]: A tuple containing a boolean indicating the success of the activation,
and either a boolean indicating if the user was already activated or the activated user object.
Source code in sharkservers/auth/services/auth.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
async def activate_user(
    self,
    code: str,
    code_service: CodeService,
) -> tuple[bool, User]:
    """
    Activate a user based on the provided activation code.

    Args:
    ----
        code (str): The activation code.
        code_service (CodeService): The code service used to retrieve the user ID.

    Returns:
    -------
        Tuple[bool, Union[bool, User]]: A tuple containing a boolean indicating the success of the activation,
        and either a boolean indicating if the user was already activated or the activated user object.
    """
    user_id = await code_service.get(code)
    if not user_id:
        return False, False
    user = await self.users_service.get_one(id=int(user_id))
    if user.is_activated:
        await code_service.delete(code)
        raise user_activated_exception
    await user.update(is_activated=True)
    return True, user
confirm_change_email(code_service: CodeService, code: str) -> User async

Confirm the change of email for a user.


code_service (CodeService): The code service used to retrieve the user data.
code (str): The activation code.

User: The updated user object.

InvalidActivationCodeException: If the activation code is invalid.
Source code in sharkservers/auth/services/auth.py
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
async def confirm_change_email(self, code_service: CodeService, code: str) -> User:
    """
    Confirm the change of email for a user.

    Args:
    ----
        code_service (CodeService): The code service used to retrieve the user data.
        code (str): The activation code.

    Returns:
    -------
        User: The updated user object.

    Raises:
    ------
        InvalidActivationCodeException: If the activation code is invalid.
    """
    user_data: dict = await code_service.get(code)
    if not user_data:
        raise invalid_activation_code_exception
    user_id = int(user_data.get("user_id"))
    email = user_data.get("email")
    user = await self.users_service.get_one(id=user_id)
    await user.update(email=email)
    return user
reset_password(reset_password_data: ResetPasswordSchema, code_service: CodeService) -> bool async

Reset the password for a user.


reset_password_data (ResetPasswordSchema): The data required to reset the password.
code_service (CodeService): The service used to validate the activation code.

bool: True if the password was successfully reset, False otherwise.
Source code in sharkservers/auth/services/auth.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
async def reset_password(
    self,
    reset_password_data: ResetPasswordSchema,
    code_service: CodeService,
) -> bool:
    """
    Reset the password for a user.

    Args:
    ----
        reset_password_data (ResetPasswordSchema): The data required to reset the password.
        code_service (CodeService): The service used to validate the activation code.

    Returns:
    -------
        bool: True if the password was successfully reset, False otherwise.
    """
    email = await code_service.get(reset_password_data.code)
    if not email:
        raise invalid_activation_code_exception
    user = await self.users_service.get_one(email=email)
    new_password = get_password_hash(reset_password_data.password)
    await user.update(
        password=new_password,
        secret_salt=self.generate_secret_salt(),
    )
    await code_service.delete(reset_password_data.code)
    return True
get_user_agent(request: Request) -> str async

Get the User-Agent header from the request.


request (Request): The incoming request.

str: The User-Agent header value, or None if not found.
Source code in sharkservers/auth/services/auth.py
520
521
522
523
524
525
526
527
528
529
530
531
532
async def get_user_agent(self, request: Request) -> str:
    """
    Get the User-Agent header from the request.

    Args:
    ----
        request (Request): The incoming request.

    Returns:
    -------
        str: The User-Agent header value, or None if not found.
    """
    return request.headers.get("User-Agent", None)

code

Code service.

Code service is a service that allows you to create a code and store it in redis. It is used for example to create a code for email confirmation.

CodeService

Service class for generating and managing codes using Redis.

Attributes
redis (aioredis.Redis): Redis instance for code storage.
key (str): Key prefix for Redis storage.
Methods
generate: Generate a random code.
get_redis_key: Get the Redis key for a given code.
create: Create a code and store it in Redis.
get: Get the data associated with a code from Redis.
delete: Delete a code and its associated data from Redis.
Source code in sharkservers/auth/services/code.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
class CodeService:
    """
    Service class for generating and managing codes using Redis.

    Attributes
    ----------
        redis (aioredis.Redis): Redis instance for code storage.
        key (str): Key prefix for Redis storage.

    Methods
    -------
        generate: Generate a random code.
        get_redis_key: Get the Redis key for a given code.
        create: Create a code and store it in Redis.
        get: Get the data associated with a code from Redis.
        delete: Delete a code and its associated data from Redis.
    """

    redis: aioredis.Redis
    key: str

    def __init__(self, redis: aioredis.Redis, key: str) -> None:
        """Initialize the CodeService."""
        self.redis = redis
        self.key = key

    @staticmethod
    def generate(number: int = 8) -> str:
        """
        Generate a random string of digits.

        Args:
        ----
            number (int): The length of the generated string. Default is 8.

        Returns:
        -------
            str: A random string of digits.
        """
        return "".join(
            random.choice(string.digits) for _ in range(number)
        )  # noqa: S311

    def get_redis_key(self, code: str) -> str:
        """
        Return the Redis key for the given code.

        Args:
        ----
            code (str): The code to generate the Redis key for.

        Returns:
        -------
            str: The Redis key.
        """
        return f"{self.key}:{code}"

    async def create(
        self,
        data: any,
        code_len: int = 8,
        expire: int = 60 * 60,
    ) -> (str, str):
        """
        Create a code and stores it in Redis with the provided data.

        Args:
        ----
            data (any): The data to be stored along with the code.
            code_len (int, optional): The length of the generated code. Defaults to 8.
            expire (int, optional): The expiration time of the code in seconds. Defaults to 3600.

        Returns:
        -------
            Tuple[str, str]: A tuple containing the code key and the stored data.
        """
        code = self.generate(number=code_len)
        redis_key = self.get_redis_key(code)
        if await self.redis.exists(redis_key):
            await self.redis.delete(redis_key)
        await self.redis.set(redis_key, data)
        await self.redis.expire(redis_key, expire)
        return redis_key.split(":")[1], await self.redis.get(redis_key)

    async def get(self, code: str) -> str:
        """
        Retrieves the value associated with the given code from Redis.

        Args:
        ----
            code (str): The code to retrieve the value for.

        Returns:
        -------
            str: The value associated with the code.
        """  # noqa: D401
        return await self.redis.get(self.get_redis_key(code))

    async def delete(self, code: str) -> bool:
        """
        Delete a code from the Redis cache.

        Args:
        ----
            code (str): The code to be deleted.

        Returns:
        -------
            bool: True if the code was successfully deleted, False otherwise.
        """
        return await self.redis.delete(self.get_redis_key(code))
__init__(redis: aioredis.Redis, key: str) -> None

Initialize the CodeService.

Source code in sharkservers/auth/services/code.py
34
35
36
37
def __init__(self, redis: aioredis.Redis, key: str) -> None:
    """Initialize the CodeService."""
    self.redis = redis
    self.key = key
generate(number: int = 8) -> str staticmethod

Generate a random string of digits.


number (int): The length of the generated string. Default is 8.

str: A random string of digits.
Source code in sharkservers/auth/services/code.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@staticmethod
def generate(number: int = 8) -> str:
    """
    Generate a random string of digits.

    Args:
    ----
        number (int): The length of the generated string. Default is 8.

    Returns:
    -------
        str: A random string of digits.
    """
    return "".join(
        random.choice(string.digits) for _ in range(number)
    )  # noqa: S311
get_redis_key(code: str) -> str

Return the Redis key for the given code.


code (str): The code to generate the Redis key for.

str: The Redis key.
Source code in sharkservers/auth/services/code.py
56
57
58
59
60
61
62
63
64
65
66
67
68
def get_redis_key(self, code: str) -> str:
    """
    Return the Redis key for the given code.

    Args:
    ----
        code (str): The code to generate the Redis key for.

    Returns:
    -------
        str: The Redis key.
    """
    return f"{self.key}:{code}"
create(data: any, code_len: int = 8, expire: int = 60 * 60) -> (str, str) async

Create a code and stores it in Redis with the provided data.


data (any): The data to be stored along with the code.
code_len (int, optional): The length of the generated code. Defaults to 8.
expire (int, optional): The expiration time of the code in seconds. Defaults to 3600.

Tuple[str, str]: A tuple containing the code key and the stored data.
Source code in sharkservers/auth/services/code.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
async def create(
    self,
    data: any,
    code_len: int = 8,
    expire: int = 60 * 60,
) -> (str, str):
    """
    Create a code and stores it in Redis with the provided data.

    Args:
    ----
        data (any): The data to be stored along with the code.
        code_len (int, optional): The length of the generated code. Defaults to 8.
        expire (int, optional): The expiration time of the code in seconds. Defaults to 3600.

    Returns:
    -------
        Tuple[str, str]: A tuple containing the code key and the stored data.
    """
    code = self.generate(number=code_len)
    redis_key = self.get_redis_key(code)
    if await self.redis.exists(redis_key):
        await self.redis.delete(redis_key)
    await self.redis.set(redis_key, data)
    await self.redis.expire(redis_key, expire)
    return redis_key.split(":")[1], await self.redis.get(redis_key)
get(code: str) -> str async

Retrieves the value associated with the given code from Redis.


code (str): The code to retrieve the value for.

str: The value associated with the code.
Source code in sharkservers/auth/services/code.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
async def get(self, code: str) -> str:
    """
    Retrieves the value associated with the given code from Redis.

    Args:
    ----
        code (str): The code to retrieve the value for.

    Returns:
    -------
        str: The value associated with the code.
    """  # noqa: D401
    return await self.redis.get(self.get_redis_key(code))
delete(code: str) -> bool async

Delete a code from the Redis cache.


code (str): The code to be deleted.

bool: True if the code was successfully deleted, False otherwise.
Source code in sharkservers/auth/services/code.py
111
112
113
114
115
116
117
118
119
120
121
122
123
async def delete(self, code: str) -> bool:
    """
    Delete a code from the Redis cache.

    Args:
    ----
        code (str): The code to be deleted.

    Returns:
    -------
        bool: True if the code was successfully deleted, False otherwise.
    """
    return await self.redis.delete(self.get_redis_key(code))

jwt

JWT Service.

This module contains JWTService class which is responsible for encoding and decoding JWT tokens.

JWTService

Service class for encoding and decoding JSON Web Tokens (JWT).

Attributes
secret_key (str): The secret key used to encode and decode the JWT.
algorithm (str): The algorithm used to encode and decode the JWT.
expires_delta (timedelta): The expiration delta for the JWT.
Methods
encode: Encodes the given data into a JWT.
decode: Decodes the given JWT.
decode_token: Decodes the given JWT and returns the token data schema.
Source code in sharkservers/auth/services/jwt.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
class JWTService:
    """
    Service class for encoding and decoding JSON Web Tokens (JWT).

    Attributes
    ----------
        secret_key (str): The secret key used to encode and decode the JWT.
        algorithm (str): The algorithm used to encode and decode the JWT.
        expires_delta (timedelta): The expiration delta for the JWT.

    Methods
    -------
        encode: Encodes the given data into a JWT.
        decode: Decodes the given JWT.
        decode_token: Decodes the given JWT and returns the token data schema.
    """

    def __init__(
        self,
        secret_key: str,
        algorithm: str = "HS512",
        expires_delta: timedelta = timedelta(minutes=15),
    ) -> None:
        """Initialize the JWTService."""
        self.secret_key = secret_key
        self.algorithm = algorithm
        self.expires_delta = expires_delta

    def encode(self, data: dict) -> (str, datetime):
        """
        Encodes the given data into a JWT.

        Args:
        ----
            data (dict): The data to be encoded.

        Returns:
        -------
            tuple: A tuple containing the encoded JWT and the expiration datetime.
        """  # noqa: D401
        to_encode = data.copy()
        expire = now_datetime() + self.expires_delta
        expire_with_timezone = expire.replace(tzinfo=ZoneInfo("Europe/Warsaw"))
        to_encode.update({"exp": expire_with_timezone})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt, expire

    def decode(self, token: str) -> dict:
        """
        Decodes the given JWT.

        Args:
        ----
            token (str): The JWT to be decoded.

        Returns:
        -------
            dict: The decoded JWT payload.
        """  # noqa: D401
        return jwt.decode(token, self.secret_key, algorithms=[self.algorithm])

    def decode_token(self, token: str) -> TokenDataSchema:
        """
        Decodes the given JWT and returns the token data schema.

        Args:
        ----
            token (str): The JWT to be decoded.

        Returns:
        -------
            TokenDataSchema: The decoded token data schema.

        Raises:
        ------
            invalid_credentials_exception: If the JWT is invalid or missing required data.
        """  # noqa: D401
        try:
            payload = self.decode(token)
            user_id: str = payload.get("sub")
            if user_id is None:
                raise invalid_credentials_exception
            token_scopes = payload.get("scopes", [])
            secret = payload.get("secret")
            session_id = payload.get("session_id")

            return TokenDataSchema(
                user_id=int(user_id),
                scopes=token_scopes,
                secret=secret,
                session_id=session_id,
            )
        except JWTError as err:
            raise invalid_credentials_exception from err
__init__(secret_key: str, algorithm: str = 'HS512', expires_delta: timedelta = timedelta(minutes=15)) -> None

Initialize the JWTService.

Source code in sharkservers/auth/services/jwt.py
34
35
36
37
38
39
40
41
42
43
def __init__(
    self,
    secret_key: str,
    algorithm: str = "HS512",
    expires_delta: timedelta = timedelta(minutes=15),
) -> None:
    """Initialize the JWTService."""
    self.secret_key = secret_key
    self.algorithm = algorithm
    self.expires_delta = expires_delta
encode(data: dict) -> (str, datetime)

Encodes the given data into a JWT.


data (dict): The data to be encoded.

tuple: A tuple containing the encoded JWT and the expiration datetime.
Source code in sharkservers/auth/services/jwt.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def encode(self, data: dict) -> (str, datetime):
    """
    Encodes the given data into a JWT.

    Args:
    ----
        data (dict): The data to be encoded.

    Returns:
    -------
        tuple: A tuple containing the encoded JWT and the expiration datetime.
    """  # noqa: D401
    to_encode = data.copy()
    expire = now_datetime() + self.expires_delta
    expire_with_timezone = expire.replace(tzinfo=ZoneInfo("Europe/Warsaw"))
    to_encode.update({"exp": expire_with_timezone})
    encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
    return encoded_jwt, expire
decode(token: str) -> dict

Decodes the given JWT.


token (str): The JWT to be decoded.

dict: The decoded JWT payload.
Source code in sharkservers/auth/services/jwt.py
64
65
66
67
68
69
70
71
72
73
74
75
76
def decode(self, token: str) -> dict:
    """
    Decodes the given JWT.

    Args:
    ----
        token (str): The JWT to be decoded.

    Returns:
    -------
        dict: The decoded JWT payload.
    """  # noqa: D401
    return jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
decode_token(token: str) -> TokenDataSchema

Decodes the given JWT and returns the token data schema.


token (str): The JWT to be decoded.

TokenDataSchema: The decoded token data schema.

invalid_credentials_exception: If the JWT is invalid or missing required data.
Source code in sharkservers/auth/services/jwt.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def decode_token(self, token: str) -> TokenDataSchema:
    """
    Decodes the given JWT and returns the token data schema.

    Args:
    ----
        token (str): The JWT to be decoded.

    Returns:
    -------
        TokenDataSchema: The decoded token data schema.

    Raises:
    ------
        invalid_credentials_exception: If the JWT is invalid or missing required data.
    """  # noqa: D401
    try:
        payload = self.decode(token)
        user_id: str = payload.get("sub")
        if user_id is None:
            raise invalid_credentials_exception
        token_scopes = payload.get("scopes", [])
        secret = payload.get("secret")
        session_id = payload.get("session_id")

        return TokenDataSchema(
            user_id=int(user_id),
            scopes=token_scopes,
            secret=secret,
            session_id=session_id,
        )
    except JWTError as err:
        raise invalid_credentials_exception from err

steam

Steam auth service.

Steam auth service is a service that allows you to authenticate a user using Steam OpenID.


SteamAuthService: Service class for authenticating users using Steam OpenID.
SteamAuthService

Service class for handling Steam authentication.

Attributes
users_service (UserService): The user service.
players_service (PlayerService): The player service.
auth_url (str): The Steam authentication URL.
Methods
get_steamid_from_url: Get the Steam ID from a Steam profile URL.
format_params: Format the Steam authentication parameters into a dict.
is_valid_params: Check if the provided Steam authentication parameters are valid.
authenticate: Authenticate a user using Steam authentication.
Source code in sharkservers/auth/services/steam.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
class SteamAuthService:
    """
    Service class for handling Steam authentication.

    Attributes
    ----------
        users_service (UserService): The user service.
        players_service (PlayerService): The player service.
        auth_url (str): The Steam authentication URL.

    Methods
    -------
        get_steamid_from_url: Get the Steam ID from a Steam profile URL.
        format_params: Format the Steam authentication parameters into a dict.
        is_valid_params: Check if the provided Steam authentication parameters are valid.
        authenticate: Authenticate a user using Steam authentication.
    """

    def __init__(
        self,
        users_service: UserService,
        players_service: PlayerService,
    ) -> None:
        """Initialize the SteamAuthService."""
        self.users_service = users_service
        self.players_service = players_service
        self.auth_url = "https://steamcommunity.com/openid/login"

    @staticmethod
    def get_steamid_from_url(url: str) -> str:
        """
        Extracts the SteamID from a given Steam profile URL.

        Args:
        ----
            url (str): The Steam profile URL.

        Returns:
        -------
            str: The extracted SteamID.
        """  # noqa: D401
        return url.split("/")[-1]

    def format_params(self, params: SteamAuthSchema) -> dict:
        """
        Format the Steam authentication parameters into a dict.

        Args:
        ----
            params (SteamAuthSchema): The Steam authentication parameters.

        Returns:
        -------
            dict: The formatted parameters.
        """
        params_dict = {}
        params_dict["openid.ns"] = params.openid_ns
        params_dict["openid.mode"] = params.openid_mode
        params_dict["openid.op_endpoint"] = params.openid_op_endpoint
        params_dict["openid.claimed_id"] = params.openid_claimed_id
        params_dict["openid.identity"] = params.openid_identity
        params_dict["openid.return_to"] = params.openid_return_to
        params_dict["openid.response_nonce"] = params.openid_response_nonce
        params_dict["openid.assoc_handle"] = params.openid_assoc_handle
        params_dict["openid.signed"] = params.openid_signed
        params_dict["openid.sig"] = params.openid_sig
        return params_dict

    async def is_valid_params(self, params: SteamAuthSchema) -> bool:
        """
        Check if the provided Steam authentication parameters are valid.

        Args:
        ----
            params (SteamAuthSchema): The Steam authentication parameters.

        Returns:
        -------
            bool: True if the parameters are valid, False otherwise.
        """
        params_copy = params.copy()
        params_copy.openid_mode = "check_authentication"
        formatted_params = self.format_params(params_copy)
        async with httpx.AsyncClient() as client:
            response = await client.post(url=self.auth_url, data=formatted_params)
            return "is_valid:true" in response.text

    async def authenticate(self, user: User, params: SteamAuthSchema) -> Player:
        """
        Authenticate a user using Steam authentication.

        Args:
        ----
            user (User): The user to authenticate.
            params (SteamAuthSchema): The Steam authentication parameters.

        Returns:
        -------
            Player: The authenticated player.

        Raises:
        ------
            HTTPException: If the Steam profile cannot be authenticated or if the user is already connected to a profile.
        """
        if not await self.is_valid_params(params):
            raise HTTPException(400, "Cannot authenticate steam profile")
        steamid64 = self.get_steamid_from_url(params.openid_claimed_id)

        # check if player exists
        player_exists = await self.players_service.Meta.model.objects.filter(
            steamid64=steamid64,
        ).exists()

        # if player exist check if user and player are connected
        if player_exists:
            if user.player:
                raise HTTPException(400, "User have already connected profile")

            player = await self.players_service.Meta.model.objects.get(
                steamid64=steamid64,
            )
            await user.update(player=user)
            return player

        new_player = await self.players_service.create_player(steamid64=steamid64)
        await user.update(player=new_player)
        return new_player
__init__(users_service: UserService, players_service: PlayerService) -> None

Initialize the SteamAuthService.

Source code in sharkservers/auth/services/steam.py
40
41
42
43
44
45
46
47
48
def __init__(
    self,
    users_service: UserService,
    players_service: PlayerService,
) -> None:
    """Initialize the SteamAuthService."""
    self.users_service = users_service
    self.players_service = players_service
    self.auth_url = "https://steamcommunity.com/openid/login"
get_steamid_from_url(url: str) -> str staticmethod

Extracts the SteamID from a given Steam profile URL.


url (str): The Steam profile URL.

str: The extracted SteamID.
Source code in sharkservers/auth/services/steam.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@staticmethod
def get_steamid_from_url(url: str) -> str:
    """
    Extracts the SteamID from a given Steam profile URL.

    Args:
    ----
        url (str): The Steam profile URL.

    Returns:
    -------
        str: The extracted SteamID.
    """  # noqa: D401
    return url.split("/")[-1]
format_params(params: SteamAuthSchema) -> dict

Format the Steam authentication parameters into a dict.


params (SteamAuthSchema): The Steam authentication parameters.

dict: The formatted parameters.
Source code in sharkservers/auth/services/steam.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def format_params(self, params: SteamAuthSchema) -> dict:
    """
    Format the Steam authentication parameters into a dict.

    Args:
    ----
        params (SteamAuthSchema): The Steam authentication parameters.

    Returns:
    -------
        dict: The formatted parameters.
    """
    params_dict = {}
    params_dict["openid.ns"] = params.openid_ns
    params_dict["openid.mode"] = params.openid_mode
    params_dict["openid.op_endpoint"] = params.openid_op_endpoint
    params_dict["openid.claimed_id"] = params.openid_claimed_id
    params_dict["openid.identity"] = params.openid_identity
    params_dict["openid.return_to"] = params.openid_return_to
    params_dict["openid.response_nonce"] = params.openid_response_nonce
    params_dict["openid.assoc_handle"] = params.openid_assoc_handle
    params_dict["openid.signed"] = params.openid_signed
    params_dict["openid.sig"] = params.openid_sig
    return params_dict
is_valid_params(params: SteamAuthSchema) -> bool async

Check if the provided Steam authentication parameters are valid.


params (SteamAuthSchema): The Steam authentication parameters.

bool: True if the parameters are valid, False otherwise.
Source code in sharkservers/auth/services/steam.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
async def is_valid_params(self, params: SteamAuthSchema) -> bool:
    """
    Check if the provided Steam authentication parameters are valid.

    Args:
    ----
        params (SteamAuthSchema): The Steam authentication parameters.

    Returns:
    -------
        bool: True if the parameters are valid, False otherwise.
    """
    params_copy = params.copy()
    params_copy.openid_mode = "check_authentication"
    formatted_params = self.format_params(params_copy)
    async with httpx.AsyncClient() as client:
        response = await client.post(url=self.auth_url, data=formatted_params)
        return "is_valid:true" in response.text
authenticate(user: User, params: SteamAuthSchema) -> Player async

Authenticate a user using Steam authentication.


user (User): The user to authenticate.
params (SteamAuthSchema): The Steam authentication parameters.

Player: The authenticated player.

HTTPException: If the Steam profile cannot be authenticated or if the user is already connected to a profile.
Source code in sharkservers/auth/services/steam.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
async def authenticate(self, user: User, params: SteamAuthSchema) -> Player:
    """
    Authenticate a user using Steam authentication.

    Args:
    ----
        user (User): The user to authenticate.
        params (SteamAuthSchema): The Steam authentication parameters.

    Returns:
    -------
        Player: The authenticated player.

    Raises:
    ------
        HTTPException: If the Steam profile cannot be authenticated or if the user is already connected to a profile.
    """
    if not await self.is_valid_params(params):
        raise HTTPException(400, "Cannot authenticate steam profile")
    steamid64 = self.get_steamid_from_url(params.openid_claimed_id)

    # check if player exists
    player_exists = await self.players_service.Meta.model.objects.filter(
        steamid64=steamid64,
    ).exists()

    # if player exist check if user and player are connected
    if player_exists:
        if user.player:
            raise HTTPException(400, "User have already connected profile")

        player = await self.players_service.Meta.model.objects.get(
            steamid64=steamid64,
        )
        await user.update(player=user)
        return player

    new_player = await self.players_service.create_player(steamid64=steamid64)
    await user.update(player=new_player)
    return new_player

utils

Utils for auth module.

Functions: - verify_password: Verifies a password. - get_password_hash: Hashes a password. - now_datetime: Returns the current datetime.

verify_password(plain_password: str, hashed_password: str) -> bool

Verify if a plain password matches a hashed password.


plain_password (str): The plain password to verify.
hashed_password (str): The hashed password to compare against.

bool: True if the plain password matches the hashed password, False otherwise.
Source code in sharkservers/auth/utils.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    Verify if a plain password matches a hashed password.

    Args:
    ----
        plain_password (str): The plain password to verify.
        hashed_password (str): The hashed password to compare against.

    Returns:
    -------
        bool: True if the plain password matches the hashed password, False otherwise.
    """
    return pwd_context.verify(plain_password, hashed_password)

get_password_hash(plain_password: str) -> str

Hashes the given plain password using the pwd_context.


plain_password (str): The plain password to be hashed.

str: The hashed password.
Source code in sharkservers/auth/utils.py
39
40
41
42
43
44
45
46
47
48
49
50
51
def get_password_hash(plain_password: str) -> str:
    """
    Hashes the given plain password using the pwd_context.

    Args:
    ----
        plain_password (str): The plain password to be hashed.

    Returns:
    -------
        str: The hashed password.
    """
    return pwd_context.hash(plain_password)

now_datetime() -> datetime

Return the current datetime in the timezone "Europe/Warsaw".

Returns
datetime: The current datetime without timezone information.
Source code in sharkservers/auth/utils.py
54
55
56
57
58
59
60
61
62
def now_datetime() -> datetime:
    """
    Return the current datetime in the timezone "Europe/Warsaw".

    Returns
    -------
        datetime: The current datetime without timezone information.
    """
    return datetime.now(tz=ZoneInfo("Europe/Warsaw")).replace(tzinfo=None)

views

Module contains the API endpoints related to authentication.

register(user_data: RegisterUserSchema, background_tasks: BackgroundTasks, request: Request, auth_service: AuthService = Depends(get_auth_service), code_service: CodeService = Depends(get_activation_account_code_service), email_service: EmailService = Depends(get_email_service), settings: Settings = Depends(get_settings)) -> UserOut async

Register a new user.


user_data (RegisterUserSchema): The user data to register.
background_tasks (BackgroundTasks): The background tasks object.
request (Request): The request object.
auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).
code_service (CodeService, optional): The code service. Defaults to Depends(get_activation_account_code_service).
email_service (EmailService, optional): The email service. Defaults to Depends(get_email_service).
settings (Settings, optional): The settings object. Defaults to Depends(get_settings).

UserOut: The registered user.
Source code in sharkservers/auth/views.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@router.post("/register", dependencies=[Depends(limiter)])
async def register(  # noqa: PLR0913
    user_data: RegisterUserSchema,
    background_tasks: BackgroundTasks,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
    code_service: CodeService = Depends(
        get_activation_account_code_service,
    ),
    email_service: EmailService = Depends(get_email_service),
    settings: Settings = Depends(get_settings),
) -> UserOut:
    """
    Register a new user.

    Args:
    ----
        user_data (RegisterUserSchema): The user data to register.
        background_tasks (BackgroundTasks): The background tasks object.
        request (Request): The request object.
        auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).
        code_service (CodeService, optional): The code service. Defaults to Depends(get_activation_account_code_service).
        email_service (EmailService, optional): The email service. Defaults to Depends(get_email_service).
        settings (Settings, optional): The settings object. Defaults to Depends(get_settings).

    Returns:
    -------
        UserOut: The registered user.
    """
    registered_user: User = await auth_service.register(
        user_data=user_data,
        request=request,
        settings=settings,
    )
    activation_code, _user_id = await code_service.create(
        data=registered_user.id,
        code_len=5,
        expire=900,
    )
    logger.info(f"Activation code: {activation_code}")
    # Send activation email only if not testing
    if not settings.TESTING:
        background_tasks.add_task(
            email_service.send_confirmation_email,
            ActivationEmailTypeEnum.ACCOUNT,
            registered_user.email,
            activation_code,
        )
    return registered_user

login_user(request: Request, form_data: OAuth2PasswordRequestForm = Depends(), access_token_service: JWTService = Depends(get_access_token_service), refresh_token_service: JWTService = Depends(get_refresh_token_service), auth_service: AuthService = Depends(get_auth_service)) -> TokenSchema async

Log in a user and returns a token.


request (Request): The incoming request object.
form_data (OAuth2PasswordRequestForm, optional): The form data containing the user's credentials. Defaults to Depends().
access_token_service (JWTService, optional): The service for generating access tokens. Defaults to Depends(get_access_token_service).
refresh_token_service (JWTService, optional): The service for generating refresh tokens. Defaults to Depends(get_refresh_token_service).
auth_service (AuthService, optional): The service for handling authentication. Defaults to Depends(get_auth_service).

TokenSchema: The token schema containing the access token.
Source code in sharkservers/auth/views.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@router.post("/token", dependencies=[Depends(limiter)])
async def login_user(
    request: Request,
    form_data: OAuth2PasswordRequestForm = Depends(),
    access_token_service: JWTService = Depends(get_access_token_service),
    refresh_token_service: JWTService = Depends(
        get_refresh_token_service,
    ),
    auth_service: AuthService = Depends(get_auth_service),
) -> TokenSchema:
    """
    Log in a user and returns a token.

    Args:
    ----
        request (Request): The incoming request object.
        form_data (OAuth2PasswordRequestForm, optional): The form data containing the user's credentials. Defaults to Depends().
        access_token_service (JWTService, optional): The service for generating access tokens. Defaults to Depends(get_access_token_service).
        refresh_token_service (JWTService, optional): The service for generating refresh tokens. Defaults to Depends(get_refresh_token_service).
        auth_service (AuthService, optional): The service for handling authentication. Defaults to Depends(get_auth_service).

    Returns:
    -------
        TokenSchema: The token schema containing the access token.
    """
    user_ip = request.client.host
    user_agent = request.headers.get("User-Agent", None)
    token, user = await auth_service.login(
        form_data,
        jwt_access_token_service=access_token_service,
        jwt_refresh_token_service=refresh_token_service,
        user_ip=user_ip,
        user_agent=user_agent,
    )
    return token

get_access_token_from_refresh_token(token_data: RefreshTokenSchema, access_token_service: JWTService = Depends(get_access_token_service), refresh_token_service: JWTService = Depends(get_refresh_token_service), auth_service: AuthService = Depends(get_auth_service)) -> TokenSchema async

Retrieve an access token from a refresh token.


token_data (RefreshTokenSchema): The refresh token data.
access_token_service (JWTService, optional): The access token service. Defaults to Depends(get_access_token_service).
refresh_token_service (JWTService, optional): The refresh token service. Defaults to Depends(get_refresh_token_service).
auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).

TokenSchema: The access token.
Source code in sharkservers/auth/views.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@router.post("/token/refresh")
async def get_access_token_from_refresh_token(
    token_data: RefreshTokenSchema,
    access_token_service: JWTService = Depends(get_access_token_service),
    refresh_token_service: JWTService = Depends(
        get_refresh_token_service,
    ),
    auth_service: AuthService = Depends(get_auth_service),
) -> TokenSchema:
    """
    Retrieve an access token from a refresh token.

    Args:
    ----
        token_data (RefreshTokenSchema): The refresh token data.
        access_token_service (JWTService, optional): The access token service. Defaults to Depends(get_access_token_service).
        refresh_token_service (JWTService, optional): The refresh token service. Defaults to Depends(get_refresh_token_service).
        auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).

    Returns:
    -------
        TokenSchema: The access token.
    """
    token, user = await auth_service.create_access_token_from_refresh_token(
        token_data,
        jwt_access_token_service=access_token_service,
        jwt_refresh_token_service=refresh_token_service,
    )
    return token

logout_user(user: User = Depends(get_current_active_user), auth_service: AuthService = Depends(get_auth_service)) -> UserOut async

Log out the user.


user (User): The user to be logged out.
auth_service (AuthService): The authentication service.

UserOut: The logged out user.
Source code in sharkservers/auth/views.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
@router.post("/logout")
async def logout_user(
    user: User = Depends(get_current_active_user),
    auth_service: AuthService = Depends(get_auth_service),
) -> UserOut:
    """
    Log out the user.

    Args:
    ----
        user (User): The user to be logged out.
        auth_service (AuthService): The authentication service.

    Returns:
    -------
        UserOut: The logged out user.
    """
    return await auth_service.logout(user)

activate_user(activate_code_data: ActivateUserCodeSchema, auth_service: AuthService = Depends(get_auth_service), activate_code_service: CodeService = Depends(get_activation_account_code_service)) -> UserActivatedSchema async

Activate a user account using the provided activation code.


activate_code_data (ActivateUserCodeSchema): The activation code data.
auth_service (AuthService): The authentication service.
activate_code_service (CodeService): The activation code service.

UserActivatedSchema: The activated user data.

invalid_activation_code_exception: If the activation code is invalid.
Source code in sharkservers/auth/views.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
@router.post("/activate", dependencies=[Depends(limiter)])
async def activate_user(
    activate_code_data: ActivateUserCodeSchema,
    auth_service: AuthService = Depends(get_auth_service),
    activate_code_service: CodeService = Depends(
        get_activation_account_code_service,
    ),
) -> UserActivatedSchema:
    """
    Activate a user account using the provided activation code.

    Args:
    ----
        activate_code_data (ActivateUserCodeSchema): The activation code data.
        auth_service (AuthService): The authentication service.
        activate_code_service (CodeService): The activation code service.

    Returns:
    -------
        UserActivatedSchema: The activated user data.

    Raises:
    ------
        invalid_activation_code_exception: If the activation code is invalid.
    """
    user_activated, user = await auth_service.activate_user(
        code=activate_code_data.code,
        code_service=activate_code_service,
    )
    if not user_activated:
        raise invalid_activation_code_exception
    return user

resend_activate_code(data: ResendActivationCodeSchema, background_tasks: BackgroundTasks, auth_service: AuthService = Depends(get_auth_service), code_service: CodeService = Depends(get_activation_account_code_service), email_service: EmailService = Depends(get_email_service)) -> dict[str, str] async

Resends the activation code to the specified email address.


data (ResendActivationCodeSchema): The data containing the email address.
background_tasks (BackgroundTasks): The background tasks manager.
auth_service (AuthService): The authentication service.
code_service (CodeService): The activation code service.
email_service (EmailService): The email service.

dict[str, str]: A dictionary with a message indicating if the email is correct and an email with the activation code will be sent.
Source code in sharkservers/auth/views.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
@router.post("/activate/resend", dependencies=[Depends(limiter)])
async def resend_activate_code(
    data: ResendActivationCodeSchema,
    background_tasks: BackgroundTasks,
    auth_service: AuthService = Depends(get_auth_service),
    code_service: CodeService = Depends(
        get_activation_account_code_service,
    ),
    email_service: EmailService = Depends(get_email_service),
) -> dict[str, str]:
    """
    Resends the activation code to the specified email address.

    Args:
    ----
        data (ResendActivationCodeSchema): The data containing the email address.
        background_tasks (BackgroundTasks): The background tasks manager.
        auth_service (AuthService): The authentication service.
        code_service (CodeService): The activation code service.
        email_service (EmailService): The email service.

    Returns:
    -------
        dict[str, str]: A dictionary with a message indicating if the email is correct and an email with the activation code will be sent.
    """
    background_tasks.add_task(
        auth_service.resend_activation_code,
        data.email,
        code_service=code_service,
        email_service=email_service,
    )
    return {
        "msg": "If email is correct, you will receive an email with activation code",
    }

forgot_password_request(data: ResendActivationCodeSchema, background_tasks: BackgroundTasks, email_service: EmailService = Depends(get_email_service), code_service: CodeService = Depends(get_reset_account_password_code_service)) -> dict[str, str] async

Send a request to reset the account password.


data (ResendActivationCodeSchema): The data containing the email address.
background_tasks (BackgroundTasks): The background tasks manager.
auth_service (AuthService): The authentication service.
email_service (EmailService): The email service.
code_service (CodeService): The code service for resetting the account password.

dict[str, str]: A dictionary with a message indicating that an email with the reset code will be sent if the email is correct.
Source code in sharkservers/auth/views.py
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
@router.post("/forgot-password", dependencies=[Depends(limiter)])
async def forgot_password_request(
    data: ResendActivationCodeSchema,
    background_tasks: BackgroundTasks,
    email_service: EmailService = Depends(get_email_service),
    code_service: CodeService = Depends(
        get_reset_account_password_code_service,
    ),
) -> dict[str, str]:
    """
    Send a request to reset the account password.

    Args:
    ----
        data (ResendActivationCodeSchema): The data containing the email address.
        background_tasks (BackgroundTasks): The background tasks manager.
        auth_service (AuthService): The authentication service.
        email_service (EmailService): The email service.
        code_service (CodeService): The code service for resetting the account password.

    Returns:
    -------
        dict[str, str]: A dictionary with a message indicating that an email with the reset code will be sent if the email is correct.
    """
    code, _data = await code_service.create(data.email, code_len=5, expire=900)
    background_tasks.add_task(
        email_service.send_confirmation_email,
        ActivationEmailTypeEnum.PASSWORD,
        data.email,
        code,
    )
    return {"msg": "If email is correct, you will receive an email with reset code"}

reset_password(data: ResetPasswordSchema, auth_service: AuthService = Depends(get_auth_service), code_service: CodeService = Depends(get_reset_account_password_code_service)) -> dict async

Reset the password for a user account.


data (ResetPasswordSchema): The data containing the password reset information.
auth_service (AuthService): The authentication service.
code_service (CodeService): The code service for resetting the account password.

dict: A dictionary with a message indicating that the password has been reset.
Source code in sharkservers/auth/views.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@router.post("/reset-password", dependencies=[Depends(limiter)])
async def reset_password(
    data: ResetPasswordSchema,
    auth_service: AuthService = Depends(get_auth_service),
    code_service: CodeService = Depends(
        get_reset_account_password_code_service,
    ),
) -> dict:
    """
    Reset the password for a user account.

    Args:
    ----
        data (ResetPasswordSchema): The data containing the password reset information.
        auth_service (AuthService): The authentication service.
        code_service (CodeService): The code service for resetting the account password.

    Returns:
    -------
        dict: A dictionary with a message indicating that the password has been reset.
    """
    await auth_service.reset_password(
        reset_password_data=data,
        code_service=code_service,
    )
    return {"msg": "Password has been reset"}

chat

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

bot

dependencies

enums

ChatEventsEnum

Bases: str, Enum

RChat events enum.

Source code in sharkservers/chat/enums.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
class ChatEventsEnum(str, Enum):
    """RChat events enum."""

    GET_ALL = "CHAT_GET_ALL"
    GET_ONE = "CHAT_GET_ONE"
    CREATE = "CHAT_CREATE"
    UPDATE = "CHAT_UPDATE"
    DELETE = "CHAT_DELETE"
    ADMIN_GET_ALL = "CHAT_ADMIN_GET_ALL"
    ADMIN_GET_ONE = "CHAT_ADMIN_GET_ONE"
    ADMIN_CREATE = "CHAT_ADMIN_CREATE"
    ADMIN_UPDATE = "CHAT_ADMIN_UPDATE"
    ADMIN_DELETE = "CHAT_ADMIN_DELETE"

models

services

websocket

db

Module contains database-related functionality for the SharkServers API.

It defines the database connection, models, and base service class.

BaseMeta

Bases: ModelMeta

Meta class for the BaseMeta class.

This class defines the metadata and database attributes for ormar models.

Source code in sharkservers/db.py
61
62
63
64
65
66
67
68
69
class BaseMeta(ormar.ModelMeta):
    """
    Meta class for the BaseMeta class.

    This class defines the metadata and database attributes for ormar models.
    """

    database = database
    metadata = metadata

DateFieldsMixins

Mixin class that provides date fields for created_at and updated_at.

Source code in sharkservers/db.py
72
73
74
75
76
77
78
79
80
81
82
class DateFieldsMixins:
    """Mixin class that provides date fields for created_at and updated_at."""

    created_at: datetime.datetime = ormar.DateTime(
        default=now_datetime().replace(tzinfo=None),
        timezone=False,
    )
    updated_at: datetime.datetime = ormar.DateTime(
        default=now_datetime().replace(tzinfo=None),
        timezone=False,
    )

BaseService

Base service class for ormar models.

Source code in sharkservers/db.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
class BaseService:
    """Base service class for ormar models."""

    class Meta:
        """Meta class for the BaseService class."""

        model: ormar.Model = None
        not_found_exception: HTTPException = None

    async def get_one(self, **kwargs) -> ormar.Model:  # noqa: ANN003
        """
        Get a single model instance based on the provided filters.

        Args:
        ----
            **kwargs: The filters to apply.

        Returns:
        -------
            ormar.Model: The retrieved model instance.

        Raises:
        ------
            HTTPException: If no matching model instance is found.
        """
        try:
            related = kwargs.pop("related", None)
            return (
                (
                    await self.Meta.model.objects.select_related(related)
                    .filter(_exclude=False, **kwargs)
                    .first()
                )
                if related
                else await self.Meta.model.objects.filter(
                    _exclude=False,
                    **kwargs,
                ).first()
            )
        except ormar.NoMatch as err:
            raise self.Meta.not_found_exception from err

    async def get_all(  # noqa: ANN201
        self,
        params: Params = None,
        related=None,  # noqa: ANN001
        order_by=None,  # noqa: ANN001
        **kwargs,  # noqa: ANN003
    ):
        """
        Get all model instances based on the provided filters.

        Args:
        ----
            params (Params, optional): The pagination parameters.
            related (str, optional): The related model to include.
            order_by (str, optional): The field to order the results by.
            **kwargs: The filters to apply.

        Returns:
        -------
            QuerySet: The retrieved model instances.
        """
        query = self.Meta.model.objects.filter(**kwargs)
        if related:
            query = query.select_related(related)
        if order_by:
            query = query.order_by(order_by)
        if params:
            query = await paginate(query, params)
        return query

    async def delete(self, _id: int):  # noqa: ANN201
        """
        Delete a model instance by its ID.

        Args:
        ----
            _id (int): The ID of the model instance to delete.

        Returns:
        -------
            ormar.Model: The deleted model instance.

        Raises:
        ------
            HTTPException: If no matching model instance is found.
        """
        _object = await self.get_one(id=_id)
        await _object.delete()
        return _object

    async def create(self, *args, **kwargs):  # noqa: ANN201, ARG002, ANN002, ANN003
        """
        Create a new model instance.

        Args:
        ----
            *args: The positional arguments.
            **kwargs: The keyword arguments.

        Returns:
        -------
            ormar.Model: The created model instance.

        Raises:
        ------
            HTTPException: If a unique constraint is violated.
        """
        try:
            return await self.Meta.model.objects.create(**kwargs)
        except (IntegrityError, SQLIntegrityError, UniqueViolationError) as err:
            raise HTTPException(status_code=422, detail="Key already exists") from err

    async def update(self, updated_data: dict, **kwargs) -> ormar.Model:  # noqa: ANN003
        """
        Update a model instance.

        Args:
        ----
            updated_data (dict): The updated data.
            **kwargs: The filters to apply.

        Returns:
        -------
            ormar.Model: The updated model instance.

        Raises:
        ------
            HTTPException: If a unique constraint is violated.
        """
        try:
            related = kwargs.pop("related", None)
            await self.Meta.model.objects.filter(_exclude=False, **kwargs).update(
                **updated_data,
            )
            return await self.get_one(**kwargs, related=related)
        except (IntegrityError, SQLIntegrityError, UniqueViolationError) as err:
            raise HTTPException(422, "Key already exists") from err

Meta

Meta class for the BaseService class.

Source code in sharkservers/db.py
88
89
90
91
92
class Meta:
    """Meta class for the BaseService class."""

    model: ormar.Model = None
    not_found_exception: HTTPException = None

get_one(**kwargs) -> ormar.Model async

Get a single model instance based on the provided filters.


**kwargs: The filters to apply.

ormar.Model: The retrieved model instance.

HTTPException: If no matching model instance is found.
Source code in sharkservers/db.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
async def get_one(self, **kwargs) -> ormar.Model:  # noqa: ANN003
    """
    Get a single model instance based on the provided filters.

    Args:
    ----
        **kwargs: The filters to apply.

    Returns:
    -------
        ormar.Model: The retrieved model instance.

    Raises:
    ------
        HTTPException: If no matching model instance is found.
    """
    try:
        related = kwargs.pop("related", None)
        return (
            (
                await self.Meta.model.objects.select_related(related)
                .filter(_exclude=False, **kwargs)
                .first()
            )
            if related
            else await self.Meta.model.objects.filter(
                _exclude=False,
                **kwargs,
            ).first()
        )
    except ormar.NoMatch as err:
        raise self.Meta.not_found_exception from err

get_all(params: Params = None, related=None, order_by=None, **kwargs) async

Get all model instances based on the provided filters.


params (Params, optional): The pagination parameters.
related (str, optional): The related model to include.
order_by (str, optional): The field to order the results by.
**kwargs: The filters to apply.

QuerySet: The retrieved model instances.
Source code in sharkservers/db.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
async def get_all(  # noqa: ANN201
    self,
    params: Params = None,
    related=None,  # noqa: ANN001
    order_by=None,  # noqa: ANN001
    **kwargs,  # noqa: ANN003
):
    """
    Get all model instances based on the provided filters.

    Args:
    ----
        params (Params, optional): The pagination parameters.
        related (str, optional): The related model to include.
        order_by (str, optional): The field to order the results by.
        **kwargs: The filters to apply.

    Returns:
    -------
        QuerySet: The retrieved model instances.
    """
    query = self.Meta.model.objects.filter(**kwargs)
    if related:
        query = query.select_related(related)
    if order_by:
        query = query.order_by(order_by)
    if params:
        query = await paginate(query, params)
    return query

delete(_id: int) async

Delete a model instance by its ID.


_id (int): The ID of the model instance to delete.

ormar.Model: The deleted model instance.

HTTPException: If no matching model instance is found.
Source code in sharkservers/db.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
async def delete(self, _id: int):  # noqa: ANN201
    """
    Delete a model instance by its ID.

    Args:
    ----
        _id (int): The ID of the model instance to delete.

    Returns:
    -------
        ormar.Model: The deleted model instance.

    Raises:
    ------
        HTTPException: If no matching model instance is found.
    """
    _object = await self.get_one(id=_id)
    await _object.delete()
    return _object

create(*args, **kwargs) async

Create a new model instance.


*args: The positional arguments.
**kwargs: The keyword arguments.

ormar.Model: The created model instance.

HTTPException: If a unique constraint is violated.
Source code in sharkservers/db.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
async def create(self, *args, **kwargs):  # noqa: ANN201, ARG002, ANN002, ANN003
    """
    Create a new model instance.

    Args:
    ----
        *args: The positional arguments.
        **kwargs: The keyword arguments.

    Returns:
    -------
        ormar.Model: The created model instance.

    Raises:
    ------
        HTTPException: If a unique constraint is violated.
    """
    try:
        return await self.Meta.model.objects.create(**kwargs)
    except (IntegrityError, SQLIntegrityError, UniqueViolationError) as err:
        raise HTTPException(status_code=422, detail="Key already exists") from err

update(updated_data: dict, **kwargs) -> ormar.Model async

Update a model instance.


updated_data (dict): The updated data.
**kwargs: The filters to apply.

ormar.Model: The updated model instance.

HTTPException: If a unique constraint is violated.
Source code in sharkservers/db.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
async def update(self, updated_data: dict, **kwargs) -> ormar.Model:  # noqa: ANN003
    """
    Update a model instance.

    Args:
    ----
        updated_data (dict): The updated data.
        **kwargs: The filters to apply.

    Returns:
    -------
        ormar.Model: The updated model instance.

    Raises:
    ------
        HTTPException: If a unique constraint is violated.
    """
    try:
        related = kwargs.pop("related", None)
        await self.Meta.model.objects.filter(_exclude=False, **kwargs).update(
            **updated_data,
        )
        return await self.get_one(**kwargs, related=related)
    except (IntegrityError, SQLIntegrityError, UniqueViolationError) as err:
        raise HTTPException(422, "Key already exists") from err

create_redis_pool() -> aioredis.Redis async

Create a Redis connection pool.

Returns
aioredis.Redis: The Redis connection pool.
Source code in sharkservers/db.py
33
34
35
36
37
38
39
40
41
42
43
async def create_redis_pool() -> aioredis.Redis:
    """
    Create a Redis connection pool.

    Returns
    -------
        aioredis.Redis: The Redis connection pool.
    """
    if settings.TESTING:
        return await fake_aioredis.FakeRedis()
    return aioredis.from_url(REDIS_URL, encoding="utf-8", decode_responses=True)

get_redis(request: Request) -> aioredis.Redis async

Get the Redis connection from the request.


request (Request): The incoming request.

aioredis.Redis: The Redis connection.
Source code in sharkservers/db.py
46
47
48
49
50
51
52
53
54
55
56
57
58
async def get_redis(request: Request) -> aioredis.Redis:
    """
    Get the Redis connection from the request.

    Args:
    ----
        request (Request): The incoming request.

    Returns:
    -------
        aioredis.Redis: The Redis connection.
    """
    return request.app.state.redis

dependencies

Dependencies for the application.

get_email_checker() -> DefaultChecker async

Retrieve the default email checker.

Returns
DefaultChecker: The default email checker.
Source code in sharkservers/dependencies.py
26
27
28
29
30
31
32
33
34
35
36
37
38
async def get_email_checker() -> DefaultChecker:
    """
    Retrieve the default email checker.

    Returns
    -------
        DefaultChecker: The default email checker.
    """
    checker = (
        DefaultChecker()
    )  # you can pass source argument for your own email domains
    await checker.fetch_temp_email_domains()  # require to fetch temporary email domains
    return checker

get_email_service(settings: Settings = Depends(get_settings), checker: DefaultChecker = Depends(get_email_checker)) -> EmailService async

Retrieve the email service.


settings (Settings, optional): The application settings. Defaults to Depends(get_settings).
checker (DefaultChecker, optional): The default email checker. Defaults to Depends(get_email_checker).

EmailService: The email service.
Source code in sharkservers/dependencies.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
async def get_email_service(
    settings: Settings = Depends(get_settings),
    checker: DefaultChecker = Depends(get_email_checker),
) -> EmailService:
    """
    Retrieve the email service.

    Args:
    ----
        settings (Settings, optional): The application settings. Defaults to Depends(get_settings).
        checker (DefaultChecker, optional): The default email checker. Defaults to Depends(get_email_checker).

    Returns:
    -------
        EmailService: The email service.
    """
    resend.api_key = settings.RESEND_API_KEY
    return EmailService(resend=resend, checker=checker)

get_upload_service(settings: Settings = Depends(get_settings)) -> UploadService async

Retrieve the upload service.


settings (Settings, optional): The application settings. Defaults to Depends(get_settings).

UploadService: The upload service.
Source code in sharkservers/dependencies.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
async def get_upload_service(
    settings: Settings = Depends(get_settings),
) -> UploadService:
    """
    Retrieve the upload service.

    Args:
    ----
        settings (Settings, optional): The application settings. Defaults to Depends(get_settings).

    Returns:
    -------
        UploadService: The upload service.
    """
    return UploadService(settings=settings)

enums

Module contains all the enums used in the project.

MainEventEnum

Bases: str, Enum

Enum class representing main events.

Attributes
STARTUP (str): Represents the startup event.
SHUTDOWN (str): Represents the shutdown event.
INSTALL (str): Represents the install event.
Source code in sharkservers/enums.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class MainEventEnum(str, Enum):
    """
    Enum class representing main events.

    Attributes
    ----------
        STARTUP (str): Represents the startup event.
        SHUTDOWN (str): Represents the shutdown event.
        INSTALL (str): Represents the install event.
    """

    STARTUP = "MAIN_STARTUP"
    SHUTDOWN = "MAIN_SHUTDOWN"
    INSTALL = "MAIN_INSTALL"

ActivationEmailTypeEnum

Bases: str, Enum

Enum class representing activation email types.

Attributes
ACCOUNT (str): Represents the account activation email type.
EMAIL (str): Represents the email activation email type.
PASSWORD (str): Represents the password activation email type.
Source code in sharkservers/enums.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class ActivationEmailTypeEnum(str, Enum):
    """
    Enum class representing activation email types.

    Attributes
    ----------
        ACCOUNT (str): Represents the account activation email type.
        EMAIL (str): Represents the email activation email type.
        PASSWORD (str): Represents the password activation email type.
    """

    ACCOUNT = "account"
    EMAIL = "email"
    PASSWORD = "password"  # noqa: S105

OrderEnum

Bases: str, Enum

Enum class representing order types.

Attributes
ID_DESC (str): Represents the descending order by ID.
ID_ASC (str): Represents the ascending order by ID.
Source code in sharkservers/enums.py
37
38
39
40
41
42
43
44
45
46
47
48
class OrderEnum(str, Enum):
    """
    Enum class representing order types.

    Attributes
    ----------
        ID_DESC (str): Represents the descending order by ID.
        ID_ASC (str): Represents the ascending order by ID.
    """

    ID_DESC = "-id"
    ID_ASC = "id"

exception_handlers

Exception handlers for FastAPI.

request_validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse async

Middleware will log all RequestValidationErrors.


request: The request object.
exc: The RequestValidationError exception.

A JSONResponse with the errors and the request body.
Source code in sharkservers/exception_handlers.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
async def request_validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    """
    Middleware will log all RequestValidationErrors.

    Args:
    ----
        request: The request object.
        exc: The RequestValidationError exception.

    Returns:
    -------
        A JSONResponse with the errors and the request body.

    """
    logger.debug("Our custom request_validation_exception_handler was called")
    body = await request.body()
    query_params = request.query_params._dict  # pylint: disable=protected-access
    detail = {
        "errors": exc.errors(),
        "body": body.decode(),
        "query_params": query_params,
    }
    logger.info(detail)
    return await _request_validation_exception_handler(request, exc)

http_exception_handler(request: Request, exc: HTTPException) -> Union[JSONResponse, Response] async

HTTPException handler.


request: The request object.
exc: The HTTPException exception.

A JSONResponse with the error.
Source code in sharkservers/exception_handlers.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
async def http_exception_handler(
    request: Request, exc: HTTPException
) -> Union[JSONResponse, Response]:
    """
    HTTPException handler.

    Args:
    ----
        request: The request object.
        exc: The HTTPException exception.

    Returns:
    -------
        A JSONResponse with the error.
    """
    logger.error(f"{exc.detail} <{exc.detail.value}>")
    return await _http_exception_handler(request, exc)

unhandled_exception_handler(request: Request, exc: Exception) -> PlainTextResponse async

Unhandled exception handler.


request: The request object.
exc: The exception.

A PlainTextResponse with the error.
Source code in sharkservers/exception_handlers.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
async def unhandled_exception_handler(
    request: Request, exc: Exception
) -> PlainTextResponse:
    """
    Unhandled exception handler.

    Args:
    ----
        request: The request object.
        exc: The exception.

    Returns:
    -------
        A PlainTextResponse with the error.
    """
    logger.debug("Our custom unhandled_exception_handler was called")
    host = getattr(getattr(request, "client", None), "host", None)
    port = getattr(getattr(request, "client", None), "port", None)
    url = (
        f"{request.url.path}?{request.query_params}"
        if request.query_params
        else request.url.path
    )
    exception_type, exception_value, exception_traceback = sys.exc_info()
    exception_name = getattr(exception_type, "__name__", None)
    logger.error(
        f'{host}:{port} - "{request.method} {url}" 500 Internal Server Error <{exception_name}: {exception_value}>',
    )
    return PlainTextResponse(str(exc), status_code=500)

forum

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Forum dependencies.

get_categories_service() -> CategoryService async

Get categories service.

Source code in sharkservers/forum/dependencies.py
23
24
25
async def get_categories_service() -> CategoryService:
    """Get categories service."""
    return CategoryService()

get_threads_service() -> ThreadService async

Get threads service.

Source code in sharkservers/forum/dependencies.py
28
29
30
async def get_threads_service() -> ThreadService:
    """Get threads service."""
    return ThreadService()

get_posts_service() -> PostService async

Get posts service.

Source code in sharkservers/forum/dependencies.py
33
34
35
async def get_posts_service() -> PostService:
    """Get posts service."""
    return PostService()

get_valid_category(category_id: int, categories_service: CategoryService = Depends(get_categories_service)) -> Category async

Get valid category.


category_id (int): The category id.
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

Category: The category.
Source code in sharkservers/forum/dependencies.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
async def get_valid_category(
    category_id: int,
    categories_service: CategoryService = Depends(get_categories_service),
) -> Category:
    """
    Get valid category.

    Args:
    ----
        category_id (int): The category id.
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

    Returns:
    -------
        Category: The category.
    """
    return await categories_service.get_one(id=category_id)

get_valid_thread(thread_id: int, threads_service: ThreadService = Depends(get_threads_service)) -> Thread async

Get valid thread.


thread_id (int): The thread id.
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

Thread: The thread.
Source code in sharkservers/forum/dependencies.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
async def get_valid_thread(
    thread_id: int,
    threads_service: ThreadService = Depends(get_threads_service),
) -> Thread:
    """
    Get valid thread.

    Args:
    ----
        thread_id (int): The thread id.
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

    Returns:
    -------
        Thread: The thread.
    """
    return await threads_service.get_one(
        id=thread_id,
        related=[
            "category",
            "author",
            "author__display_role",
            "author__player",
            "author__player__steamrep_profile",
            "meta_fields",
            "server",
            "server__admin_role",
        ],
    )

get_valid_open_thread(thread: Thread = Depends(get_valid_thread)) -> Thread async

Get valid open thread.


thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

Thread: The thread.
Source code in sharkservers/forum/dependencies.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
async def get_valid_open_thread(thread: Thread = Depends(get_valid_thread)) -> Thread:
    """
    Get valid open thread.

    Args:
    ----
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

    Returns:
    -------
        Thread: The thread.
    """
    if thread.is_closed:
        raise thread_is_closed_exception
    return thread

get_valid_thread_with_author(thread: Thread = Depends(get_valid_open_thread), user: User = Security(get_current_active_user, scopes=['threads:update'])) -> Thread async

Get valid thread with author.


thread (Thread, optional): The thread. Defaults to Depends(get_valid_open_thread).
user (User, optional): The current active user. Defaults to Security(get_current_active_user, scopes=["threads:update"]).

Thread: The thread.
Source code in sharkservers/forum/dependencies.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
async def get_valid_thread_with_author(
    thread: Thread = Depends(get_valid_open_thread),
    user: User = Security(get_current_active_user, scopes=["threads:update"]),
) -> Thread:
    """
    Get valid thread with author.

    Args:
    ----
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_open_thread).
        user (User, optional): The current active user. Defaults to Security(get_current_active_user, scopes=["threads:update"]).

    Returns:
    -------
        Thread: The thread.
    """
    if thread.author != user:
        raise thread_not_valid_author_exception
    return thread

get_valid_post(post_id: int, posts_service: PostService = Depends(get_posts_service)) -> Post async

Get valid post.


post_id (int): The post id.
posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).

Post: The post.
Source code in sharkservers/forum/dependencies.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
async def get_valid_post(
    post_id: int,
    posts_service: PostService = Depends(get_posts_service),
) -> Post:
    """
    Get valid post.

    Args:
    ----
        post_id (int): The post id.
        posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).

    Returns:
    -------
        Post: The post.
    """
    dispatch(PostEventEnum.GET_ONE_PRE, payload={"data": post_id})
    return await posts_service.get_one(
        id=post_id,
        related=[
            "author",
            "author__display_role",
            "likes",
            "author__player",
            "author__player__steamrep_profile",
        ],
    )

get_valid_post_author(post: Post = Depends(get_valid_post), user: User = Security(get_current_active_user, scopes=['posts:update'])) -> Post async

Get valid post author.


post (Post, optional): The post. Defaults to Depends(get_valid_post).
user (User, optional): The current active user. Defaults to Security(get_current_active_user, scopes=["posts:update"]).

Post: The post.
Source code in sharkservers/forum/dependencies.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
async def get_valid_post_author(
    post: Post = Depends(get_valid_post),
    user: User = Security(get_current_active_user, scopes=["posts:update"]),
) -> Post:
    """
    Get valid post author.

    Args:
    ----
        post (Post, optional): The post. Defaults to Depends(get_valid_post).
        user (User, optional): The current active user. Defaults to Security(get_current_active_user, scopes=["posts:update"]).

    Returns:
    -------
        Post: The post.
    """
    if post.author != user:
        raise post_not_valid_author_exception
    return post

get_likes_service() -> LikeService async

Get likes service.

Source code in sharkservers/forum/dependencies.py
176
177
178
async def get_likes_service() -> LikeService:
    """Get likes service."""
    return LikeService()

get_thread_meta_service() -> ThreadMetaService async

Get thread meta service.

Source code in sharkservers/forum/dependencies.py
181
182
183
async def get_thread_meta_service() -> ThreadMetaService:
    """Get thread meta service."""
    return ThreadMetaService()

enums

Enums for forum app.

CategoryExceptionEnum

Bases: str, Enum

Enum for categories exceptions.

Source code in sharkservers/forum/enums.py
 5
 6
 7
 8
 9
10
class CategoryExceptionEnum(str, Enum):
    """Enum for categories exceptions."""

    NOT_FOUND = "Category not found"
    ALREADY_EXISTS = "Category already exists"
    NOT_ALLOWED = "You are not allowed to create a category"

TagExceptionEnum

Bases: str, Enum

Enum for tags exceptions.

Source code in sharkservers/forum/enums.py
13
14
15
16
class TagExceptionEnum(str, Enum):
    """Enum for tags exceptions."""

    NOT_FOUND = "Tag not found"

ThreadExceptionEnum

Bases: str, Enum

Enum for thread exceptions.

Source code in sharkservers/forum/enums.py
19
20
21
22
23
24
25
class ThreadExceptionEnum(str, Enum):
    """Enum for thread exceptions."""

    NOT_FOUND = "Thread not found"
    ALREADY_EXISTS = "Thread with this title on this category exists"
    IS_CLOSED = "Thread is closed"
    NOT_VALID_THREAD_AUTHOR = "You are not allowed to update this thread"

PostExceptionsEnum

Bases: str, Enum

Enum for post exceptions.

Source code in sharkservers/forum/enums.py
28
29
30
31
32
33
class PostExceptionsEnum(str, Enum):
    """Enum for post exceptions."""

    NOT_FOUND = "Post not found"
    ALREADY_EXISTS = "Post already exists"
    NOT_VALID_AUTHOR = "You are not allowed to update this post"

LikeExceptionsEnum

Bases: str, Enum

Enum for like exceptions.

Source code in sharkservers/forum/enums.py
36
37
38
39
40
class LikeExceptionsEnum(str, Enum):
    """Enum for like exceptions."""

    NOT_FOUND = "Like not found"
    ALREADY_EXISTS = "U already liked this post"

CategoryEventEnum

Bases: str, Enum

Enum for categories events.

Source code in sharkservers/forum/enums.py
43
44
45
46
47
48
49
class CategoryEventEnum(str, Enum):
    """Enum for categories events."""

    GET_ALL_PRE = "CATEGORIES_GET_ALL_PRE"
    GET_ALL_POST = "CATEGORIES_GET_ALL_POST"
    GET_ONE_PRE = "CATEGORIES_GET_ONE_PRE"
    GET_ONE_POST = "CATEGORIES_GET_ONE_POST"

CategoryAdminEventEnum

Bases: str, Enum

Enum for categories admin events.

Source code in sharkservers/forum/enums.py
52
53
54
55
56
57
58
59
60
61
62
63
64
class CategoryAdminEventEnum(str, Enum):
    """Enum for categories admin events."""

    GET_ALL_PRE = "CATEGORIES_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "CATEGORIES_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "CATEGORIES_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "CATEGORIES_ADMIN_GET_ONE_POST"
    CREATE_PRE = "CATEGORIES_ADMIN_CREATE_PRE"
    CREATE_POST = "CATEGORIES_ADMIN_CREATE_POST"
    UPDATE_PRE = "CATEGORIES_ADMIN_UPDATE_PRE"
    UPDATE_POST = "CATEGORIES_ADMIN_UPDATE_POST"
    DELETE_PRE = "CATEGORIES_ADMIN_DELETE_PRE"
    DELETE_POST = "CATEGORIES_ADMIN_DELETE_POST"

TagEventsEnum

Bases: str, Enum

Enum for tags events.

Source code in sharkservers/forum/enums.py
67
68
69
70
71
72
73
class TagEventsEnum(str, Enum):
    """Enum for tags events."""

    GET_ALL_PRE = "TAGS_GET_ALL_PRE"
    GET_ALL_POST = "TAGS_GET_ALL_POST"
    GET_ONE_PRE = "TAGS_GET_ONE_PRE"
    GET_ONE_POST = "TAGS_GET_ONE_POST"

TagAdminEventsEnum

Bases: str, Enum

Enum for tags admin events.

Source code in sharkservers/forum/enums.py
76
77
78
79
80
81
82
83
84
85
86
87
88
class TagAdminEventsEnum(str, Enum):
    """Enum for tags admin events."""

    GET_ALL_PRE = "TAGS_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "TAGS_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "TAGS_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "TAGS_ADMIN_GET_ONE_POST"
    CREATE_PRE = "TAGS_ADMIN_CREATE_PRE"
    CREATE_POST = "TAGS_ADMIN_CREATE_POST"
    UPDATE_PRE = "TAGS_ADMIN_UPDATE_PRE"
    UPDATE_POST = "TAGS_ADMIN_UPDATE_POST"
    DELETE_PRE = "TAGS_ADMIN_DELETE_PRE"
    DELETE_POST = "TAGS_ADMIN_DELETE_POST"

ThreadEventEnum

Bases: str, Enum

Enum for threads events.

Source code in sharkservers/forum/enums.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
class ThreadEventEnum(str, Enum):
    """Enum for threads events."""

    GET_ALL_PRE = "THREADS_GET_ALL_PRE"
    GET_ALL_POST = "THREADS_GET_ALL_POST"
    GET_ONE_PRE = "THREADS_GET_ONE_PRE"
    GET_ONE_POST = "THREADS_GET_ONE_POST"
    CREATE_PRE = "THREADS_CREATE_PRE"
    CREATE_POST = "THREADS_CREATE_POST"
    UPDATE_PRE = "THREADS_UPDATE_PRE"
    UPDATE_POST = "THREADS_UPDATE_POST"
    DELETE_PRE = "THREADS_DELETE_PRE"
    DELETE_POST = "THREADS_DELETE_POST"
    CLOSE_PRE = "THREADS_CLOSE_PRE"
    CLOSE_POST = "THREADS_CLOSE_POST"
    OPEN_PRE = "THREADS_OPEN_PRE"
    OPEN_POST = "THREADS_OPEN_POST"

ThreadAdminEventEnum

Bases: str, Enum

Enum for threads admin events.

Source code in sharkservers/forum/enums.py
110
111
112
113
114
115
116
117
118
119
120
121
122
class ThreadAdminEventEnum(str, Enum):
    """Enum for threads admin events."""

    GET_ALL_PRE = "THREADS_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "THREADS_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "THREADS_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "THREADS_ADMIN_GET_ONE_POST"
    CREATE_PRE = "THREADS_ADMIN_CREATE_PRE"
    CREATE_POST = "THREADS_ADMIN_CREATE_POST"
    UPDATE_PRE = "THREADS_ADMIN_UPDATE_PRE"
    UPDATE_POST = "THREADS_ADMIN_UPDATE_POST"
    DELETE_PRE = "THREADS_ADMIN_DELETE_PRE"
    DELETE_POST = "THREADS_ADMIN_DELETE_POST"

PostEventEnum

Bases: str, Enum

Enum for posts events.

Source code in sharkservers/forum/enums.py
125
126
127
128
129
130
131
132
133
134
135
136
137
class PostEventEnum(str, Enum):
    """Enum for posts events."""

    GET_ALL_PRE = "POSTS_GET_ALL_PRE"
    GET_ALL_POST = "POSTS_GET_ALL_POST"
    GET_ONE_PRE = "POSTS_GET_ONE_PRE"
    GET_ONE_POST = "POSTS_GET_ONE_POST"
    CREATE_PRE = "POSTS_CREATE_PRE"
    CREATE_POST = "POSTS_CREATE_POST"
    UPDATE_PRE = "POSTS_UPDATE_PRE"
    UPDATE_POST = "POSTS_UPDATE_POST"
    DELETE_PRE = "POSTS_DELETE_PRE"
    DELETE_POST = "POSTS_DELETE_POST"

PostAdminEventEnum

Bases: str, Enum

Enum for posts admin events.

Source code in sharkservers/forum/enums.py
141
142
143
144
145
146
147
148
149
150
151
152
153
class PostAdminEventEnum(str, Enum):
    """Enum for posts admin events."""

    GET_ALL_PRE = "POSTS_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "POSTS_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "POSTS_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "POSTS_ADMIN_GET_ONE_POST"
    CREATE_PRE = "POSTS_ADMIN_CREATE_PRE"
    CREATE_POST = "POSTS_ADMIN_CREATE_POST"
    UPDATE_PRE = "POSTS_ADMIN_UPDATE_PRE"
    UPDATE_POST = "POSTS_ADMIN_UPDATE_POST"
    DELETE_PRE = "POSTS_ADMIN_DELETE_PRE"
    DELETE_POST = "POSTS_ADMIN_DELETE_POST"

CategoryTypeEnum

Bases: str, Enum

Enum for category types.

Source code in sharkservers/forum/enums.py
156
157
158
159
160
161
162
class CategoryTypeEnum(str, Enum):
    """Enum for category types."""

    PUBLIC = "public"
    PRIVATE = "private"
    APPLICATION = "application"
    APPEAL = "appeal"

ThreadTypeEnum

Bases: str, Enum

Enum for thread types.

Source code in sharkservers/forum/enums.py
165
166
167
168
169
class ThreadTypeEnum(str, Enum):
    """Enum for thread types."""

    APPLICATION = "application"
    APPEAL = "appeal"

ThreadStatusEnum

Bases: str, Enum

Enum for thread statuses.

Source code in sharkservers/forum/enums.py
172
173
174
175
176
177
class ThreadStatusEnum(str, Enum):
    """Enum for thread statuses."""

    APPROVED = "approved"
    PENDING = "pending"
    REJECTED = "rejected"

ThreadOrderEnum

Bases: str, Enum

Enum for thread order.

Source code in sharkservers/forum/enums.py
180
181
182
183
184
class ThreadOrderEnum(str, Enum):
    """Enum for thread order."""

    ID_DESC = "-id"
    ID_ASC = "id"

ThreadActionEnum

Bases: str, Enum

Enum for thread actions.

Source code in sharkservers/forum/enums.py
187
188
189
190
191
192
193
194
195
196
class ThreadActionEnum(str, Enum):
    """Enum for thread actions."""

    CLOSE = "close"
    OPEN = "open"
    APPROVE = "approve"
    REJECT = "reject"
    MOVE = "move"
    PIN = "pin"
    UNPIN = "unpin"

exceptions

Forum exceptions.

models

Forum models.

Category

Bases: Model, DateFieldsMixins

Category model.

Attributes
id (int): Category ID
name (str): Category name
description (str): Category description
type (str): Category type
threads_count (int): Category threads count
Source code in sharkservers/forum/models.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Category(ormar.Model, DateFieldsMixins):
    """
    Category model.

    Attributes
    ----------
        id (int): Category ID
        name (str): Category name
        description (str): Category description
        type (str): Category type
        threads_count (int): Category threads count
    """

    class Meta(BaseMeta):
        """Meta class for Category model."""

        tablename = "forum_categories"

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=64, unique=True)
    description: str = ormar.Text(nullable=True)
    type: str = ormar.String(
        max_length=64,
        choices=list(CategoryTypeEnum),
        default=CategoryTypeEnum.PUBLIC.value,
    )
    threads_count: int = ormar.Integer(default=0)
Meta

Bases: BaseMeta

Meta class for Category model.

Source code in sharkservers/forum/models.py
33
34
35
36
class Meta(BaseMeta):
    """Meta class for Category model."""

    tablename = "forum_categories"

Like

Bases: Model, DateFieldsMixins

Like model.

Attributes
id (int): Like ID
author (User): Like author
Source code in sharkservers/forum/models.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Like(ormar.Model, DateFieldsMixins):
    """
    Like model.

    Attributes
    ----------
        id (int): Like ID
        author (User): Like author
    """

    class Meta(BaseMeta):
        """Meta class for Like model."""

        tablename = "forum_reputation"

    id: int = ormar.Integer(primary_key=True)
    author: User | None = ormar.ForeignKey(User, related_name="user_reputation")
Meta

Bases: BaseMeta

Meta class for Like model.

Source code in sharkservers/forum/models.py
59
60
61
62
class Meta(BaseMeta):
    """Meta class for Like model."""

    tablename = "forum_reputation"

Post

Bases: Model, DateFieldsMixins

Post model.

Attributes
id (int): Post ID
author (User): Post author
content (str): Post content
likes (List[Like]): Post likes
likes_count (int): Post likes count
Source code in sharkservers/forum/models.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class Post(ormar.Model, DateFieldsMixins):
    """
    Post model.

    Attributes
    ----------
        id (int): Post ID
        author (User): Post author
        content (str): Post content
        likes (List[Like]): Post likes
        likes_count (int): Post likes count
    """

    class Meta(BaseMeta):
        """Meta class for Post model."""

        tablename = "forum_posts"

    id: int = ormar.Integer(primary_key=True)
    author: User | None = ormar.ForeignKey(User, related_name="user_posts")
    content: str = ormar.Text()
    likes: list[Like] | None = ormar.ManyToMany(Like, related_name="post_likes")
    likes_count = ormar.Integer(default=0)
Meta

Bases: BaseMeta

Meta class for Post model.

Source code in sharkservers/forum/models.py
81
82
83
84
class Meta(BaseMeta):
    """Meta class for Post model."""

    tablename = "forum_posts"

ThreadMeta

Bases: Model, DateFieldsMixins

Thread meta model.

Attributes
id (str): Thread meta ID
name (str): Thread meta name
value (str): Thread meta value
description (str): Thread meta description
Source code in sharkservers/forum/models.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class ThreadMeta(ormar.Model, DateFieldsMixins):
    """
    Thread meta model.

    Attributes
    ----------
        id (str): Thread meta ID
        name (str): Thread meta name
        value (str): Thread meta value
        description (str): Thread meta description
    """

    class Meta(BaseMeta):
        """Meta class for ThreadMeta model."""

        tablename = "forum_threads_meta"

    id: str = ormar.UUID(primary_key=True, default=uuid.uuid4)
    name: str | None = ormar.String(max_length=64)
    value: str | None = ormar.Text(nullable=True)
    description: str | None = ormar.Text(nullable=True)
Meta

Bases: BaseMeta

Meta class for ThreadMeta model.

Source code in sharkservers/forum/models.py
105
106
107
108
class Meta(BaseMeta):
    """Meta class for ThreadMeta model."""

    tablename = "forum_threads_meta"

Thread

Bases: Model, DateFieldsMixins

Thread model.

Attributes
id (int): Thread ID
title (str): Thread title
content (str): Thread content
is_closed (bool): Is thread closed
is_pinned (bool): Is thread pinned
status (str): Thread status
category (Category): Thread category
author (User): Thread author
posts (List[Post]): Thread posts
meta_fields (List[ThreadMeta]): Thread meta fields
post_count (int): Thread post count
Source code in sharkservers/forum/models.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
class Thread(ormar.Model, DateFieldsMixins):
    """
    Thread model.

    Attributes
    ----------
        id (int): Thread ID
        title (str): Thread title
        content (str): Thread content
        is_closed (bool): Is thread closed
        is_pinned (bool): Is thread pinned
        status (str): Thread status
        category (Category): Thread category
        author (User): Thread author
        posts (List[Post]): Thread posts
        meta_fields (List[ThreadMeta]): Thread meta fields
        post_count (int): Thread post count
    """

    class Meta(BaseMeta):
        """Meta class for Thread model."""

        tablename = "forum_threads"

    id: int = ormar.Integer(primary_key=True)
    title: str | None = ormar.String(max_length=64)
    content: str | None = ormar.Text()
    is_closed: bool | None = ormar.Boolean(default=False)
    is_pinned: bool | None = ormar.Boolean(default=False)
    status: str | None = ormar.String(
        max_length=64,
        choices=list(ThreadStatusEnum),
        default=None,
        nullable=True,
    )
    category: Category | None = ormar.ForeignKey(Category)
    author: User | None = ormar.ForeignKey(User, related_name="user_threads")
    posts: list[Post] | None = ormar.ManyToMany(Post, related_name="thread_post")
    meta_fields: list[ThreadMeta] | None = ormar.ManyToMany(
        ThreadMeta,
        related_name="thread_meta",
    )
    post_count: int = ormar.Integer(default=0)
    server: Server | None = ormar.ForeignKey(Server, related_name="thread_server")

    async def close(self) -> None:
        """Close thread."""
        await self.update(is_closed=True)

    async def open(self) -> None:
        """Open thread."""
        await self.update(is_closed=False)

    async def approve(self) -> None:
        """Approve thread."""
        await self.update(status=ThreadStatusEnum.APPROVED)
        await self.close()

    async def reject(self) -> None:
        """Reject thread."""
        await self.update(status=ThreadStatusEnum.REJECTED)
        await self.close()

    async def pin(self) -> None:
        """Pin thread."""
        await self.update(is_pinned=True)

    async def unpin(self) -> None:
        """Unpin thread."""
        await self.update(is_pinned=False)

    async def change_category(self, new_category: Category) -> None:
        """Change thread category."""
        await self.update(category=new_category)

    async def run_action(
        self,
        action: ThreadActionEnum,
        new_category: Category = None,
    ) -> None:
        """Run thread action."""
        if action == ThreadActionEnum.APPROVE:
            await self.approve()
        elif action == ThreadActionEnum.REJECT:
            await self.reject()
        elif action == ThreadActionEnum.PIN:
            await self.pin()
        elif action == ThreadActionEnum.UNPIN:
            await self.unpin()
        elif action == ThreadActionEnum.MOVE:
            await self.change_category(new_category=new_category)
        elif action == ThreadActionEnum.CLOSE:
            await self.close()
        elif action == ThreadActionEnum.OPEN:
            await self.open()
Meta

Bases: BaseMeta

Meta class for Thread model.

Source code in sharkservers/forum/models.py
135
136
137
138
class Meta(BaseMeta):
    """Meta class for Thread model."""

    tablename = "forum_threads"
close() -> None async

Close thread.

Source code in sharkservers/forum/models.py
161
162
163
async def close(self) -> None:
    """Close thread."""
    await self.update(is_closed=True)
open() -> None async

Open thread.

Source code in sharkservers/forum/models.py
165
166
167
async def open(self) -> None:
    """Open thread."""
    await self.update(is_closed=False)
approve() -> None async

Approve thread.

Source code in sharkservers/forum/models.py
169
170
171
172
async def approve(self) -> None:
    """Approve thread."""
    await self.update(status=ThreadStatusEnum.APPROVED)
    await self.close()
reject() -> None async

Reject thread.

Source code in sharkservers/forum/models.py
174
175
176
177
async def reject(self) -> None:
    """Reject thread."""
    await self.update(status=ThreadStatusEnum.REJECTED)
    await self.close()
pin() -> None async

Pin thread.

Source code in sharkservers/forum/models.py
179
180
181
async def pin(self) -> None:
    """Pin thread."""
    await self.update(is_pinned=True)
unpin() -> None async

Unpin thread.

Source code in sharkservers/forum/models.py
183
184
185
async def unpin(self) -> None:
    """Unpin thread."""
    await self.update(is_pinned=False)
change_category(new_category: Category) -> None async

Change thread category.

Source code in sharkservers/forum/models.py
187
188
189
async def change_category(self, new_category: Category) -> None:
    """Change thread category."""
    await self.update(category=new_category)
run_action(action: ThreadActionEnum, new_category: Category = None) -> None async

Run thread action.

Source code in sharkservers/forum/models.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
async def run_action(
    self,
    action: ThreadActionEnum,
    new_category: Category = None,
) -> None:
    """Run thread action."""
    if action == ThreadActionEnum.APPROVE:
        await self.approve()
    elif action == ThreadActionEnum.REJECT:
        await self.reject()
    elif action == ThreadActionEnum.PIN:
        await self.pin()
    elif action == ThreadActionEnum.UNPIN:
        await self.unpin()
    elif action == ThreadActionEnum.MOVE:
        await self.change_category(new_category=new_category)
    elif action == ThreadActionEnum.CLOSE:
        await self.close()
    elif action == ThreadActionEnum.OPEN:
        await self.open()

on_thread_save(sender, instance, **kwargs) -> None async

On thread save event.


sender (Thread): Thread model
instance (Thread): Thread instance
**kwargs: Additional arguments

None
Source code in sharkservers/forum/models.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
@post_save(Thread)
async def on_thread_save(
    sender, instance, **kwargs
) -> None:  # noqa: ANN003, ARG001, ANN001
    """
    On thread save event.

    Args:
    ----
        sender (Thread): Thread model
        instance (Thread): Thread instance
        **kwargs: Additional arguments

    Returns:
    -------
        None
    """
    # Update category thread counter
    category = await Category.objects.get(id=instance.category.id)
    threads_category_count = category.threads_count + 1
    threads_count = instance.author.threads_count + 1
    await category.update(threads_count=threads_category_count)
    await instance.author.update(threads_count=threads_count)
    # Check category type
    if category.type == CategoryTypeEnum.APPLICATION:
        # create thread server_id meta field
        server_id = await ThreadMeta.objects.create(
            name="server_id",
            description="Serwer na który aplikujesz",
        )
        experience = await ThreadMeta.objects.create(
            name="question_experience",
            description="Twoje doświadczenie",
        )
        age = await ThreadMeta.objects.create(
            name="question_age",
            description="Twój wiek",
        )
        reason = await ThreadMeta.objects.create(
            name="question_reason",
            description="Dlaczego chcesz zostac administratorem?",
        )
        meta_fields = [
            server_id,
            experience,
            age,
            reason,
        ]
        for meta_field in meta_fields:
            await instance.meta_fields.add(meta_field)

update_category_thread_counter_after_delete(sender: Thread, instance: Thread, **kwargs) -> None async

Update category thread counter after thread delete.


sender (Thread): Thread model
instance (Thread): Thread instance
**kwargs: Additional arguments
Source code in sharkservers/forum/models.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
@post_delete(Thread)
async def update_category_thread_counter_after_delete(
    sender: Thread,  # noqa: ARG001
    instance: Thread,
    **kwargs,  # noqa: ARG001, ANN003
) -> None:
    """
    Update category thread counter after thread delete.

    Args:
    ----
        sender (Thread): Thread model
        instance (Thread): Thread instance
        **kwargs: Additional arguments
    """
    category = await Category.objects.get(id=instance.category.id)
    await category.update(
        threads_count=category.threads_count - 1 if category.threads_count > 0 else 0,
    )

update_thread_post_counter_after_relation_add(sender: Thread, instance: Thread, child: Post, **kwargs) -> None async

Update thread post counter after relation add.

Source code in sharkservers/forum/models.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
@post_relation_add(Thread)
async def update_thread_post_counter_after_relation_add(
    sender: Thread,  # noqa: ARG001
    instance: Thread,
    child: Post,
    **kwargs,  # noqa: ARG001, ANN003
) -> None:
    """Update thread post counter after relation add."""
    if isinstance(child, Post):
        thread = await Thread.objects.get(id=instance.id)
        thread_posts_count = thread.post_count + 1
        posts_count = child.author.posts_count + 1
        await thread.update(post_count=thread_posts_count)
        logger.info(f"Thread {thread.title} post count updated to {thread.post_count}")
        await child.author.update(posts_count=posts_count)

update_thread_post_counter_after_relation_remove(sender: Thread, instance: Thread, child: Post, **kwargs) -> None async

Update thread post counter after relation remove.

Source code in sharkservers/forum/models.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@post_relation_remove(Thread)
async def update_thread_post_counter_after_relation_remove(
    sender: Thread,  # noqa: ARG001
    instance: Thread,
    child: Post,
    **kwargs,  # noqa: ANN003, ARG001
) -> None:
    """Update thread post counter after relation remove."""
    if isinstance(child, Post):
        thread = await Thread.objects.get(id=instance.id)
        thread_posts_count = thread.post_count - 1 if thread.post_count > 0 else 0
        posts_count = (
            child.author.posts_count - 1 if child.author.posts_count > 0 else 0
        )
        await thread.update(post_count=thread_posts_count)
        logger.info(f"Thread {thread.title} post count updated to {thread.post_count}")
        await child.author.update(posts_count=posts_count)

schemas

Forum schemas.

CategoryOut

Bases: category_out

Category output schema.

Source code in sharkservers/forum/schemas.py
41
42
43
44
45
46
47
class CategoryOut(category_out):
    """Category output schema."""

    class Config:
        """Category output schema config."""

        orm_mode = True
Config

Category output schema config.

Source code in sharkservers/forum/schemas.py
44
45
46
47
class Config:
    """Category output schema config."""

    orm_mode = True

ThreadMetaOut

Bases: BaseModel

Thread meta output schema.

Source code in sharkservers/forum/schemas.py
50
51
52
53
54
55
56
class ThreadMetaOut(BaseModel):
    """Thread meta output schema."""

    class Config:
        """Thread meta output schema config."""

        orm_mode = True
Config

Thread meta output schema config.

Source code in sharkservers/forum/schemas.py
53
54
55
56
class Config:
    """Thread meta output schema config."""

    orm_mode = True

ThreadOut

Bases: thread_out

Thread output schema.

Source code in sharkservers/forum/schemas.py
59
60
61
62
63
64
65
class ThreadOut(thread_out):
    """Thread output schema."""

    class Config:
        """Thread output schema config."""

        orm_mode = True
Config

Thread output schema config.

Source code in sharkservers/forum/schemas.py
62
63
64
65
class Config:
    """Thread output schema config."""

    orm_mode = True

PostOut

Bases: post_out

Post output schema.

Source code in sharkservers/forum/schemas.py
68
69
70
71
72
73
74
class PostOut(post_out):
    """Post output schema."""

    class Config:
        """Post output schema config."""

        orm_mode = True
Config

Post output schema config.

Source code in sharkservers/forum/schemas.py
71
72
73
74
class Config:
    """Post output schema config."""

    orm_mode = True

LikeOut

Bases: like_out

Like output schema.

Source code in sharkservers/forum/schemas.py
77
78
79
80
81
82
83
class LikeOut(like_out):
    """Like output schema."""

    class Config:
        """Like output schema config."""

        orm_mode = True
Config

Like output schema config.

Source code in sharkservers/forum/schemas.py
80
81
82
83
class Config:
    """Like output schema config."""

    orm_mode = True

ThreadTag

Bases: BaseModel

Thread tag schema.

Source code in sharkservers/forum/schemas.py
86
87
88
89
90
class ThreadTag(BaseModel):
    """Thread tag schema."""

    id: int
    name: str

ThreadCategory

Bases: BaseModel

Thread category schema.

Source code in sharkservers/forum/schemas.py
93
94
95
96
97
class ThreadCategory(BaseModel):
    """Thread category schema."""

    id: int
    name: str

ThreadAuthor

Bases: BaseModel

Thread author schema.

Source code in sharkservers/forum/schemas.py
100
101
102
103
104
105
106
class ThreadAuthor(BaseModel):
    """Thread author schema."""

    id: int
    username: str
    avatar: str
    display_role: Role

ThreadPostSchema

Bases: BaseModel

Thread post schema.

Source code in sharkservers/forum/schemas.py
109
110
111
112
113
114
class ThreadPostSchema(BaseModel):
    """Thread post schema."""

    id: int
    content: str
    author: ThreadAuthor

AdminCreateCategorySchema

Bases: BaseModel

Admin create category schema.

Source code in sharkservers/forum/schemas.py
117
118
119
120
121
122
class AdminCreateCategorySchema(BaseModel):
    """Admin create category schema."""

    title: str = Field(max_length=64, min_length=3)
    content: str = Field(min_length=2)
    category: int

AdminCreateThreadSchema

Bases: AdminCreateCategorySchema

Admin create thread schema.

Source code in sharkservers/forum/schemas.py
126
127
128
129
class AdminCreateThreadSchema(AdminCreateCategorySchema):
    """Admin create thread schema."""

    author_id: int

UpdateThreadSchema

Bases: BaseModel

Update thread schema.

Source code in sharkservers/forum/schemas.py
132
133
134
135
136
class UpdateThreadSchema(BaseModel):
    """Update thread schema."""

    title: str | None = Field(max_length=64)
    content: str | None

AdminUpdateThreadSchema

Bases: UpdateThreadSchema

Admin update thread schema.

Source code in sharkservers/forum/schemas.py
140
141
142
143
144
class AdminUpdateThreadSchema(UpdateThreadSchema):
    """Admin update thread schema."""

    author_id: int | None
    category_id: int | None

CreatePostSchema

Bases: BaseModel

Create post schema.

Source code in sharkservers/forum/schemas.py
147
148
149
150
151
class CreatePostSchema(BaseModel):
    """Create post schema."""

    thread_id: int
    content: str = Field(min_length=2)

UpdatePostSchema

Bases: BaseModel

Update post schema.

Source code in sharkservers/forum/schemas.py
154
155
156
157
class UpdatePostSchema(BaseModel):
    """Update post schema."""

    content: str

CreateCategorySchema

Bases: BaseModel

Create category schema.

Source code in sharkservers/forum/schemas.py
160
161
162
163
164
165
class CreateCategorySchema(BaseModel):
    """Create category schema."""

    name: str
    description: str | None
    type: CategoryTypeEnum = CategoryTypeEnum.PUBLIC

CreateThreadSchema

Bases: BaseModel

Create thread schema.

Source code in sharkservers/forum/schemas.py
168
169
170
171
172
173
174
175
176
177
class CreateThreadSchema(BaseModel):
    """Create thread schema."""

    title: str = Field(max_length=64, min_length=3)
    content: str = Field(min_length=2)
    category: int
    server_id: int | None = None
    question_experience: int | None = None
    question_age: int | None = None
    question_reason: int | None = None

AdminCreatePostSchema

Bases: CreatePostSchema

Admin create post schema.

Source code in sharkservers/forum/schemas.py
180
181
182
183
class AdminCreatePostSchema(CreatePostSchema):
    """Admin create post schema."""

    user_id: int

AdminUpdatePostSchema

Bases: UpdatePostSchema

Admin update post schema.

Source code in sharkservers/forum/schemas.py
186
187
188
189
190
class AdminUpdatePostSchema(UpdatePostSchema):
    """Admin update post schema."""

    user_id: int | None
    thread_id: int | None

ThreadQuery

Bases: OrderQuery

Thread query schema.

Source code in sharkservers/forum/schemas.py
193
194
195
196
197
198
199
200
201
202
203
class ThreadQuery(OrderQuery):
    """Thread query schema."""

    category: int | None = Query(None, description="Category ID", gt=0)
    server: int | None = Query(None, description="Server ID", gt=0)
    status: str | None = Query(
        None,
        description="Thread status",
        enum=ThreadStatusEnum,
    )
    closed: bool | None = Query(None, description="Is thread closed")

PostQuery

Bases: OrderQuery

Post query schema.

Source code in sharkservers/forum/schemas.py
206
207
class PostQuery(OrderQuery):
    """Post query schema."""

AdminThreadActionSchema

Bases: BaseModel

Admin thread action schema.

Source code in sharkservers/forum/schemas.py
210
211
212
213
214
class AdminThreadActionSchema(BaseModel):
    """Admin thread action schema."""

    action: ThreadActionEnum = ThreadActionEnum.CLOSE.value
    category: int | None = None

services

Forum services.

CategoryService

Bases: BaseService

Category service.

Source code in sharkservers/forum/services.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class CategoryService(BaseService):
    """Category service."""

    class Meta:
        """Category service meta."""

        model = Category
        not_found_exception = category_not_found_exception

    async def sync_counters(self) -> None:
        """Sync category threads counters."""
        try:
            categories = await self.Meta.model.objects.select_related("threads").all()
            for category in categories:
                threads_count = await category.threads.count()
                await category.update(threads_count=threads_count)
            logger.info(
                f"Finished sync counters to category threads -> {len(categories)}",
            )
        except Exception as e:  # noqa: BLE001
            logger.error(e)
Meta

Category service meta.

Source code in sharkservers/forum/services.py
31
32
33
34
35
class Meta:
    """Category service meta."""

    model = Category
    not_found_exception = category_not_found_exception
sync_counters() -> None async

Sync category threads counters.

Source code in sharkservers/forum/services.py
37
38
39
40
41
42
43
44
45
46
47
48
async def sync_counters(self) -> None:
    """Sync category threads counters."""
    try:
        categories = await self.Meta.model.objects.select_related("threads").all()
        for category in categories:
            threads_count = await category.threads.count()
            await category.update(threads_count=threads_count)
        logger.info(
            f"Finished sync counters to category threads -> {len(categories)}",
        )
    except Exception as e:  # noqa: BLE001
        logger.error(e)

ThreadMetaService

Bases: BaseService

Thread meta service.

Source code in sharkservers/forum/services.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class ThreadMetaService(BaseService):
    """Thread meta service."""

    class Meta:
        """Thread meta service meta."""

        model = ThreadMeta
        not_found_exception = thread_meta_not_found_exception

    async def fill_meta(self, thread_id: int, data: dict) -> ThreadMeta:
        """
        Fill thread meta.

        Args:
        ----
            thread_id (int): The thread id.
            data (dict): The data.

        Returns:
        -------
            ThreadMeta: The thread meta.
        """
        for key, value in data.items():
            thread_meta = await self.get_one(name=key, thread_meta__id=thread_id)
            await thread_meta.update(value=value)
Meta

Thread meta service meta.

Source code in sharkservers/forum/services.py
54
55
56
57
58
class Meta:
    """Thread meta service meta."""

    model = ThreadMeta
    not_found_exception = thread_meta_not_found_exception
fill_meta(thread_id: int, data: dict) -> ThreadMeta async

Fill thread meta.


thread_id (int): The thread id.
data (dict): The data.

ThreadMeta: The thread meta.
Source code in sharkservers/forum/services.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
async def fill_meta(self, thread_id: int, data: dict) -> ThreadMeta:
    """
    Fill thread meta.

    Args:
    ----
        thread_id (int): The thread id.
        data (dict): The data.

    Returns:
    -------
        ThreadMeta: The thread meta.
    """
    for key, value in data.items():
        thread_meta = await self.get_one(name=key, thread_meta__id=thread_id)
        await thread_meta.update(value=value)

ThreadService

Bases: BaseService

Thread service.

Source code in sharkservers/forum/services.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
class ThreadService(BaseService):
    """Thread service."""

    class Meta:
        """Thread service meta."""

        model = Thread
        not_found_exception = thread_not_found_exception

    async def create_thread(  # noqa: PLR0913
        self,
        data: CreateThreadSchema,
        author: User,
        category: Category,
        status: ThreadStatusEnum = ThreadStatusEnum.PENDING.value,
        thread_meta_service: ThreadMetaService = None,
        servers_service: ServerService = None,
        **kwargs,  # noqa: ANN003
    ) -> ThreadOut:
        """
        Create thread.

        Args:
        ----
            data (CreateThreadSchema): The data.
            author (User): The author.
            category (Category): The category.
            status (ThreadStatusEnum, optional): The status. Defaults to ThreadStatusEnum.PENDING.value.
            thread_meta_service (ThreadMetaService, optional): The thread meta service. Defaults to None.
            servers_service (ServerService, optional): The servers service. Defaults to None.
            **kwargs: The kwargs.

        Raises:
        ------
            HTTPException: The HTTP exception.

        Returns:
        -------
            ThreadOut: The thread.
        """
        if category.type == CategoryTypeEnum.APPLICATION:
            if not data.server_id:
                raise HTTPException(
                    status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail="server_id is required",
                )
            if not data.question_experience:
                raise HTTPException(
                    status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail="question_experience is required",
                )
            if not data.question_age:
                raise HTTPException(
                    status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail="question_age is required",
                )
            if not data.question_reason:
                raise HTTPException(
                    status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail="question_reason is required",
                )
            server = await servers_service.get_one(id=data.server_id)
            new_thread = await self.create(
                title=data.title,
                content=data.content,
                category=category,
                author=author,
                status=status,
                server=server,
                **kwargs,
            )
            await thread_meta_service.fill_meta(
                thread_id=new_thread.id,
                data={
                    "server_id": data.server_id,
                    "question_experience": data.question_experience,
                    "question_age": data.question_age,
                    "question_reason": data.question_reason,
                },
            )

            return await self.get_one(
                id=new_thread.id,
                related=[
                    "category",
                    "author",
                    "author__display_role",
                    "meta_fields",
                    "server",
                    "server__admin_role",
                    "author__player",
                    "author__player__steamrep_profile",
                ],
            )
        return await self.create(
            title=data.title,
            content=data.content,
            category=category,
            author=author,
            status=status,
            **kwargs,
        )

    async def sync_counters(self) -> None:
        """Sync thread posts counters."""
        try:
            threads = await self.Meta.model.objects.select_related("posts").all()
            for thread in threads:
                posts_count = await thread.posts.count()
                await thread.update(post_count=posts_count)
            logger.info(f"Finished sync counters to thread posts -> {len(threads)}")
        except Exception as e:  # noqa: BLE001
            logger.error(e)

    async def set_author_role(self, thread: Thread) -> None:
        """
        Set author role.

        Args:
        ----
            thread (Thread): The thread.

        Returns:
        -------
            None: None.
        """
        user = thread.author
        admin_role = thread.server.admin_role
        if user.display_role.tag == ProtectedDefaultRolesTagEnum.USER.value:
            await user.update(display_role=admin_role)
            await user.roles.add(admin_role)
        else:
            await user.roles.add(admin_role)
Meta

Thread service meta.

Source code in sharkservers/forum/services.py
81
82
83
84
85
class Meta:
    """Thread service meta."""

    model = Thread
    not_found_exception = thread_not_found_exception
create_thread(data: CreateThreadSchema, author: User, category: Category, status: ThreadStatusEnum = ThreadStatusEnum.PENDING.value, thread_meta_service: ThreadMetaService = None, servers_service: ServerService = None, **kwargs) -> ThreadOut async

Create thread.


data (CreateThreadSchema): The data.
author (User): The author.
category (Category): The category.
status (ThreadStatusEnum, optional): The status. Defaults to ThreadStatusEnum.PENDING.value.
thread_meta_service (ThreadMetaService, optional): The thread meta service. Defaults to None.
servers_service (ServerService, optional): The servers service. Defaults to None.
**kwargs: The kwargs.

HTTPException: The HTTP exception.

ThreadOut: The thread.
Source code in sharkservers/forum/services.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
async def create_thread(  # noqa: PLR0913
    self,
    data: CreateThreadSchema,
    author: User,
    category: Category,
    status: ThreadStatusEnum = ThreadStatusEnum.PENDING.value,
    thread_meta_service: ThreadMetaService = None,
    servers_service: ServerService = None,
    **kwargs,  # noqa: ANN003
) -> ThreadOut:
    """
    Create thread.

    Args:
    ----
        data (CreateThreadSchema): The data.
        author (User): The author.
        category (Category): The category.
        status (ThreadStatusEnum, optional): The status. Defaults to ThreadStatusEnum.PENDING.value.
        thread_meta_service (ThreadMetaService, optional): The thread meta service. Defaults to None.
        servers_service (ServerService, optional): The servers service. Defaults to None.
        **kwargs: The kwargs.

    Raises:
    ------
        HTTPException: The HTTP exception.

    Returns:
    -------
        ThreadOut: The thread.
    """
    if category.type == CategoryTypeEnum.APPLICATION:
        if not data.server_id:
            raise HTTPException(
                status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="server_id is required",
            )
        if not data.question_experience:
            raise HTTPException(
                status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="question_experience is required",
            )
        if not data.question_age:
            raise HTTPException(
                status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="question_age is required",
            )
        if not data.question_reason:
            raise HTTPException(
                status_code=starlette_status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="question_reason is required",
            )
        server = await servers_service.get_one(id=data.server_id)
        new_thread = await self.create(
            title=data.title,
            content=data.content,
            category=category,
            author=author,
            status=status,
            server=server,
            **kwargs,
        )
        await thread_meta_service.fill_meta(
            thread_id=new_thread.id,
            data={
                "server_id": data.server_id,
                "question_experience": data.question_experience,
                "question_age": data.question_age,
                "question_reason": data.question_reason,
            },
        )

        return await self.get_one(
            id=new_thread.id,
            related=[
                "category",
                "author",
                "author__display_role",
                "meta_fields",
                "server",
                "server__admin_role",
                "author__player",
                "author__player__steamrep_profile",
            ],
        )
    return await self.create(
        title=data.title,
        content=data.content,
        category=category,
        author=author,
        status=status,
        **kwargs,
    )
sync_counters() -> None async

Sync thread posts counters.

Source code in sharkservers/forum/services.py
181
182
183
184
185
186
187
188
189
190
async def sync_counters(self) -> None:
    """Sync thread posts counters."""
    try:
        threads = await self.Meta.model.objects.select_related("posts").all()
        for thread in threads:
            posts_count = await thread.posts.count()
            await thread.update(post_count=posts_count)
        logger.info(f"Finished sync counters to thread posts -> {len(threads)}")
    except Exception as e:  # noqa: BLE001
        logger.error(e)
set_author_role(thread: Thread) -> None async

Set author role.


thread (Thread): The thread.

None: None.
Source code in sharkservers/forum/services.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
async def set_author_role(self, thread: Thread) -> None:
    """
    Set author role.

    Args:
    ----
        thread (Thread): The thread.

    Returns:
    -------
        None: None.
    """
    user = thread.author
    admin_role = thread.server.admin_role
    if user.display_role.tag == ProtectedDefaultRolesTagEnum.USER.value:
        await user.update(display_role=admin_role)
        await user.roles.add(admin_role)
    else:
        await user.roles.add(admin_role)

PostService

Bases: BaseService

Post service.

Source code in sharkservers/forum/services.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
class PostService(BaseService):
    """Post service."""

    class Meta:
        """Post service meta."""

        model = Post
        not_found_exception = post_not_found_exception

    async def sync_counters(self) -> None:
        """Sync post likes counters."""
        try:
            posts = await self.Meta.model.objects.select_related("likes").all()
            for post in posts:
                likes_count = await post.likes.count()
                await post.update(likes_count=likes_count)
            logger.info(f"Finished sync counters to post likes -> {len(posts)}")
        except Exception as e:  # noqa: BLE001
            logger.error(e)
Meta

Post service meta.

Source code in sharkservers/forum/services.py
216
217
218
219
220
class Meta:
    """Post service meta."""

    model = Post
    not_found_exception = post_not_found_exception
sync_counters() -> None async

Sync post likes counters.

Source code in sharkservers/forum/services.py
222
223
224
225
226
227
228
229
230
231
async def sync_counters(self) -> None:
    """Sync post likes counters."""
    try:
        posts = await self.Meta.model.objects.select_related("likes").all()
        for post in posts:
            likes_count = await post.likes.count()
            await post.update(likes_count=likes_count)
        logger.info(f"Finished sync counters to post likes -> {len(posts)}")
    except Exception as e:  # noqa: BLE001
        logger.error(e)

LikeService

Bases: BaseService

Like service.

Source code in sharkservers/forum/services.py
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
class LikeService(BaseService):
    """Like service."""

    class Meta:
        """Like service meta."""

        model = Like
        not_found_exception = like_not_found_exception

    async def add_like_to_post(self, post: Post, author: User) -> tuple[LikeOut, int]:
        """
        Add like to post.

        Args:
        ----
            post (Post): The post.
            author (User): The author.

        Raises:
        ------
            like_already_exists_exception: The like already exists exception.

        Returns:
        -------
            tuple[LikeOut, int]: The like and likes count.
        """
        like_exists = False
        for like in post.likes:
            if like.user == author:
                like_exists = True
                break
        if like_exists:
            raise like_already_exists_exception
        new_like = await self.create(author=author)
        await post.likes.add(new_like)
        return new_like, post.likes

    async def remove_like_from_post(self, post: Post, author: User) -> dict:
        """
        Remove like from post.

        Args:
        ----
            post (Post): The post.
            author (User): The author.

        Raises:
        ------
            like_not_found_exception: The like not found exception.

        Returns:
        -------
            dict: The dict.
        """
        like_exists = False
        for like in post.likes:
            if like.author == author:
                like_exists = True
                await post.likes.remove(like)
                await self.delete(_id=like.id)
                break
        if not like_exists:
            raise like_not_found_exception
        return {"message": "Like removed"}
Meta

Like service meta.

Source code in sharkservers/forum/services.py
237
238
239
240
241
class Meta:
    """Like service meta."""

    model = Like
    not_found_exception = like_not_found_exception
add_like_to_post(post: Post, author: User) -> tuple[LikeOut, int] async

Add like to post.


post (Post): The post.
author (User): The author.

like_already_exists_exception: The like already exists exception.

tuple[LikeOut, int]: The like and likes count.
Source code in sharkservers/forum/services.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
async def add_like_to_post(self, post: Post, author: User) -> tuple[LikeOut, int]:
    """
    Add like to post.

    Args:
    ----
        post (Post): The post.
        author (User): The author.

    Raises:
    ------
        like_already_exists_exception: The like already exists exception.

    Returns:
    -------
        tuple[LikeOut, int]: The like and likes count.
    """
    like_exists = False
    for like in post.likes:
        if like.user == author:
            like_exists = True
            break
    if like_exists:
        raise like_already_exists_exception
    new_like = await self.create(author=author)
    await post.likes.add(new_like)
    return new_like, post.likes
remove_like_from_post(post: Post, author: User) -> dict async

Remove like from post.


post (Post): The post.
author (User): The author.

like_not_found_exception: The like not found exception.

dict: The dict.
Source code in sharkservers/forum/services.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
async def remove_like_from_post(self, post: Post, author: User) -> dict:
    """
    Remove like from post.

    Args:
    ----
        post (Post): The post.
        author (User): The author.

    Raises:
    ------
        like_not_found_exception: The like not found exception.

    Returns:
    -------
        dict: The dict.
    """
    like_exists = False
    for like in post.likes:
        if like.author == author:
            like_exists = True
            await post.likes.remove(like)
            await self.delete(_id=like.id)
            break
    if not like_exists:
        raise like_not_found_exception
    return {"message": "Like removed"}

views

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

admin

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

categories

Admin categories views.

admin_create_category(category_data: CreateCategorySchema, categories_service: CategoryService = Depends(get_categories_service)) -> CategoryOut async

Admin create category.


category_data (CreateCategorySchema): The category data.
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

Category: The category.
Source code in sharkservers/forum/views/admin/categories.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@router.post("", dependencies=[Security(get_admin_user, scopes=["categories:create"])])
async def admin_create_category(
    category_data: CreateCategorySchema,
    categories_service: CategoryService = Depends(get_categories_service),
) -> CategoryOut:
    """
    Admin create category.

    Args:
    ----
        category_data (CreateCategorySchema): The category data.
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

    Returns:
    -------
        Category: The category.


    """
    return await categories_service.create(**category_data.dict())
admin_delete_category(category: Category = Depends(get_valid_category)) -> CategoryOut async

Delete category.


category (Category, optional): The category. Defaults to Depends(get_valid_category).

Category: The category.
Source code in sharkservers/forum/views/admin/categories.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@router.delete(
    "/{category_id}",
    dependencies=[Security(get_admin_user, scopes=["categories:delete"])],
)
async def admin_delete_category(
    category: Category = Depends(get_valid_category),
) -> CategoryOut:
    """
    Delete category.

    Args:
    ----
        category (Category, optional): The category. Defaults to Depends(get_valid_category).

    Returns:
    -------
        Category: The category.
    """
    await category.delete()
    return category
posts

Admin posts views.

admin_delete_post(post: Post = Depends(get_valid_post)) -> PostOut async

Admin delete post.


post (Post, optional): The post. Defaults to Depends(get_valid_post).

Post: The post.
Source code in sharkservers/forum/views/admin/posts.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@router.delete(
    "/{post_id}",
    dependencies=[Security(get_admin_user, scopes=["posts:delete"])],
)
async def admin_delete_post(
    post: Post = Depends(get_valid_post),
) -> PostOut:
    """
    Admin delete post.

    Args:
    ----
        post (Post, optional): The post. Defaults to Depends(get_valid_post).

    Returns:
    -------
        Post: The post.
    """
    await post.delete()
    return post
admin_update_post(post_data: AdminUpdatePostSchema, posts_service: PostService = Depends(get_posts_service), threads_service: ThreadService = Depends(get_threads_service), users_service: UserService = Depends(get_users_service)) -> PostOut async

Admin update post.


post_data (AdminUpdatePostSchema): The post data.
posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
users_service (UserService, optional): The users service. Defaults to Depends(get_users_service).

PostOut: The post.
Source code in sharkservers/forum/views/admin/posts.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@router.put(
    "/{post_id}",
    dependencies=[Security(get_admin_user, scopes=["posts:update"])],
)
async def admin_update_post(
    post_data: AdminUpdatePostSchema,
    posts_service: PostService = Depends(get_posts_service),
    threads_service: ThreadService = Depends(get_threads_service),
    users_service: UserService = Depends(get_users_service),
) -> PostOut:
    """
    Admin update post.

    Args:
    ----
        post_data (AdminUpdatePostSchema): The post data.
        posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
        users_service (UserService, optional): The users service. Defaults to Depends(get_users_service).

    Returns:
    -------
        PostOut: The post.
    """
    thread = await get_valid_thread(post_data.thread_id, threads_service)
    author = await get_valid_user(post_data.user_id, users_service)
    return await posts_service.update(
        thread__id=thread.id,
        author=author,
        content=post_data.content,
    )
threads

Admin threads views.

router = APIRouter() module-attribute
admin_delete_thread(thread: Thread = Depends(get_valid_thread)) -> ThreadOut async

Delete thread.


thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

Thread: The thread.
Source code in sharkservers/forum/views/admin/threads.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@router.delete(
    "/{thread_id}",
    dependencies=[Security(get_admin_user, scopes=["threads:delete"])],
)
async def admin_delete_thread(
    thread: Thread = Depends(get_valid_thread),
) -> ThreadOut:
    """
    Delete thread.

    Args:
    ----
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

    Returns:
    -------
        Thread: The thread.
    """
    await thread.delete()
    return thread
admin_update_thread(update_thread_data: AdminUpdateThreadSchema, thread: Thread = Depends(get_valid_thread), threads_service: ThreadService = Depends(get_threads_service), users_service: UserService = Depends(get_users_service), categories_service: CategoryService = Depends(get_categories_service)) -> ThreadOut async

Update thread.


update_thread_data (AdminUpdateThreadSchema): The update thread data.
thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
users_service (UserService, optional): The users service. Defaults to Depends(get_users_service).
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

Thread: The thread.
Source code in sharkservers/forum/views/admin/threads.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@router.put(
    "/{thread_id}",
    dependencies=[Security(get_admin_user, scopes=["threads:update"])],
)
async def admin_update_thread(
    update_thread_data: AdminUpdateThreadSchema,
    thread: Thread = Depends(get_valid_thread),
    threads_service: ThreadService = Depends(get_threads_service),
    users_service: UserService = Depends(get_users_service),
    categories_service: CategoryService = Depends(get_categories_service),
) -> ThreadOut:
    """
    Update thread.

    Args:
    ----
        update_thread_data (AdminUpdateThreadSchema): The update thread data.
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
        users_service (UserService, optional): The users service. Defaults to Depends(get_users_service).
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

    Returns:
    -------
        Thread: The thread.
    """
    update_thread_data_dict = update_thread_data.dict(exclude_unset=True)
    author_id = update_thread_data_dict.pop("author", None)
    category_id = update_thread_data_dict.pop("category", None)
    if author_id:
        author = await get_valid_user(author_id, users_service=users_service)
        update_thread_data_dict["author"] = author
    if category_id:
        category = await get_valid_category(
            category_id,
            categories_service=categories_service,
        )
        update_thread_data_dict["category"] = category
    return await threads_service.update(
        id=thread.id,
        updated_data=update_thread_data_dict,
    )
run_thread_action(data: AdminThreadActionSchema, thread: Thread = Depends(get_valid_thread), categories_service: CategoryService = Depends(get_categories_service)) -> ThreadOut async

Run thread action.


data (AdminThreadActionSchema): The data.
thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

HTTPException: The HTTP exception.

ThreadOut: The thread.
Source code in sharkservers/forum/views/admin/threads.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@router.post(
    "/{thread_id}/action",
    dependencies=[Security(get_admin_user, scopes=["threads:close"])],
)
async def run_thread_action(
    data: AdminThreadActionSchema,
    thread: Thread = Depends(get_valid_thread),
    categories_service: CategoryService = Depends(get_categories_service),
) -> ThreadOut:
    """
    Run thread action.

    Args:
    ----
        data (AdminThreadActionSchema): The data.
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

    Raises:
    ------
        HTTPException: The HTTP exception.

    Returns:
    -------
        ThreadOut: The thread.

    """
    if data.action == ThreadActionEnum.MOVE:
        if not data.category:
            raise HTTPException(
                status_code=400,
                detail="Category is required for move action",
            )
        category = await get_valid_category(
            data.category,
            categories_service=categories_service,
        )
        await thread.run_action(
            action=data.action,
            new_category=category,
        )
    await thread.run_action(action=data.action)
    return thread

categories

Category views.

get_categories(params: Params = Depends(), queries: OrderQuery = Depends(), categories_service: CategoryService = Depends(get_categories_service)) -> Page[CategoryOut] async

Get all categories.


params (Params, optional): The params. Defaults to Depends().
queries (OrderQuery, optional): The queries. Defaults to Depends().
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

Page[CategoryOut]: The categories.
Source code in sharkservers/forum/views/categories.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@router.get("")
async def get_categories(
    params: Params = Depends(),
    queries: OrderQuery = Depends(),
    categories_service: CategoryService = Depends(get_categories_service),
) -> Page[CategoryOut]:
    """
    Get all categories.

    Args:
    ----
        params (Params, optional): The params. Defaults to Depends().
        queries (OrderQuery, optional): The queries. Defaults to Depends().
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).

    Returns:
    -------
        Page[CategoryOut]: The categories.
    """
    return await categories_service.get_all(
        params=params,
        order_by=queries.order_by,
    )
get_category(category: Category = Depends(get_valid_category)) -> CategoryOut async

Get a category.


category (Category, optional): The category. Defaults to Depends(get_valid_category).

CategoryOut: The category.
Source code in sharkservers/forum/views/categories.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@router.get("/{category_id}")
async def get_category(category: Category = Depends(get_valid_category)) -> CategoryOut:
    """
    Get a category.

    Args:
    ----
        category (Category, optional): The category. Defaults to Depends(get_valid_category).

    Returns:
    -------
        CategoryOut: The category.
    """
    return category

posts

Posts views.

get_posts(thread_id: int | None = None, params: Params = Depends(), queries: PostQuery = Depends(), posts_service: PostService = Depends(get_posts_service)) -> Page[PostOut] async

Get all posts.


thread_id (int, optional): The thread ID. Defaults to None.
params (Params, optional): The params. Defaults to Depends().
queries (PostQuery, optional): The queries. Defaults to Depends().
posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).

Page[PostOut]: The posts.
Source code in sharkservers/forum/views/posts.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@router.get("")
async def get_posts(
    thread_id: int | None = None,
    params: Params = Depends(),
    queries: PostQuery = Depends(),
    posts_service: PostService = Depends(get_posts_service),
) -> Page[PostOut]:
    """
    Get all posts.

    Args:
    ----
        thread_id (int, optional): The thread ID. Defaults to None.
        params (Params, optional): The params. Defaults to Depends().
        queries (PostQuery, optional): The queries. Defaults to Depends().
        posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).

    Returns:
    -------
        Page[PostOut]: The posts.
    """
    kwargs = {}
    if thread_id:
        kwargs["thread_post__id"] = thread_id
    return await posts_service.get_all(
        params=params,
        related=[
            "author",
            "author__display_role",
            "thread_post",
            "author__player",
            "author__player__steamrep_profile",
        ],
        order_by=queries.order_by,
        **kwargs,
    )
get_post_by_id(post: Post = Depends(get_valid_post)) -> PostOut async

Get post by ID.


post (Post): The post. Defaults to Depends(get_valid_post).

Post: The post.
Source code in sharkservers/forum/views/posts.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@router.get("/{post_id}")
async def get_post_by_id(post: Post = Depends(get_valid_post)) -> PostOut:
    """
    Get post by ID.

    Args:
    ----
        post (Post): The post. Defaults to Depends(get_valid_post).

    Returns:
    -------
        Post: The post.
    """
    return post
create_post(post_data: CreatePostSchema, user: User = Security(get_current_active_user, scopes=['posts:create']), posts_service: PostService = Depends(get_posts_service), threads_service: ThreadService = Depends(get_threads_service)) -> PostOut async

Create post.


post_data (CreatePostSchema): The post data.
user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

thread_is_closed_exception: The thread is closed exception.

PostOut: The post.
Source code in sharkservers/forum/views/posts.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@router.post("", dependencies=[Depends(limiter)])
async def create_post(
    post_data: CreatePostSchema,
    user: User = Security(get_current_active_user, scopes=["posts:create"]),
    posts_service: PostService = Depends(get_posts_service),
    threads_service: ThreadService = Depends(get_threads_service),
) -> PostOut:
    """
    Create post.

    Args:
    ----
        post_data (CreatePostSchema): The post data.
        user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
        posts_service (PostService, optional): The posts service. Defaults to Depends(get_posts_service).
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

    Raises:
    ------
        thread_is_closed_exception: The thread is closed exception.

    Returns:
    -------
        PostOut: The post.
    """
    post_data_dict = post_data.dict()
    thread_id = post_data_dict.pop("thread_id")
    thread = await threads_service.get_one(id=thread_id)
    if thread.is_closed:
        raise thread_is_closed_exception
    new_post = await posts_service.create(**post_data_dict, author=user)
    await thread.posts.add(new_post)
    dispatch(PostEventEnum.CREATE_POST, payload={"data": new_post})
    return new_post
update_post(post_data: UpdatePostSchema, post: Post = Depends(get_valid_post_author)) -> PostOut async

Update post.


post_data (UpdatePostSchema): The post data.
post (Post, optional): The post. Defaults to Depends(get_valid_post_author).

Post: The post.
Source code in sharkservers/forum/views/posts.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
@router.put("/{post_id}", dependencies=[Depends(limiter)])
async def update_post(
    post_data: UpdatePostSchema,
    post: Post = Depends(get_valid_post_author),
) -> PostOut:
    """
    Update post.

    Args:
    ----
        post_data (UpdatePostSchema): The post data.
        post (Post, optional): The post. Defaults to Depends(get_valid_post_author).

    Returns:
    -------
        Post: The post.
    """
    return await post.update(**post_data.dict(exclude_unset=True))
get_post_likes(post: Post = Depends(get_valid_post), likes_service: LikeService = Depends(get_likes_service), params: Params = Depends()) -> Page[LikeOut] async

Get post likes.


post (Post, optional): The post. Defaults to Depends(get_valid_post).
likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).
params (Params, optional): The params. Defaults to Depends().

Page[LikeOut]: The likes.
Source code in sharkservers/forum/views/posts.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
@router.get("/{post_id}/likes")
async def get_post_likes(
    post: Post = Depends(get_valid_post),
    likes_service: LikeService = Depends(get_likes_service),
    params: Params = Depends(),
) -> Page[LikeOut]:
    """
    Get post likes.

    Args:
    ----
        post (Post, optional): The post. Defaults to Depends(get_valid_post).
        likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).
        params (Params, optional): The params. Defaults to Depends().

    Returns:
    -------
        Page[LikeOut]: The likes.
    """
    return await paginate(
        likes_service.Meta.model.objects.select_related(
            ["author", "post_likes", "author__display_role"],
        ).filter(post_likes__id=post.id),
        params,
    )
like_post(post: Post = Depends(get_valid_post), user: User = Security(get_current_active_user, scopes=['posts:create']), likes_service: LikeService = Depends(get_likes_service)) -> LikeOut async

Like post.


post (Post, optional): The post. Defaults to Depends(get_valid_post).
user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).

LikeOut: The like.
Source code in sharkservers/forum/views/posts.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
@router.post("/{post_id}/like", dependencies=[Depends(limiter)])
async def like_post(
    post: Post = Depends(get_valid_post),
    user: User = Security(get_current_active_user, scopes=["posts:create"]),
    likes_service: LikeService = Depends(get_likes_service),
) -> LikeOut:
    """
    Like post.

    Args:
    ----
        post (Post, optional): The post. Defaults to Depends(get_valid_post).
        user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
        likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).

    Returns:
    -------
        LikeOut: The like.
    """
    new_like, likes = await likes_service.add_like_to_post(post=post, author=user)
    return new_like
dislike_post(post: Post = Depends(get_valid_post), user: User = Security(get_current_active_user, scopes=['posts:create']), likes_service: LikeService = Depends(get_likes_service)) -> dict async

Dislike post.


post (Post, optional): The post. Defaults to Depends(get_valid_post).
user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).

dict: The response.
Source code in sharkservers/forum/views/posts.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@router.post("/{post_id}/dislike", dependencies=[Depends(limiter)])
async def dislike_post(
    post: Post = Depends(get_valid_post),
    user: User = Security(get_current_active_user, scopes=["posts:create"]),
    likes_service: LikeService = Depends(get_likes_service),
) -> dict:
    """
    Dislike post.

    Args:
    ----
        post (Post, optional): The post. Defaults to Depends(get_valid_post).
        user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["posts:create"]).
        likes_service (LikeService, optional): The likes service. Defaults to Depends(get_likes_service).

    Returns:
    -------
        dict: The response.
    """
    return await likes_service.remove_like_from_post(post=post, author=user)

threads

Threads views.

get_threads(params: Params = Depends(), queries: ThreadQuery = Depends(), threads_service: ThreadService = Depends(get_threads_service)) -> Page[ThreadOut] async

Get all threads.


params (Params, optional): The params. Defaults to Depends().
queries (ThreadQuery, optional): The queries. Defaults to Depends().
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

Page[ThreadOut]: The threads.
Source code in sharkservers/forum/views/threads.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@router.get("")
async def get_threads(
    params: Params = Depends(),
    queries: ThreadQuery = Depends(),
    threads_service: ThreadService = Depends(get_threads_service),
) -> Page[ThreadOut]:
    """
    Get all threads.

    Args:
    ----
        params (Params, optional): The params. Defaults to Depends().
        queries (ThreadQuery, optional): The queries. Defaults to Depends().
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).

    Returns:
    -------
        Page[ThreadOut]: The threads.
    """
    kwargs = {}
    if queries.category:
        kwargs["category__id"] = queries.category
    if queries.order_by:
        if queries.category:
            kwargs["order_by"] = ["-is_pinned", queries.order_by]
        else:
            kwargs["order_by"] = queries.order_by
    if queries.server:
        kwargs["server__id"] = queries.server
    if queries.status:
        kwargs["status"] = queries.status
    if queries.closed is not None:
        kwargs["is_closed"] = queries.closed

    return await threads_service.get_all(
        params=params,
        related=[
            "category",
            "author",
            "author__display_role",
            "author__player",
            "author__player__steamrep_profile",
            "meta_fields",
            "server",
            "server__admin_role",
        ],
        **kwargs,
    )
create_thread(thread_data: CreateThreadSchema, user: User = Security(get_current_active_user, scopes=['threads:create']), threads_service: ThreadService = Depends(get_threads_service), categories_service: CategoryService = Depends(get_categories_service), thread_meta_service: ThreadMetaService = Depends(get_thread_meta_service), servers_service: ServerService = Depends(get_servers_service)) -> ThreadOut async

Create thread.


thread_data (CreateThreadSchema): The thread data.
user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["threads:create"]).
threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).
thread_meta_service (ThreadMetaService, optional): The thread meta service. Defaults to Depends(get_thread_meta_service).
servers_service (ServerService, optional): The servers service. Defaults to Depends(get_servers_service).

ThreadOut: The thread.
Source code in sharkservers/forum/views/threads.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
@router.post("", dependencies=[Depends(limiter)])
async def create_thread(  # noqa: PLR0913
    thread_data: CreateThreadSchema,
    user: User = Security(get_current_active_user, scopes=["threads:create"]),
    threads_service: ThreadService = Depends(get_threads_service),
    categories_service: CategoryService = Depends(get_categories_service),
    thread_meta_service: ThreadMetaService = Depends(get_thread_meta_service),
    servers_service: ServerService = Depends(get_servers_service),
) -> ThreadOut:
    """
    Create thread.

    Args:
    ----
        thread_data (CreateThreadSchema): The thread data.
        user (User, optional): The user. Defaults to Security(get_current_active_user, scopes=["threads:create"]).
        threads_service (ThreadService, optional): The threads service. Defaults to Depends(get_threads_service).
        categories_service (CategoryService, optional): The categories service. Defaults to Depends(get_categories_service).
        thread_meta_service (ThreadMetaService, optional): The thread meta service. Defaults to Depends(get_thread_meta_service).
        servers_service (ServerService, optional): The servers service. Defaults to Depends(get_servers_service).


    Returns:
    -------
        ThreadOut: The thread.
    """
    category = await categories_service.get_one(id=thread_data.category)
    return await threads_service.create_thread(
        data=thread_data,
        author=user,
        category=category,
        status=ThreadStatusEnum.PENDING.value,
        thread_meta_service=thread_meta_service,
        servers_service=servers_service,
    )
get_thread(thread: Thread = Depends(get_valid_thread)) -> ThreadOut async

Get thread by id.


thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

ThreadOut: The thread.
Source code in sharkservers/forum/views/threads.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
@router.get("/{thread_id}")
async def get_thread(thread: Thread = Depends(get_valid_thread)) -> ThreadOut:
    """
    Get thread by id.

    Args:
    ----
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread).

    Returns:
    -------
        ThreadOut: The thread.
    """
    return thread
update_thread(thread_data: UpdateThreadSchema, thread: Thread = Depends(get_valid_thread_with_author)) -> ThreadOut async

Update thread by id.


thread_data (UpdateThreadSchema): The thread data.
thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread_with_author).

ThreadOut: The thread.
Source code in sharkservers/forum/views/threads.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@router.put("/{thread_id}", dependencies=[Depends(limiter)])
async def update_thread(
    thread_data: UpdateThreadSchema,
    thread: Thread = Depends(get_valid_thread_with_author),
) -> ThreadOut:
    """
    Update thread by id.

    Args:
    ----
        thread_data (UpdateThreadSchema): The thread data.
        thread (Thread, optional): The thread. Defaults to Depends(get_valid_thread_with_author).

    Returns:
    -------
        ThreadOut: The thread.
    """
    return await thread.update(**thread_data.dict(exclude_unset=True))

logger

Logging configuration for the server.

logger_with_filename(filename: str, data: str) -> None

Log a message with the filename and data.


filename (str): The filename to log.
data (str): The data to log.

None
Source code in sharkservers/logger.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def logger_with_filename(filename: str, data: str) -> None:
    """
    Log a message with the filename and data.

    Args:
    ----
        filename (str): The filename to log.
        data (str): The data to log.

    Returns:
    -------
        None
    """
    logger.debug(f"{filename} | {data!s}")  # noqa: G004

main

Main moduke of the SharkServers API.

It contains the FastAPI application setup, including routes, middlewares, and WebSocket endpoints.

init_routes(_app: FastAPI) -> FastAPI

Initialize the routes for the FastAPI application.


_app (FastAPI): The FastAPI application instance.

FastAPI: The FastAPI application instance with the routes initialized.
Source code in sharkservers/main.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def init_routes(_app: FastAPI) -> FastAPI:
    """
    Initialize the routes for the FastAPI application.

    Args:
    ----
        _app (FastAPI): The FastAPI application instance.

    Returns:
    -------
        FastAPI: The FastAPI application instance with the routes initialized.
    """
    # V1 routes
    _app.include_router(root_router, tags=["root"])
    _app.include_router(auth_router_v1, prefix="/v1/auth", tags=["auth"])
    _app.include_router(users_me_router, prefix="/v1/users/me", tags=["users-me"])
    _app.include_router(users_router, prefix="/v1/users", tags=["users"])
    _app.include_router(scopes_router, prefix="/v1/scopes", tags=["scopes"])
    _app.include_router(roles_router, prefix="/v1/roles", tags=["roles"])
    _app.include_router(steamprofile_router, prefix="/v1/players", tags=["players"])
    _app.include_router(forum_router)
    _app.include_router(servers_router, prefix="/v1/servers", tags=["servers"])
    _app.include_router(chat_router, prefix="/v1/chat", tags=["chat"])
    _app.include_router(
        subscryptions_router,
        prefix="/v1/subscryption",
        tags=["subscryption"],
    )

    # Admin routes
    _app.include_router(
        admin_users_router,
        prefix="/v1/admin/users",
        tags=["admin-users"],
    )
    _app.include_router(
        admin_roles_router,
        prefix="/v1/admin/roles",
        tags=["admin-roles"],
    )
    _app.include_router(
        admin_scopes_router,
        prefix="/v1/admin/scopes",
        tags=["admin-scopes"],
    )
    _app.include_router(
        admin_steamprofiles_router,
        prefix="/v1/admin/players",
        tags=["admin-players"],
    )
    _app.include_router(admin_forum_router)
    _app.include_router(
        admin_servers_router,
        prefix="/v1/admin/servers",
        tags=["admin-servers"],
    )
    _app.include_router(
        admin_admins_groups_router,
        prefix="/v1/admin/servers",
        tags=["admin-servers-admin-groups"],
    )
    _app.include_router(
        admin_servers_admin_users_router,
        prefix="/v1/admin/servers",
        tags=["admin-servers-admins"],
    )
    return _app

update_tables_counters() -> None async

Cron job function to update the counters in the database tables.

Source code in sharkservers/main.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
@crontab("* 5 * * *")
async def update_tables_counters() -> None:
    """Cron job function to update the counters in the database tables."""
    try:
        logger.info("Updating tables counters")
        categories_service = await get_categories_service()
        threads_service = await get_threads_service()
        posts_service = await get_posts_service()
        users_service = await get_users_service()
        await categories_service.sync_counters()
        await threads_service.sync_counters()
        await posts_service.sync_counters()
        await users_service.sync_counters(
            threads_service=threads_service,
            posts_service=posts_service,
        )
        logger.info("Finished updating tables counters")
    except Exception as e:  # noqa: BLE001
        logger.error(e)

add_middlewares(_app: FastAPI) -> FastAPI

Add middlewares to the FastAPI application.


_app (FastAPI): The FastAPI application instance.

FastAPI: The FastAPI application instance with the middlewares added.
Source code in sharkservers/main.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
def add_middlewares(_app: FastAPI) -> FastAPI:
    """
    Add middlewares to the FastAPI application.

    Args:
    ----
        _app (FastAPI): The FastAPI application instance.

    Returns:
    -------
        FastAPI: The FastAPI application instance with the middlewares added.
    """

    @_app.middleware("http")
    async def update_user_last_online_time_middleware(
        request: Request,
        call_next,  # noqa: ANN001
    ) -> Response:
        settings: Settings = get_settings()  # Add type annotation for settings
        response: Response = await call_next(request)
        if "Authorization" not in request.headers:
            return response
        try:
            access_token_service: JWTService = await get_access_token_service(
                settings=settings,
            )  # Import AccessTokenService and use the correct type
            jwt_token: str = await AuthService.oauth2_scheme(request=request)
            token_data: TokenDataSchema = access_token_service.decode_token(
                jwt_token,
            )  # Import TokenData and use the correct type
            users_service: UserService = await get_users_service()
            user = await users_service.get_one(id=token_data.user_id)
            await user.update(last_online=now_datetime())
            return response  # noqa: TRY300
        except (JWTError, HTTPException):
            return response

    _app.add_middleware(GZipMiddleware)
    _app.add_middleware(
        CORSMiddleware,
        allow_origins=[
            "http://localhost:3000",
            "http://localhost:3001https://api-sharkservers.qwizi.dev",
            "https://beta.sharkservers.pl",
            "https://api.sharkservers.pl",
            "https://api-beta.sharkservers.pl",
        ],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    event_handler_id: int = id(_app)
    _app.add_middleware(
        EventHandlerASGIMiddleware,
        handlers=[local_handler],
        middleware_id=event_handler_id,
    )
    _app.middleware("http")(log_request_middleware)
    return _app

create_app() -> FastAPI

Create the FastAPI application.

Returns
FastAPI: The FastAPI application instance.
Source code in sharkservers/main.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def create_app() -> FastAPI:
    """
    Create the FastAPI application.

    Returns
    -------
        FastAPI: The FastAPI application instance.
    """
    _app = FastAPI(
        name="Shark API",
        version=VERSION,
        debug=True,
        generate_unique_id_function=custom_generate_unique_id,
        lifespan=app_lifespan,
    )
    _app = add_middlewares(_app)
    _app.mount("/static", StaticFiles(directory=st_abs_file_path), name="static")
    init_routes(_app)
    add_pagination(_app)
    _app.add_exception_handler(
        RequestValidationError, request_validation_exception_handler
    )
    _app.add_exception_handler(HTTPException, http_exception_handler)
    _app.add_exception_handler(Exception, unhandled_exception_handler)

    @_app.websocket("/ws")
    async def websocket_endpoint(
        websocket: WebSocket,
        chat_service: ChatService = Depends(get_chat_service),
        author=Depends(ws_get_current_user),  # noqa: ANN001
    ) -> None:
        try:
            logger.info(_app.state.broadcast)
            await websocket.accept()

            async with anyio.create_task_group() as task_group:
                # run until first is complete
                async def run_chatroom_ws_receiver() -> None:
                    await chatroom_ws_receiver(
                        websocket=websocket,
                        chat_service=chat_service,
                        author=author,
                    )
                    await task_group.cancel_scope.cancel()

                task_group.start_soon(run_chatroom_ws_receiver)
                await chatroom_ws_sender(
                    websocket,
                    chat_service=chat_service,
                    author=author,
                )
        except WebSocketDisconnect:
            pass

    return _app

manager

Module contains the ConnectionManager class, which manages WebSocket connections and provides methods for sending messages.

ConnectionManager

Manages WebSocket connections and provides methods for sending messages.

Source code in sharkservers/manager.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class ConnectionManager:
    """Manages WebSocket connections and provides methods for sending messages."""

    def __init__(self) -> None:
        """Initialize the ConnectionManager class."""
        self.active_connections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket) -> None:
        """
        Accepts a WebSocket connection and adds it to the list of active connections.

        Args:
        ----
            websocket (WebSocket): The WebSocket connection to be added.

        """  # noqa: D401
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket) -> None:
        """
        Removes a WebSocket connection from the list of active connections.

        Args:
        ----
            websocket (WebSocket): The WebSocket connection to be removed.

        Returns:
        -------
            None
        """  # noqa: D401
        self.active_connections.remove(websocket)

    async def send_personal_message(
        self,
        chat_schema: ChatEventSchema,
        websocket: WebSocket,
    ) -> None:
        """
        Send a personal message to a specific WebSocket connection.

        Args:
        ----
            chat_schema (ChatEventSchema): The chat event schema to be sent.
            websocket (WebSocket): The WebSocket connection to send the message to.

        Returns:
        -------
            None
        """
        await websocket.send_json(jsonable_encoder(chat_schema))

    async def broadcast(self, chat_schema: ChatEventSchema) -> None:
        """
        Send a message to all active WebSocket connections.

        Args:
        ----
            chat_schema (ChatEventSchema): The chat event schema to be sent.

        Returns:
        -------
            None
        """
        for connection in self.active_connections:
            await connection.send_json(jsonable_encoder(chat_schema))

__init__() -> None

Initialize the ConnectionManager class.

Source code in sharkservers/manager.py
17
18
19
def __init__(self) -> None:
    """Initialize the ConnectionManager class."""
    self.active_connections: list[WebSocket] = []

connect(websocket: WebSocket) -> None async

Accepts a WebSocket connection and adds it to the list of active connections.


websocket (WebSocket): The WebSocket connection to be added.
Source code in sharkservers/manager.py
21
22
23
24
25
26
27
28
29
30
31
async def connect(self, websocket: WebSocket) -> None:
    """
    Accepts a WebSocket connection and adds it to the list of active connections.

    Args:
    ----
        websocket (WebSocket): The WebSocket connection to be added.

    """  # noqa: D401
    await websocket.accept()
    self.active_connections.append(websocket)

disconnect(websocket: WebSocket) -> None

Removes a WebSocket connection from the list of active connections.


websocket (WebSocket): The WebSocket connection to be removed.

None
Source code in sharkservers/manager.py
33
34
35
36
37
38
39
40
41
42
43
44
45
def disconnect(self, websocket: WebSocket) -> None:
    """
    Removes a WebSocket connection from the list of active connections.

    Args:
    ----
        websocket (WebSocket): The WebSocket connection to be removed.

    Returns:
    -------
        None
    """  # noqa: D401
    self.active_connections.remove(websocket)

send_personal_message(chat_schema: ChatEventSchema, websocket: WebSocket) -> None async

Send a personal message to a specific WebSocket connection.


chat_schema (ChatEventSchema): The chat event schema to be sent.
websocket (WebSocket): The WebSocket connection to send the message to.

None
Source code in sharkservers/manager.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
async def send_personal_message(
    self,
    chat_schema: ChatEventSchema,
    websocket: WebSocket,
) -> None:
    """
    Send a personal message to a specific WebSocket connection.

    Args:
    ----
        chat_schema (ChatEventSchema): The chat event schema to be sent.
        websocket (WebSocket): The WebSocket connection to send the message to.

    Returns:
    -------
        None
    """
    await websocket.send_json(jsonable_encoder(chat_schema))

broadcast(chat_schema: ChatEventSchema) -> None async

Send a message to all active WebSocket connections.


chat_schema (ChatEventSchema): The chat event schema to be sent.

None
Source code in sharkservers/manager.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
async def broadcast(self, chat_schema: ChatEventSchema) -> None:
    """
    Send a message to all active WebSocket connections.

    Args:
    ----
        chat_schema (ChatEventSchema): The chat event schema to be sent.

    Returns:
    -------
        None
    """
    for connection in self.active_connections:
        await connection.send_json(jsonable_encoder(chat_schema))

middleware

log_request_middleware(request: Request, call_next) async

This middleware will log all requests and their processing time. E.g. log: 0.0.0.0:1234 - GET /ping 200 OK 1.00ms

Source code in sharkservers/middleware.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
async def log_request_middleware(request: Request, call_next):
    """
    This middleware will log all requests and their processing time.
    E.g. log:
    0.0.0.0:1234 - GET /ping 200 OK 1.00ms
    """
    logger.debug("middleware: log_request_middleware")
    url = (
        f"{request.url.path}?{request.query_params}"
        if request.query_params
        else request.url.path
    )
    start_time = time.time()
    response = await call_next(request)
    process_time = (time.time() - start_time) * 1000
    formatted_process_time = f"{process_time:.2f}"
    host = getattr(getattr(request, "client", None), "host", None)
    port = getattr(getattr(request, "client", None), "port", None)
    try:
        status_phrase = http.HTTPStatus(response.status_code).phrase
    except ValueError:
        status_phrase = ""
    logger.info(
        f'{host}:{port} - "{request.method} {url}" {response.status_code} {status_phrase} {formatted_process_time}ms'
    )
    return response

players

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Players dependencies.

get_steamrep_service() -> SteamRepService async

Retrieve an instance of the SteamRepService class.

Returns
SteamRepService: An instance of the SteamRepService class.
Source code in sharkservers/players/dependencies.py
13
14
15
16
17
18
19
20
21
async def get_steamrep_service() -> SteamRepService:
    """
    Retrieve an instance of the SteamRepService class.

    Returns
    -------
        SteamRepService: An instance of the SteamRepService class.
    """
    return SteamRepService()

get_players_service(steamrep_service: SteamRepService = Depends(get_steamrep_service), settings: Settings = Depends(get_settings)) -> PlayerService async

Retrieve the PlayerService instance with the specified dependencies.


steamrep_service (SteamRepService): The SteamRepService instance.
settings (Settings): The Settings instance.

PlayerService: The PlayerService instance.
Source code in sharkservers/players/dependencies.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
async def get_players_service(
    steamrep_service: SteamRepService = Depends(get_steamrep_service),
    settings: Settings = Depends(get_settings),
) -> PlayerService:
    """
    Retrieve the PlayerService instance with the specified dependencies.

    Args:
    ----
        steamrep_service (SteamRepService): The SteamRepService instance.
        settings (Settings): The Settings instance.

    Returns:
    -------
        PlayerService: The PlayerService instance.
    """
    return PlayerService(
        steamrep_service=steamrep_service,
        steam_api_key=settings.STEAM_API_KEY,
    )

get_valid_player(player_id: int, player_service: PlayerService = Depends(get_players_service)) -> Player async

Retrieve a valid player by their ID.


player_id (int): The ID of the player.
player_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

Player: The valid player object.
Source code in sharkservers/players/dependencies.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
async def get_valid_player(
    player_id: int,
    player_service: PlayerService = Depends(get_players_service),
) -> Player:
    """
    Retrieve a valid player by their ID.

    Args:
    ----
        player_id (int): The ID of the player.
        player_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

    Returns:
    -------
        Player: The valid player object.
    """
    return await player_service.get_one(
        id=player_id,
        select_related=["steamrep_profile"],
    )

get_valid_player_by_steamid(steamid64: str, player_service: PlayerService = Depends(get_players_service)) -> Player async

Retrieve a valid player by their SteamID.


steamid64 (str): The SteamID of the player.
player_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

Player: The player object.
Source code in sharkservers/players/dependencies.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
async def get_valid_player_by_steamid(
    steamid64: str,
    player_service: PlayerService = Depends(get_players_service),
) -> Player:
    """
    Retrieve a valid player by their SteamID.

    Args:
    ----
        steamid64 (str): The SteamID of the player.
        player_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

    Returns:
    -------
        Player: The player object.
    """
    return await player_service.get_one(
        steamid64=steamid64,
        related=["steamrep_profile"],
    )

enums

Players enums.

exceptions

Players exceptions.

models

Players models.

SteamRepProfile

Bases: Model, DateFieldsMixins

Represents a SteamRep profile.

Attributes
id (int): The unique identifier of the profile.
profile_url (str): The URL of the profile.
is_scammer (bool): Indicates if the profile is a scammer.
steamid64 (str): The 64-bit Steam ID of the profile.
Source code in sharkservers/players/models.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class SteamRepProfile(ormar.Model, DateFieldsMixins):
    """
    Represents a SteamRep profile.

    Attributes
    ----------
        id (int): The unique identifier of the profile.
        profile_url (str): The URL of the profile.
        is_scammer (bool): Indicates if the profile is a scammer.
        steamid64 (str): The 64-bit Steam ID of the profile.
    """

    class Meta(BaseMeta):
        """SteamRep profile metadata."""

        tablename = "steamrep_profiles"

    id: int = ormar.Integer(primary_key=True)
    profile_url: str | None = ormar.String(max_length=255, unique=True)
    is_scammer: bool | None = ormar.Boolean(default=False)
    steamid64: str | None = ormar.String(max_length=255, unique=True)
Meta

Bases: BaseMeta

SteamRep profile metadata.

Source code in sharkservers/players/models.py
21
22
23
24
class Meta(BaseMeta):
    """SteamRep profile metadata."""

    tablename = "steamrep_profiles"

Player

Bases: Model, DateFieldsMixins

Represents a player in the game.

Attributes
id (int): The unique identifier of the player.
steamrep_profile (Optional[SteamRepProfile]): The player's SteamRep profile.
username (str): The username of the player.
steamid3 (str): The player's SteamID3.
steamid32 (str): The player's SteamID32.
steamid64 (str): The player's SteamID64.
profile_url (str): The URL to the player's profile.
avatar (str): The URL to the player's avatar.
country_code (str): The country code of the player.
reputation (int): The reputation score of the player.
Source code in sharkservers/players/models.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Player(ormar.Model, DateFieldsMixins):
    """
    Represents a player in the game.

    Attributes
    ----------
        id (int): The unique identifier of the player.
        steamrep_profile (Optional[SteamRepProfile]): The player's SteamRep profile.
        username (str): The username of the player.
        steamid3 (str): The player's SteamID3.
        steamid32 (str): The player's SteamID32.
        steamid64 (str): The player's SteamID64.
        profile_url (str): The URL to the player's profile.
        avatar (str): The URL to the player's avatar.
        country_code (str): The country code of the player.
        reputation (int): The reputation score of the player.
    """

    class Meta(BaseMeta):
        """Player metadata."""

        tablename = "players"

    id: int = ormar.Integer(primary_key=True)
    steamrep_profile: SteamRepProfile | None = ormar.ForeignKey(
        SteamRepProfile,
        related_name="players",
    )
    username: str | None = ormar.String(max_length=32)
    steamid3: str | None = ormar.String(max_length=255, unique=True)
    steamid32: str | None = ormar.String(max_length=255, unique=True)
    steamid64: str | None = ormar.String(max_length=255, unique=True)
    profile_url: str | None = ormar.String(max_length=255, nullable=True, unique=True)
    avatar: str | None = ormar.String(max_length=255, nullable=True)
    country_code: str | None = ormar.String(max_length=15)
    reputation: int | None = ormar.Integer(default=1000)
Meta

Bases: BaseMeta

Player metadata.

Source code in sharkservers/players/models.py
50
51
52
53
class Meta(BaseMeta):
    """Player metadata."""

    tablename = "players"

schemas

Schemas for the players module.

PlayerOut

Bases: player_out

Player out schema.

Source code in sharkservers/players/schemas.py
11
12
class PlayerOut(player_out):
    """Player out schema."""

SteamPlayer

Bases: BaseModel

Steam player schema.

Source code in sharkservers/players/schemas.py
15
16
17
18
19
20
21
22
23
24
25
class SteamPlayer(BaseModel):
    """Steam player schema."""

    id: int | None = None
    username: str | None = None
    steamid64: str | None = None
    steamid32: str | None = None
    steamid3: str | None = None
    profile_url: str | None = None
    avatar: str | None = None
    country_code: str | None = None

CreatePlayerSchema

Bases: BaseModel

Create player schema.

Source code in sharkservers/players/schemas.py
28
29
30
31
class CreatePlayerSchema(BaseModel):
    """Create player schema."""

    steamid64: str

services

Player services.

SteamRepService

Bases: BaseService

Service for interacting with SteamRep API.

Attributes
api_url (str): The URL to the SteamRep API.
Methods
get_data(steamid64: str) -> tuple[str, bool]: Get data from SteamRep API for a given steamid64.
create_profile(steamid64: str) -> SteamRepProfile: Create a SteamRep profile for a given steamid64.
Source code in sharkservers/players/services.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class SteamRepService(BaseService):
    """
    Service for interacting with SteamRep API.

    Attributes
    ----------
        api_url (str): The URL to the SteamRep API.

    Methods
    -------
        get_data(steamid64: str) -> tuple[str, bool]: Get data from SteamRep API for a given steamid64.
        create_profile(steamid64: str) -> SteamRepProfile: Create a SteamRep profile for a given steamid64.

    """

    api_url = "https://steamrep.com/api/beta4/reputation/"

    class Meta:
        """SteamRep service metadata."""

        model = SteamRepProfile
        not_found_exception = None

    async def get_data(self, steamid64: str) -> tuple[str, bool]:
        """
        Get data from SteamRep API for a given steamid64.

        Args:
        ----
            steamid64 (str): The steamid64 of the player.

        Returns:
        -------
            tuple: A tuple containing the profile URL and a boolean indicating if the player is a scammer.
        """
        async with httpx.AsyncClient() as client:
            response = await client.get(
                self.api_url + steamid64 + "?json=1",
                timeout=20,
            )
            data = response.json()
            profile = data["steamrep"]["steamrepurl"]
            is_scammer = data["steamrep"]["reputation"]["summary"] == "SCAMMER"
            return profile, is_scammer

    async def create_profile(self, steamid64: str) -> SteamRepProfile:
        """
        Create a SteamRep profile for a given steamid64.

        Args:
        ----
            steamid64 (str): The steamid64 of the player.

        Returns:
        -------
            SteamRepProfile: The created SteamRep profile.
        """
        profile_created = await self.Meta.model.objects.filter(
            steamid64=steamid64,
        ).exists()
        if profile_created:
            raise HTTPException(
                detail="SteamRep profile already exists",
                status_code=401,
            )
        profile, is_scammer = await self.get_data(steamid64)
        return await self.create(
            profile_url=profile,
            is_scammer=is_scammer,
            steamid64=steamid64,
        )
Meta

SteamRep service metadata.

Source code in sharkservers/players/services.py
39
40
41
42
43
class Meta:
    """SteamRep service metadata."""

    model = SteamRepProfile
    not_found_exception = None
get_data(steamid64: str) -> tuple[str, bool] async

Get data from SteamRep API for a given steamid64.


steamid64 (str): The steamid64 of the player.

tuple: A tuple containing the profile URL and a boolean indicating if the player is a scammer.
Source code in sharkservers/players/services.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
async def get_data(self, steamid64: str) -> tuple[str, bool]:
    """
    Get data from SteamRep API for a given steamid64.

    Args:
    ----
        steamid64 (str): The steamid64 of the player.

    Returns:
    -------
        tuple: A tuple containing the profile URL and a boolean indicating if the player is a scammer.
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            self.api_url + steamid64 + "?json=1",
            timeout=20,
        )
        data = response.json()
        profile = data["steamrep"]["steamrepurl"]
        is_scammer = data["steamrep"]["reputation"]["summary"] == "SCAMMER"
        return profile, is_scammer
create_profile(steamid64: str) -> SteamRepProfile async

Create a SteamRep profile for a given steamid64.


steamid64 (str): The steamid64 of the player.

SteamRepProfile: The created SteamRep profile.
Source code in sharkservers/players/services.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
async def create_profile(self, steamid64: str) -> SteamRepProfile:
    """
    Create a SteamRep profile for a given steamid64.

    Args:
    ----
        steamid64 (str): The steamid64 of the player.

    Returns:
    -------
        SteamRepProfile: The created SteamRep profile.
    """
    profile_created = await self.Meta.model.objects.filter(
        steamid64=steamid64,
    ).exists()
    if profile_created:
        raise HTTPException(
            detail="SteamRep profile already exists",
            status_code=401,
        )
    profile, is_scammer = await self.get_data(steamid64)
    return await self.create(
        profile_url=profile,
        is_scammer=is_scammer,
        steamid64=steamid64,
    )

PlayerService

Bases: BaseService

Service for interacting with Player data.

Attributes
steam_api_key (str): The Steam API key.
steam_api (WebAPI): The Steam API.
steamrep_service (SteamRepService): The SteamRep service.
Methods
get_steam_player_info(steamid64: str) -> SteamPlayer: Get Steam player information for a given steamid64.
create_player(steamid64: str) -> Player: Create a player with the given steamid64.
Source code in sharkservers/players/services.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
class PlayerService(BaseService):
    """
    Service for interacting with Player data.

    Attributes
    ----------
        steam_api_key (str): The Steam API key.
        steam_api (WebAPI): The Steam API.
        steamrep_service (SteamRepService): The SteamRep service.

    Methods
    -------
        get_steam_player_info(steamid64: str) -> SteamPlayer: Get Steam player information for a given steamid64.
        create_player(steamid64: str) -> Player: Create a player with the given steamid64.
    """

    steam_api_key: str = None
    steam_api: WebAPI = None
    steamrep_service: SteamRepService

    class Meta:
        """Player service metadata."""

        model = Player
        not_found_exception = player_not_found_exception

    def __init__(self, steam_api_key: str, steamrep_service: SteamRepService) -> None:
        """Initialize the PlayerService."""
        self.steam_api_key = steam_api_key
        self.steamrep_service = steamrep_service

    def get_steam_player_info(self, steamid64: str) -> SteamPlayer:
        """
        Get Steam player information for a given steamid64.

        Args:
        ----
            steamid64 (str): The steamid64 of the player.

        Returns:
        -------
            SteamPlayer: The Steam player information.
        """
        try:
            steam_api = WebAPI(self.steam_api_key)
            results = steam_api.call(
                "ISteamUser.GetPlayerSummaries",
                steamids=steamid64,
            )
            logger.info(results)
            if not len(results["response"]["players"]):
                msg = "Invalid steamid64"
                raise Exception(msg)  # noqa: TRY002

            player = results["response"]["players"][0]

            profile_url = player["profileurl"]
            avatar = player["avatarfull"]
            loccountrycode = player.get("loccountrycode", "N/A")

            steamid64_from_player = player["steamid"]
            steamid32 = SteamID(steamid64_from_player).as_steam2
            steamid3 = SteamID(steamid64_from_player).as_steam3
            return SteamPlayer(
                username=player["personaname"],
                steamid64=steamid64_from_player,
                steamid32=steamid32,
                steamid3=steamid3,
                profile_url=profile_url,
                avatar=avatar,
                country_code=loccountrycode,
            )
        except HTTPError as e:
            logger.error(e)
            raise HTTPException(detail="Invalid steam api key", status_code=401) from e

    async def create_player(self, steamid64: str) -> Player:
        """
        Create a player with the given steamid64.

        Args:
        ----
            steamid64 (str): The steamid64 of the player.

        Returns:
        -------
            Player: The created player.
        """
        if await self.Meta.model.objects.filter(steamid64=steamid64).exists():
            raise HTTPException(detail="Player already exists", status_code=401)
        try:
            player_info = self.get_steam_player_info(steamid64)
            steamrep_profile = await self.steamrep_service.create_profile(steamid64)
            reputation = 1000
            if steamrep_profile.is_scammer:
                reputation = 800
            return await self.create(
                username=player_info.username,
                steamid64=player_info.steamid64,
                steamid32=player_info.steamid32,
                steamid3=player_info.steamid3,
                profile_url=player_info.profile_url,
                avatar=player_info.avatar,
                country_code=player_info.country_code,
                steamrep_profile=steamrep_profile,
                reputation=reputation,
            )
        except HTTPError as err:
            raise HTTPException(
                detail="Http error occurred while creating player",
                status_code=401,
            ) from err
Meta

Player service metadata.

Source code in sharkservers/players/services.py
115
116
117
118
119
class Meta:
    """Player service metadata."""

    model = Player
    not_found_exception = player_not_found_exception
__init__(steam_api_key: str, steamrep_service: SteamRepService) -> None

Initialize the PlayerService.

Source code in sharkservers/players/services.py
121
122
123
124
def __init__(self, steam_api_key: str, steamrep_service: SteamRepService) -> None:
    """Initialize the PlayerService."""
    self.steam_api_key = steam_api_key
    self.steamrep_service = steamrep_service
get_steam_player_info(steamid64: str) -> SteamPlayer

Get Steam player information for a given steamid64.


steamid64 (str): The steamid64 of the player.

SteamPlayer: The Steam player information.
Source code in sharkservers/players/services.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def get_steam_player_info(self, steamid64: str) -> SteamPlayer:
    """
    Get Steam player information for a given steamid64.

    Args:
    ----
        steamid64 (str): The steamid64 of the player.

    Returns:
    -------
        SteamPlayer: The Steam player information.
    """
    try:
        steam_api = WebAPI(self.steam_api_key)
        results = steam_api.call(
            "ISteamUser.GetPlayerSummaries",
            steamids=steamid64,
        )
        logger.info(results)
        if not len(results["response"]["players"]):
            msg = "Invalid steamid64"
            raise Exception(msg)  # noqa: TRY002

        player = results["response"]["players"][0]

        profile_url = player["profileurl"]
        avatar = player["avatarfull"]
        loccountrycode = player.get("loccountrycode", "N/A")

        steamid64_from_player = player["steamid"]
        steamid32 = SteamID(steamid64_from_player).as_steam2
        steamid3 = SteamID(steamid64_from_player).as_steam3
        return SteamPlayer(
            username=player["personaname"],
            steamid64=steamid64_from_player,
            steamid32=steamid32,
            steamid3=steamid3,
            profile_url=profile_url,
            avatar=avatar,
            country_code=loccountrycode,
        )
    except HTTPError as e:
        logger.error(e)
        raise HTTPException(detail="Invalid steam api key", status_code=401) from e
create_player(steamid64: str) -> Player async

Create a player with the given steamid64.


steamid64 (str): The steamid64 of the player.

Player: The created player.
Source code in sharkservers/players/services.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
async def create_player(self, steamid64: str) -> Player:
    """
    Create a player with the given steamid64.

    Args:
    ----
        steamid64 (str): The steamid64 of the player.

    Returns:
    -------
        Player: The created player.
    """
    if await self.Meta.model.objects.filter(steamid64=steamid64).exists():
        raise HTTPException(detail="Player already exists", status_code=401)
    try:
        player_info = self.get_steam_player_info(steamid64)
        steamrep_profile = await self.steamrep_service.create_profile(steamid64)
        reputation = 1000
        if steamrep_profile.is_scammer:
            reputation = 800
        return await self.create(
            username=player_info.username,
            steamid64=player_info.steamid64,
            steamid32=player_info.steamid32,
            steamid3=player_info.steamid3,
            profile_url=player_info.profile_url,
            avatar=player_info.avatar,
            country_code=player_info.country_code,
            steamrep_profile=steamrep_profile,
            reputation=reputation,
        )
    except HTTPError as err:
        raise HTTPException(
            detail="Http error occurred while creating player",
            status_code=401,
        ) from err

views

Players views.

get_players(params: Params = Depends(), players_service: PlayerService = Depends(get_players_service)) -> Page[PlayerOut] async

Retrieve a list of players based on the provided parameters.


params (Params): The parameters for filtering and pagination.
players_service (PlayerService): The service for retrieving player data.

Page[PlayerOut]: A paginated list of player data.
Source code in sharkservers/players/views.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@router.get("")
async def get_players(
    params: Params = Depends(),
    players_service: PlayerService = Depends(get_players_service),
) -> Page[PlayerOut]:
    """
    Retrieve a list of players based on the provided parameters.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        players_service (PlayerService): The service for retrieving player data.

    Returns:
    -------
        Page[PlayerOut]: A paginated list of player data.
    """
    return await players_service.get_all(params=params, related=["steamrep_profile"])

create_player(player_data: CreatePlayerSchema, players_service: PlayerService = Depends(get_players_service)) -> PlayerOut async

Create a new player.


player_data (CreatePlayerSchema): The data for creating a player.
players_service (PlayerService): The service for managing players.

PlayerOut: The created player.
Source code in sharkservers/players/views.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@router.post("")
async def create_player(
    player_data: CreatePlayerSchema,
    players_service: PlayerService = Depends(get_players_service),
) -> PlayerOut:
    """
    Create a new player.

    Args:
    ----
        player_data (CreatePlayerSchema): The data for creating a player.
        players_service (PlayerService): The service for managing players.

    Returns:
    -------
        PlayerOut: The created player.
    """
    return await players_service.create_player(player_data.steamid64)

get_player(player: Player = Depends(get_valid_player_by_steamid)) -> PlayerOut async

Retrieve a player by their Steam ID.


player (Player): The player object obtained from the `get_valid_player_by_steamid` dependency.

Player: The retrieved player object.
Source code in sharkservers/players/views.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@router.get("/{steamid64}")
async def get_player(
    player: Player = Depends(get_valid_player_by_steamid),
) -> PlayerOut:
    """
    Retrieve a player by their Steam ID.

    Args:
    ----
        player (Player): The player object obtained from the `get_valid_player_by_steamid` dependency.

    Returns:
    -------
        Player: The retrieved player object.

    """
    return player

views_admin

Admin views for players.

admin_get_steam_profiles(params: Params = Depends(), players_service: PlayerService = Depends(get_players_service)) -> Page[PlayerOut] async

Retrieve all player profiles with their associated SteamRep profiles.


params (Params): The parameters for filtering and pagination.
players_service (PlayerService): The service for retrieving player profiles.

Page[PlayerOut]: A paginated list of player profiles with their associated SteamRep profiles.
Source code in sharkservers/players/views_admin.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@router.get("", dependencies=[Security(get_admin_user, scopes=["players:all"])])
async def admin_get_steam_profiles(
    params: Params = Depends(),
    players_service: PlayerService = Depends(get_players_service),
) -> Page[PlayerOut]:
    """
    Retrieve all player profiles with their associated SteamRep profiles.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        players_service (PlayerService): The service for retrieving player profiles.

    Returns:
    -------
        Page[PlayerOut]: A paginated list of player profiles with their associated SteamRep profiles.
    """
    return await players_service.get_all(params, related=["steamrep_profile"])

admin_get_steam_profile(profile_id: int) -> PlayerOut async

Retrieve the Steam profile of a player with the given profile ID.


profile_id (int): The ID of the player's Steam profile.

PlayerOut: The player's Steam profile.

player_not_found_exception: If the player's profile is not found.
Source code in sharkservers/players/views_admin.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@router.get(
    "/{profile_id}",
    dependencies=[Security(get_admin_user, scopes=["players:all"])],
)
async def admin_get_steam_profile(
    profile_id: int,
) -> PlayerOut:
    """
    Retrieve the Steam profile of a player with the given profile ID.

    Args:
    ----
        profile_id (int): The ID of the player's Steam profile.

    Returns:
    -------
        PlayerOut: The player's Steam profile.

    Raises:
    ------
        player_not_found_exception: If the player's profile is not found.
    """
    try:
        return await Player.objects.get(id=profile_id)
    except NoMatch:
        raise player_not_found_exception  # noqa: B904

admin_create_player(profile_data: CreatePlayerSchema, background_tasks: BackgroundTasks, players_service: PlayerService = Depends(get_players_service)) -> dict async

Admin endpoint to create a player.


profile_data (CreatePlayerSchema): The data for creating a player.
background_tasks (BackgroundTasks): The background tasks object.
players_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

dict: A dictionary with a message indicating that the player was created.
Source code in sharkservers/players/views_admin.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@router.post("", dependencies=[Security(get_admin_user, scopes=["players:all"])])
async def admin_create_player(
    profile_data: CreatePlayerSchema,
    background_tasks: BackgroundTasks,
    players_service: PlayerService = Depends(get_players_service),
) -> dict:
    """
    Admin endpoint to create a player.

    Args:
    ----
        profile_data (CreatePlayerSchema): The data for creating a player.
        background_tasks (BackgroundTasks): The background tasks object.
        players_service (PlayerService, optional): The player service dependency. Defaults to Depends(get_players_service).

    Returns:
    -------
        dict: A dictionary with a message indicating that the player was created.
    """
    background_tasks.add_task(
        players_service.create_player,
        profile_data.dict()["steamid64"],
    )
    return {"msg": "Player created"}

admin_delete_steam_profile(profile_id: int) -> PlayerOut async

Delete a Steam profile with the given profile ID.


profile_id (int): The ID of the profile to delete.

PlayerOut: The deleted player profile.

player_not_found_exception: If the player profile with the given ID is not found.
Source code in sharkservers/players/views_admin.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@router.delete(
    "/{profile_id}",
    dependencies=[Security(get_admin_user, scopes=["players:all"])],
)
async def admin_delete_steam_profile(
    profile_id: int,
) -> PlayerOut:
    """
    Delete a Steam profile with the given profile ID.

    Args:
    ----
        profile_id (int): The ID of the profile to delete.

    Returns:
    -------
        PlayerOut: The deleted player profile.

    Raises:
    ------
        player_not_found_exception: If the player profile with the given ID is not found.
    """
    try:
        profile = await Player.objects.get(id=profile_id)
        await profile.delete()
        return profile  # noqa: TRY300
    except NoMatch as err:
        raise player_not_found_exception from err

roles

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Dependencies for roles module.

get_roles_service() -> RoleService async

Get the RoleService instance.

Source code in sharkservers/roles/dependencies.py
 9
10
11
async def get_roles_service() -> RoleService:
    """Get the RoleService instance."""
    return RoleService()

get_valid_role(role_id: int, roles_service: RoleService = Depends(get_roles_service)) -> Model async

Get a valid role by its ID.


role_id (int): The ID of the role.
roles_service (RoleService, optional): The RoleService instance to use. Defaults to Depends(get_roles_service).

Model: The valid role model.
Source code in sharkservers/roles/dependencies.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
async def get_valid_role(
    role_id: int,
    roles_service: RoleService = Depends(get_roles_service),
) -> Model:
    """
    Get a valid role by its ID.

    Args:
    ----
        role_id (int): The ID of the role.
        roles_service (RoleService, optional): The RoleService instance to use. Defaults to Depends(get_roles_service).

    Returns:
    -------
        Model: The valid role model.

    """
    return await roles_service.get_one(id=role_id, related=["scopes"])

enums

Enum classes for roles module.

ProtectedDefaultRolesEnum

Bases: int, Enum

Enumeration class for protected default roles.

Source code in sharkservers/roles/enums.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class ProtectedDefaultRolesEnum(int, Enum):
    """Enumeration class for protected default roles."""

    ADMIN = 1
    USER = 2
    BANNED = 3
    VIP = 4

    @classmethod
    def has_value(cls, value) -> bool:  # noqa: ANN001
        """Check if the given value is a valid member of the enumeration."""
        return value in cls._value2member_map_
has_value(value) -> bool classmethod

Check if the given value is a valid member of the enumeration.

Source code in sharkservers/roles/enums.py
13
14
15
16
@classmethod
def has_value(cls, value) -> bool:  # noqa: ANN001
    """Check if the given value is a valid member of the enumeration."""
    return value in cls._value2member_map_

ProtectedDefaultRolesTagEnum

Bases: str, Enum

Enumeration class for protected default role tags.

Source code in sharkservers/roles/enums.py
19
20
21
22
23
24
25
26
27
28
29
30
class ProtectedDefaultRolesTagEnum(str, Enum):
    """Enumeration class for protected default role tags."""

    ADMIN = "admin"
    USER = "user"
    BANNED = "banned"
    VIP = "vip"

    @classmethod
    def has_value(cls, value) -> bool:  # noqa: ANN001
        """Check if the given value is a valid member of the enumeration."""
        return value in cls._value2member_map_
has_value(value) -> bool classmethod

Check if the given value is a valid member of the enumeration.

Source code in sharkservers/roles/enums.py
27
28
29
30
@classmethod
def has_value(cls, value) -> bool:  # noqa: ANN001
    """Check if the given value is a valid member of the enumeration."""
    return value in cls._value2member_map_

RolesExceptionsDetailEnum

Bases: str, Enum

Enumeration class for roles exceptions detail.

Source code in sharkservers/roles/enums.py
33
34
35
36
37
38
class RolesExceptionsDetailEnum(str, Enum):
    """Enumeration class for roles exceptions detail."""

    NOT_FOUND = "Role not found"
    ALREADY_EXISTS = "Role already exists"
    PROTECTED = "U cannot delete protected role"

RolesEventsEnum

Bases: str, Enum

Enumeration class for roles events.

Source code in sharkservers/roles/enums.py
41
42
43
44
45
46
47
class RolesEventsEnum(str, Enum):
    """Enumeration class for roles events."""

    GET_ALL_PRE = "ROLES_GET_ALL_PRE"
    GET_ALL_POST = "ROLES_GET_ALL_POST"
    GET_ONE_PRE = "ROLES_GET_ONE_PRE"
    GET_ONE_POST = "ROLES_GET_ONE_POST"

RolesAdminEventsEnum

Bases: str, Enum

Enumeration class for roles admin events.

Source code in sharkservers/roles/enums.py
50
51
52
53
54
55
56
57
58
59
60
61
62
class RolesAdminEventsEnum(str, Enum):
    """Enumeration class for roles admin events."""

    GET_ALL_PRE = "ROLES_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "ROLES_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "ROLES_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "ROLES_ADMIN_GET_ONE_POST"
    CREATE_PRE = "ROLES_ADMIN_CREATE_PRE"
    CREATE_POST = "ROLES_ADMIN_CREATE_POST"
    UPDATE_PRE = "ROLES_ADMIN_UPDATE_PRE"
    UPDATE_POST = "ROLES_ADMIN_UPDATE_POST"
    DELETE_PRE = "ROLES_ADMIN_DELETE_PRE"
    DELETE_POST = "ROLES_ADMIN_DELETE_POST"

exceptions

Exceptions for roles module.

models

Roles models.

Role

Bases: Model, DateFieldsMixins

Represent a role in the system.

Attributes
id (int): The unique identifier of the role.
tag (str, optional): The tag associated with the role.
name (str, optional): The name of the role.
color (str, optional): The color associated with the role.
scopes (List[Scope], optional): The list of scopes associated with the role.
is_staff (bool, optional): Indicates if the role is a staff role.
Source code in sharkservers/roles/models.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Role(ormar.Model, DateFieldsMixins):
    """
    Represent a role in the system.

    Attributes
    ----------
        id (int): The unique identifier of the role.
        tag (str, optional): The tag associated with the role.
        name (str, optional): The name of the role.
        color (str, optional): The color associated with the role.
        scopes (List[Scope], optional): The list of scopes associated with the role.
        is_staff (bool, optional): Indicates if the role is a staff role.
    """

    class Meta(BaseMeta):
        """Role metadata."""

        tablename = "roles"

    id: int = ormar.Integer(primary_key=True)
    tag: str | None = ormar.String(max_length=64, unique=True)
    name: str | None = ormar.String(max_length=64)
    color: str | None = ormar.String(max_length=256, default="#999999")
    scopes: list[Scope] | None = ormar.ManyToMany(Scope)
    is_staff: bool | None = ormar.Boolean(default=False)
Meta

Bases: BaseMeta

Role metadata.

Source code in sharkservers/roles/models.py
24
25
26
27
class Meta(BaseMeta):
    """Role metadata."""

    tablename = "roles"

schemas

Schemas for the roles module.

RoleOut

Bases: role_out

RoleOut schema.

Source code in sharkservers/roles/schemas.py
13
14
class RoleOut(role_out):
    """RoleOut schema."""

RoleOutWithScopes

Bases: role_out_with_scopes

RoleOutWithScopes schema.

Source code in sharkservers/roles/schemas.py
17
18
class RoleOutWithScopes(role_out_with_scopes):
    """RoleOutWithScopes schema."""

RoleOutWithoutScopesAndUserRoles

Bases: role_out_without_scopes_and_user_roles

RoleOutWithoutScopesAndUserRoles schema.

Source code in sharkservers/roles/schemas.py
21
22
class RoleOutWithoutScopesAndUserRoles(role_out_without_scopes_and_user_roles):
    """RoleOutWithoutScopesAndUserRoles schema."""

StaffUserInRolesSchema

Bases: BaseModel

StaffUserInRolesSchema schema.

Source code in sharkservers/roles/schemas.py
25
26
27
28
29
30
class StaffUserInRolesSchema(BaseModel):
    """StaffUserInRolesSchema schema."""

    id: int
    username: str
    avatar: str

StaffRolesSchema

Bases: BaseModel

StaffRolesSchema schema.

Source code in sharkservers/roles/schemas.py
33
34
35
36
37
38
39
class StaffRolesSchema(BaseModel):
    """StaffRolesSchema schema."""

    id: int
    name: str
    color: str
    user_display_role: list[StaffUserInRolesSchema]

CreateRoleSchema

Bases: BaseModel

CreateRoleSchema schema.

Source code in sharkservers/roles/schemas.py
42
43
44
45
46
47
48
49
class CreateRoleSchema(BaseModel):
    """CreateRoleSchema schema."""

    tag: str
    name: str
    color: str
    is_staff: bool = False
    scopes: list[int] | None = None

UpdateRoleSchema

Bases: BaseModel

UpdateRoleSchema schema.

Source code in sharkservers/roles/schemas.py
52
53
54
55
56
57
58
59
class UpdateRoleSchema(BaseModel):
    """UpdateRoleSchema schema."""

    tag: str | None
    name: str | None
    color: str | None
    is_staff: bool | None
    scopes: list[int] | None

services

Services for roles.

RoleService

Bases: BaseService

Service class for managing roles.

Attributes
create_default_roles (Callable): Creates default roles with specified scopes.
get_staff_roles (Callable): Retrieves staff roles.
admin_create_role (Callable): Creates a role with specified scopes.
Source code in sharkservers/roles/services.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class RoleService(BaseService):
    """
    Service class for managing roles.

    Attributes
    ----------
        create_default_roles (Callable): Creates default roles with specified scopes.
        get_staff_roles (Callable): Retrieves staff roles.
        admin_create_role (Callable): Creates a role with specified scopes.

    """

    class Meta:
        """RoleService metadata."""

        model = Role
        not_found_exception = role_not_found_exception

    async def create_default_roles(self, scopes_service: ScopeService) -> None:
        """
        Creates default roles with specified scopes.

        Args:
        ----
            scopes_service (ScopeService): The service for managing scopes.
        """  # noqa: D401
        roles_to_create: list[tuple[ProtectedDefaultRolesTagEnum, str]] = [
            (ProtectedDefaultRolesTagEnum.ADMIN.value, "Admin", "#C53030"),
            (ProtectedDefaultRolesTagEnum.USER.value, "User", "#99999"),
            (ProtectedDefaultRolesTagEnum.BANNED.value, "Banned", "#000000"),
            (ProtectedDefaultRolesTagEnum.VIP.value, "VIP", "#ffda83"),
        ]
        for role in roles_to_create:
            logger_with_filename(filename=self.__class__.__name__, data=role)
            if not await self.Meta.model.objects.filter(tag=role[0]).exists():
                default_role = await self.Meta.model.objects.create(
                    tag=role[0],
                    name=role[1],
                    color=role[2],
                    is_staff=role[0] == ProtectedDefaultRolesTagEnum.ADMIN.value,
                )
                scopes = await scopes_service.get_default_scopes_for_role(
                    role_id=default_role.id,
                )
                if role[0] != ProtectedDefaultRolesTagEnum.BANNED.value:
                    for scope in scopes:
                        await default_role.scopes.add(scope)

    async def get_staff_roles(self, params: Params) -> Page[Role]:
        """
        Retrieves staff roles.

        Args:
        ----
            params: Additional parameters for filtering or pagination.

        Returns:
        -------
            List: A list of staff roles.
        """  # noqa: D401
        return await self.get_all(
            params=params,
            related=["user_display_role"],
            is_staff=True,
        )

    async def admin_create_role(
        self,
        role_data: CreateRoleSchema,
        scopes_service: ScopeService,
    ) -> Role:
        """
        Create a role with specified scopes.

        Args:
        ----
            role_data (CreateRoleSchema): The data for creating the role.
            scopes_service (ScopeService): The service for managing scopes.

        Returns:
        -------
            Role: The created role.
        """
        scopes = []
        role_data_dict = role_data.dict()
        scopes_from_role_data = role_data_dict.pop("scopes", None)
        if scopes_from_role_data:
            scopes = await scopes_service.Meta.model.objects.filter(
                id__in=role_data.scopes,
            ).all()
        role = await self.create(**role_data_dict)
        if scopes:
            for scope in scopes:
                await role.scopes.add(scope)
        return role
Meta

RoleService metadata.

Source code in sharkservers/roles/services.py
31
32
33
34
35
class Meta:
    """RoleService metadata."""

    model = Role
    not_found_exception = role_not_found_exception
create_default_roles(scopes_service: ScopeService) -> None async

Creates default roles with specified scopes.


scopes_service (ScopeService): The service for managing scopes.
Source code in sharkservers/roles/services.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
async def create_default_roles(self, scopes_service: ScopeService) -> None:
    """
    Creates default roles with specified scopes.

    Args:
    ----
        scopes_service (ScopeService): The service for managing scopes.
    """  # noqa: D401
    roles_to_create: list[tuple[ProtectedDefaultRolesTagEnum, str]] = [
        (ProtectedDefaultRolesTagEnum.ADMIN.value, "Admin", "#C53030"),
        (ProtectedDefaultRolesTagEnum.USER.value, "User", "#99999"),
        (ProtectedDefaultRolesTagEnum.BANNED.value, "Banned", "#000000"),
        (ProtectedDefaultRolesTagEnum.VIP.value, "VIP", "#ffda83"),
    ]
    for role in roles_to_create:
        logger_with_filename(filename=self.__class__.__name__, data=role)
        if not await self.Meta.model.objects.filter(tag=role[0]).exists():
            default_role = await self.Meta.model.objects.create(
                tag=role[0],
                name=role[1],
                color=role[2],
                is_staff=role[0] == ProtectedDefaultRolesTagEnum.ADMIN.value,
            )
            scopes = await scopes_service.get_default_scopes_for_role(
                role_id=default_role.id,
            )
            if role[0] != ProtectedDefaultRolesTagEnum.BANNED.value:
                for scope in scopes:
                    await default_role.scopes.add(scope)
get_staff_roles(params: Params) -> Page[Role] async

Retrieves staff roles.


params: Additional parameters for filtering or pagination.

List: A list of staff roles.
Source code in sharkservers/roles/services.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
async def get_staff_roles(self, params: Params) -> Page[Role]:
    """
    Retrieves staff roles.

    Args:
    ----
        params: Additional parameters for filtering or pagination.

    Returns:
    -------
        List: A list of staff roles.
    """  # noqa: D401
    return await self.get_all(
        params=params,
        related=["user_display_role"],
        is_staff=True,
    )
admin_create_role(role_data: CreateRoleSchema, scopes_service: ScopeService) -> Role async

Create a role with specified scopes.


role_data (CreateRoleSchema): The data for creating the role.
scopes_service (ScopeService): The service for managing scopes.

Role: The created role.
Source code in sharkservers/roles/services.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
async def admin_create_role(
    self,
    role_data: CreateRoleSchema,
    scopes_service: ScopeService,
) -> Role:
    """
    Create a role with specified scopes.

    Args:
    ----
        role_data (CreateRoleSchema): The data for creating the role.
        scopes_service (ScopeService): The service for managing scopes.

    Returns:
    -------
        Role: The created role.
    """
    scopes = []
    role_data_dict = role_data.dict()
    scopes_from_role_data = role_data_dict.pop("scopes", None)
    if scopes_from_role_data:
        scopes = await scopes_service.Meta.model.objects.filter(
            id__in=role_data.scopes,
        ).all()
    role = await self.create(**role_data_dict)
    if scopes:
        for scope in scopes:
            await role.scopes.add(scope)
    return role

views

Views for roles.

get_roles(params: Params = Depends(), roles_service: RoleService = Depends(get_roles_service)) -> Page[RoleOut] async

Retrieve all roles based on the provided parameters.


params (Params): The parameters for filtering and pagination.
roles_service (RoleService): The service for retrieving roles.

Page[RoleOut]: A paginated list of RoleOut objects.
Source code in sharkservers/roles/views.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@router.get("")
async def get_roles(
    params: Params = Depends(),
    roles_service: RoleService = Depends(get_roles_service),
) -> Page[RoleOut]:
    """
    Retrieve all roles based on the provided parameters.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        roles_service (RoleService): The service for retrieving roles.

    Returns:
    -------
        Page[RoleOut]: A paginated list of RoleOut objects.
    """
    return await roles_service.get_all(params=params)

get_role(role: Role = Depends(get_valid_role)) -> RoleOutWithScopes async

Retrieve a role by its ID.


role (Role): The role object.

RoleOutWithScopes: The role object with associated scopes.
Source code in sharkservers/roles/views.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@router.get("/{role_id}")
async def get_role(role: Role = Depends(get_valid_role)) -> RoleOutWithScopes:
    """
    Retrieve a role by its ID.

    Args:
    ----
        role (Role): The role object.

    Returns:
    -------
        RoleOutWithScopes: The role object with associated scopes.

    """
    return role

views_admin

Views for admin roles.

admin_get_roles(params: Params = Depends(), roles_service: RoleService = Depends(get_roles_service)) -> Page[RoleOut] async

Retrieve all roles with pagination.


params (Params, optional): The query parameters for pagination. Defaults to Depends().
admin_user (User, optional): The authenticated admin user. Defaults to Security(get_admin_user, scopes=["roles:all"]).
roles_service (RoleService, optional): The service for managing roles. Defaults to Depends(get_roles_service).

Page[RoleOut]: The paginated list of roles.
Source code in sharkservers/roles/views_admin.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@router.get("", dependencies=[Security(get_admin_user, scopes=["roles:all"])])
async def admin_get_roles(
    params: Params = Depends(),
    roles_service: RoleService = Depends(get_roles_service),
) -> Page[RoleOut]:
    """
    Retrieve all roles with pagination.

    Args:
    ----
        params (Params, optional): The query parameters for pagination. Defaults to Depends().
        admin_user (User, optional): The authenticated admin user. Defaults to Security(get_admin_user, scopes=["roles:all"]).
        roles_service (RoleService, optional): The service for managing roles. Defaults to Depends(get_roles_service).

    Returns:
    -------
        Page[RoleOut]: The paginated list of roles.
    """
    return await roles_service.get_all(params=params)

admin_get_role(role: Role = Depends(get_valid_role)) -> RoleOutWithScopes async

Retrieve the details of a role.


role (Role): The role object to retrieve details for.

RoleOutWithScopes: The role object with associated scopes.
Source code in sharkservers/roles/views_admin.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@router.get(
    "/{role_id}",
    dependencies=[Security(get_admin_user, scopes=["roles:retrieve"])],
)
async def admin_get_role(
    role: Role = Depends(get_valid_role),
) -> RoleOutWithScopes:
    """
    Retrieve the details of a role.

    Args:
    ----
        role (Role): The role object to retrieve details for.

    Returns:
    -------
        RoleOutWithScopes: The role object with associated scopes.

    """
    return role

admin_create_role(role_data: CreateRoleSchema, roles_service: RoleService = Depends(get_roles_service), scopes_service: ScopeService = Depends(get_scopes_service)) -> RoleOutWithScopes async

Admin endpoint to create a new role.


role_data (CreateRoleSchema): The data for creating the role.
roles_service (RoleService, optional): The service for managing roles. Defaults to Depends(get_roles_service).
scopes_service (ScopeService, optional): The service for managing scopes. Defaults to Depends(get_scopes_service).

The newly created role.
Source code in sharkservers/roles/views_admin.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@router.post("", dependencies=[Security(get_admin_user, scopes=["roles:create"])])
async def admin_create_role(
    role_data: CreateRoleSchema,
    roles_service: RoleService = Depends(get_roles_service),
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> RoleOutWithScopes:
    """
    Admin endpoint to create a new role.

    Args:
    ----
        role_data (CreateRoleSchema): The data for creating the role.
        roles_service (RoleService, optional): The service for managing roles. Defaults to Depends(get_roles_service).
        scopes_service (ScopeService, optional): The service for managing scopes. Defaults to Depends(get_scopes_service).

    Returns:
    -------
        The newly created role.
    """
    return await roles_service.admin_create_role(role_data, scopes_service)

admin_delete_role(role: Role = Depends(get_valid_role), roles_service: RoleService = Depends(get_roles_service)) -> RoleOutWithScopes async

Delete a role.


role (Role): The role to be deleted.
roles_service (RoleService): The service used to delete the role.

RoleOutWithScopes: The deleted role.
Source code in sharkservers/roles/views_admin.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
@router.delete(
    "/{role_id}",
    dependencies=[Security(get_admin_user, scopes=["roles:delete"])],
)
async def admin_delete_role(
    role: Role = Depends(get_valid_role),
    roles_service: RoleService = Depends(get_roles_service),
) -> RoleOutWithScopes:
    """
    Delete a role.

    Args:
    ----
        role (Role): The role to be deleted.
        roles_service (RoleService): The service used to delete the role.

    Returns:
    -------
        RoleOutWithScopes: The deleted role.

    """
    await roles_service.delete(_id=role.id)
    return role

admin_update_role(update_role_data: UpdateRoleSchema, role: Role = Depends(get_valid_role), scopes_service: ScopeService = Depends(get_scopes_service)) -> RoleOutWithScopes async

Update a role with the provided data.


update_role_data (UpdateRoleSchema): The data to update the role with.
role (Role): The role to be updated.
scopes_service (ScopeService): The service to handle scopes.

Role: The updated role.
Source code in sharkservers/roles/views_admin.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@router.put(
    "/{role_id}",
    dependencies=[Security(get_admin_user, scopes=["roles:update"])],
)
async def admin_update_role(
    update_role_data: UpdateRoleSchema,
    role: Role = Depends(get_valid_role),
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> RoleOutWithScopes:
    """
    Update a role with the provided data.

    Args:
    ----
        update_role_data (UpdateRoleSchema): The data to update the role with.
        role (Role): The role to be updated.
        scopes_service (ScopeService): The service to handle scopes.

    Returns:
    -------
        Role: The updated role.
    """
    update_role_data_dict = update_role_data.dict(exclude_unset=True)
    scopes_ids = update_role_data_dict.pop("scopes", None)
    scopes_list = []
    if scopes_ids:
        # Get scopes by ids
        for scope_id in scopes_ids:
            scope = await scopes_service.get_one(id=scope_id)
            scopes_list.append(scope)

        # First remove all scopes from role
        for _scope in role.scopes:
            await role.scopes.remove(_scope)
        # Then add new scopes to role
        for scope in scopes_list:
            await role.scopes.add(scope)
    await role.update(**update_role_data_dict)
    return role

schemas

Module contains the schema definitions used in the SharkServers API.

It includes the following classes: - CreateAdmin: Represents the schema for creating an admin user. - HTTPErrorSchema: Represents the base schema for HTTP error responses. - HTTPError404Schema: Represents the schema for a 404 Not Found error response. - HTTPError400Schema: Represents the schema for a 400 Bad Request error response. - HTTPError401Schema: Represents the schema for a 401 Unauthorized error response. - OrderQuery: Represents the schema for the order query parameter.

CreateAdmin

Bases: BaseModel

Represents the data required to create an admin.

Attributes
admin_username (str): The username of the admin.
admin_password (str): The password of the admin.
admin_email (str): The email address of the admin.
Source code in sharkservers/schemas.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class CreateAdmin(BaseModel):
    """
    Represents the data required to create an admin.

    Attributes
    ----------
        admin_username (str): The username of the admin.
        admin_password (str): The password of the admin.
        admin_email (str): The email address of the admin.
    """

    admin_username: str
    admin_password: str
    admin_email: str

HTTPErrorSchema

Bases: BaseModel

Schema for representing an HTTP error response.

Attributes
detail (str): The error message.
Source code in sharkservers/schemas.py
37
38
39
40
41
42
43
44
45
46
class HTTPErrorSchema(BaseModel):
    """
    Schema for representing an HTTP error response.

    Attributes
    ----------
        detail (str): The error message.
    """

    detail: str

HTTPError404Schema

Bases: HTTPErrorSchema

Schema for representing a 404 Not Found HTTP error.

Source code in sharkservers/schemas.py
49
50
51
52
class HTTPError404Schema(HTTPErrorSchema):
    """Schema for representing a 404 Not Found HTTP error."""

    status_code: int = status.HTTP_404_NOT_FOUND

HTTPError400Schema

Bases: HTTPErrorSchema

Schema for HTTP 400 Bad Request error.

Source code in sharkservers/schemas.py
55
56
57
58
class HTTPError400Schema(HTTPErrorSchema):
    """Schema for HTTP 400 Bad Request error."""

    status_code: int = status.HTTP_400_BAD_REQUEST

HTTPError401Schema

Bases: HTTPErrorSchema

Schema for representing a 401 Unauthorized HTTP error.

Inherits from the base HTTPErrorSchema class.

Source code in sharkservers/schemas.py
61
62
63
64
65
66
67
68
class HTTPError401Schema(HTTPErrorSchema):
    """
    Schema for representing a 401 Unauthorized HTTP error.

    Inherits from the base HTTPErrorSchema class.
    """

    status_code: int = status.HTTP_401_UNAUTHORIZED

OrderQuery

Bases: BaseModel

Represents the query parameters for ordering data.

:param order_by: The field to order by.

Source code in sharkservers/schemas.py
71
72
73
74
75
76
77
78
79
80
81
82
class OrderQuery(BaseModel):
    """
    Represents the query parameters for ordering data.

    :param order_by: The field to order by.
    """

    order_by: str | None = Query(
        OrderEnum.ID_DESC,
        description="Order by",
        enum=OrderEnum,
    )

scopes

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Dependencies for scopes endpoints.

get_scopes_service() -> ScopeService async

Get the ScopeService instance.

Source code in sharkservers/scopes/dependencies.py
 9
10
11
async def get_scopes_service() -> ScopeService:
    """Get the ScopeService instance."""
    return ScopeService()

get_valid_scope(scope_id: int, scopes_service: ScopeService = Depends(get_scopes_service)) -> Scope async

Get a valid scope by ID.

Source code in sharkservers/scopes/dependencies.py
14
15
16
17
18
19
async def get_valid_scope(
    scope_id: int,
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> Scope:
    """Get a valid scope by ID."""
    return await scopes_service.get_one(id=scope_id)

enums

Enums for scopes module.

ScopeEnum

Bases: Enum

Scope enum.

Source code in sharkservers/scopes/enums.py
 5
 6
 7
 8
 9
10
11
12
class ScopeEnum(Enum):
    """Scope enum."""

    CREATE = "create"
    UPDATE = "update"
    DELETE = "delete"
    RETRIEVE = "retrieve"
    ALL = "all"

ScopesExceptionsDetailEnum

Bases: str, Enum

Scopes exceptions detail enum.

Source code in sharkservers/scopes/enums.py
15
16
17
18
class ScopesExceptionsDetailEnum(str, Enum):
    """Scopes exceptions detail enum."""

    NOT_FOUND = "Scope not found"

ScopesEventsEnum

Bases: str, Enum

Scopes events enum.

Source code in sharkservers/scopes/enums.py
21
22
23
24
25
26
27
class ScopesEventsEnum(str, Enum):
    """Scopes events enum."""

    GET_ALL_PRE = "SCOPES_GET_ALL_PRE"
    GET_ALL_POST = "SCOPES_GET_ALL_POST"
    GET_ONE_PRE = "SCOPES_GET_ONE_PRE"
    GET_ONE_POST = "SCOPES_GET_ONE_POST"

ScopesAdminEventsEnum

Bases: str, Enum

Scopes events enum.

Source code in sharkservers/scopes/enums.py
30
31
32
33
34
35
36
37
38
39
40
41
42
class ScopesAdminEventsEnum(str, Enum):
    """Scopes events enum."""

    GET_ALL_PRE = "SCOPES_ADMIN_GET_ALL_PRE"
    GET_ALL_POST = "SCOPES_ADMIN_GET_ALL_POST"
    GET_ONE_PRE = "SCOPES_ADMIN_GET_ONE_PRE"
    GET_ONE_POST = "SCOPES_ADMIN_GET_ONE_POST"
    CREATE_PRE = "SCOPES_ADMIN_CREATE_PRE"
    CREATE_POST = "SCOPES_ADMIN_CREATE_POST"
    DELETE_PRE = "SCOPES_ADMIN_DELETE_PRE"
    DELETE_POST = "SCOPES_ADMIN_DELETE_POST"
    UPDATE_PRE = "SCOPES_ADMIN_UPDATE_PRE"
    UPDATE_POST = "SCOPES_ADMIN_UPDATE_POST"

exceptions

Exceptions for scopes module.

models

Scopes models.

Scope

Bases: Model

Represents a scope in the application.

Attributes
id (int): The unique identifier of the scope.
app_name (str): The name of the application.
value (str): The value of the scope.
description (str): The description of the scope.
protected (bool): Indicates if the scope is protected.
Methods
get_string(): Returns a string representation of the scope.
get_tuple(): Returns a tuple representation of the scope.
get_dict(): Returns a dictionary representation of the scope.
Source code in sharkservers/scopes/models.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class Scope(ormar.Model):
    """
    Represents a scope in the application.

    Attributes
    ----------
        id (int): The unique identifier of the scope.
        app_name (str): The name of the application.
        value (str): The value of the scope.
        description (str): The description of the scope.
        protected (bool): Indicates if the scope is protected.

    Methods
    -------
        get_string(): Returns a string representation of the scope.
        get_tuple(): Returns a tuple representation of the scope.
        get_dict(): Returns a dictionary representation of the scope.
    """

    class Meta(BaseMeta):
        """Meta class for Scope model."""

        tablename = "scopes"

    id = ormar.Integer(primary_key=True)
    app_name = ormar.String(max_length=64)
    value = ormar.String(max_length=120)
    description = ormar.String(max_length=256)
    protected = ormar.Boolean(default=True)

    def get_string(self) -> str:
        """
        Return a string representation of the object.

        Returns
        -------
            str: The string representation of the object.
        """
        return f"{self.app_name}:{self.value}"

    def get_tuple(self) -> tuple:
        """
        Return a tuple containing the formatted app name and value, along with the description.

        Returns
        -------
            tuple: A tuple containing the formatted app name and value, along with the description.
        """
        return f"{self.app_name}:{self.value}", self.description

    def get_dict(self) -> dict:
        """
        Return a dictionary representation of the object.

        Returns
        -------
            dict: A dictionary with the object's attributes.
        """
        _dict = {}
        dict_index = f"{self.app_name}:{self.value}"
        _dict[dict_index] = self.description
        return _dict
Meta

Bases: BaseMeta

Meta class for Scope model.

Source code in sharkservers/scopes/models.py
26
27
28
29
class Meta(BaseMeta):
    """Meta class for Scope model."""

    tablename = "scopes"
get_string() -> str

Return a string representation of the object.

Returns
str: The string representation of the object.
Source code in sharkservers/scopes/models.py
37
38
39
40
41
42
43
44
45
def get_string(self) -> str:
    """
    Return a string representation of the object.

    Returns
    -------
        str: The string representation of the object.
    """
    return f"{self.app_name}:{self.value}"
get_tuple() -> tuple

Return a tuple containing the formatted app name and value, along with the description.

Returns
tuple: A tuple containing the formatted app name and value, along with the description.
Source code in sharkservers/scopes/models.py
47
48
49
50
51
52
53
54
55
def get_tuple(self) -> tuple:
    """
    Return a tuple containing the formatted app name and value, along with the description.

    Returns
    -------
        tuple: A tuple containing the formatted app name and value, along with the description.
    """
    return f"{self.app_name}:{self.value}", self.description
get_dict() -> dict

Return a dictionary representation of the object.

Returns
dict: A dictionary with the object's attributes.
Source code in sharkservers/scopes/models.py
57
58
59
60
61
62
63
64
65
66
67
68
def get_dict(self) -> dict:
    """
    Return a dictionary representation of the object.

    Returns
    -------
        dict: A dictionary with the object's attributes.
    """
    _dict = {}
    dict_index = f"{self.app_name}:{self.value}"
    _dict[dict_index] = self.description
    return _dict

schemas

Schemas for scopes module.

CreateScopeSchema

Bases: BaseModel

Create scope schema.

Source code in sharkservers/scopes/schemas.py
11
12
13
14
15
16
17
class CreateScopeSchema(BaseModel):
    """Create scope schema."""

    app_name: str
    value: str
    description: str
    protected: bool = True

UpdateScopeSchema

Bases: BaseModel

Update scope schema.

Source code in sharkservers/scopes/schemas.py
20
21
22
23
24
25
26
class UpdateScopeSchema(BaseModel):
    """Update scope schema."""

    app_name: str | None
    value: str | None
    description: str | None
    protected: str | None

services

Scopes services.

ScopeService

Bases: BaseService

Service class for managing scopes.

Attributes

extra_scopes : list[CreateScopeSchema] | None List of extra scopes. default_scopes : list[str] List of default scopes.

Methods
add_default_scopes(apps)
    Add default scope.
add_extra_scope(app_name, scope_values)
    Add extra scope.
get_extra_scopes()
    Get extra scopes.
create_scopes_for_app(app_name, additional_scopes)
    Create scopes for app.
create_default_scopes(applications, additional=None)
    Create default scopes for applications.
get_scopes_list(roles)
    Get list of scopes for the given roles.
get_default_scopes_for_role(role_id)
    Get default scopes for the given role.
Source code in sharkservers/scopes/services.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
class ScopeService(BaseService):
    """
    Service class for managing scopes.

    Attributes
    ----------
    extra_scopes : list[CreateScopeSchema] | None
        List of extra scopes.
    default_scopes : list[str]
        List of default scopes.

    Methods
    -------
        add_default_scopes(apps)
            Add default scope.
        add_extra_scope(app_name, scope_values)
            Add extra scope.
        get_extra_scopes()
            Get extra scopes.
        create_scopes_for_app(app_name, additional_scopes)
            Create scopes for app.
        create_default_scopes(applications, additional=None)
            Create default scopes for applications.
        get_scopes_list(roles)
            Get list of scopes for the given roles.
        get_default_scopes_for_role(role_id)
            Get default scopes for the given role.
    """

    extra_scopes: list[CreateScopeSchema] | None
    default_scopes: list[str]

    class Meta:
        """Meta class for ScopeService."""

        model = Scope
        not_found_exception = scope_not_found_exception

    def add_default_scopes(self, apps: list[str]) -> ScopeService:
        """
        Add default scope.

        Args:
        ----
        apps : list[str]
            List of application names.

        Returns:
        -------
        ScopeService
            The ScopeService instance.

        """
        for app in apps:
            self.default_scopes.append(app)
        return self

    def add_extra_scope(
        self,
        app_name: str,
        scope_values: list[tuple[str, str, str]],
    ) -> ScopeService:
        """
        Add extra scope.

        Parameters
        ----------
        app_name : str
            Name of the application.
        scope_values : list[tuple[str, str, str]]
            List of tuples containing scope values.

        Returns
        -------
        ScopeService
            The ScopeService instance.

        """
        if not self.extra_scopes:
            self.extra_scopes = []
        for scope_value in scope_values:
            app_name = scope_value[0]
            value = scope_value[1]
            description = scope_value[2]
            self.extra_scopes.append(
                CreateScopeSchema(
                    app_name=app_name,
                    value=value,
                    description=description,
                ),
            )
        return self

    def get_extra_scopes(self) -> list[CreateScopeSchema]:
        """
        Get extra scopes.

        Returns
        -------
        list[CreateScopeSchema]
            List of extra scopes.

        """
        return self.extra_scopes or []

    async def create_scopes_for_app(
        self,
        app_name: str,
        additional_scopes: list[tuple[str, str, str]],
    ) -> None:
        """
        Create scopes for app.

        Args:
        ----
        app_name : str
            Name of the application.
        additional_scopes : list[tuple[str, str, str]]
            List of tuples containing additional scope values.

        Returns:
        -------
        None

        """
        for scope_enum in ScopeEnum:
            scope, created = await self.Meta.model.objects.get_or_create(
                app_name=app_name,
                value=scope_enum.value,
                description=f"{scope_enum.value} {app_name}s".capitalize(),
                protected=True,
            )
        for scope in additional_scopes:
            _app_name, value, description = scope
            (
                additional_scope,
                additional_scope_created,
            ) = await self.Meta.model.objects.get_or_create(
                app_name=_app_name,
                value=value,
                description=description,
            )

    async def create_default_scopes(
        self, applications, additional=None
    ) -> None:  # noqa: ANN001
        """
        Create default scopes for applications.

        Parameters
        ----------
        applications : list
            List of application names.
        additional : list, optional
            List of additional scope values, by default None.

        Returns
        -------
        None

        """
        for app in applications:
            # Code for creating default scopes goes here
            await self.create_scopes_for_app(app, additional)

    @staticmethod
    async def get_scopes_list(roles: list[Role]) -> list[str]:
        """
        Get list of scopes for the given roles.

        Args:
        ----
        roles : list[Role]
            List of roles.

        Returns:
        -------
        list
            List of scopes.

        """
        scopes = []
        for role in roles:
            if role.id == ProtectedDefaultRolesEnum.BANNED.value:
                return []
            for scope in role.scopes:
                scope_str = scope.get_string()
                if scope_str not in scopes:
                    scopes.append(scope.get_string())
        return scopes

    async def get_default_scopes_for_role(self, role_id: str) -> list[Scope]:
        """
        Get default scopes for the given role.

        Args:
        ----
        role_id : str
            ID of the role.

        Returns:
        -------
        list
            List of default scopes.

        """
        scopes = None
        if role_id == ProtectedDefaultRolesEnum.ADMIN.value:
            scopes = await self.Meta.model.objects.all()
        elif role_id in (
            ProtectedDefaultRolesEnum.USER.value,
            ProtectedDefaultRolesEnum.VIP.value,
        ):
            scopes = await self.Meta.model.objects.filter(
                or_(
                    and_(app_name="users", value="me"),
                    and_(app_name="users", value="me:username"),
                    and_(app_name="users", value="me:password"),
                    and_(app_name="users", value="me:display-role"),
                    and_(app_name="threads", value="create"),
                    and_(app_name="threads", value="update"),
                    and_(app_name="posts", value="create"),
                    and_(app_name="posts", value="update"),
                ),
            ).all()
        return scopes
Meta

Meta class for ScopeService.

Source code in sharkservers/scopes/services.py
51
52
53
54
55
class Meta:
    """Meta class for ScopeService."""

    model = Scope
    not_found_exception = scope_not_found_exception
add_default_scopes(apps: list[str]) -> ScopeService

Add default scope.

Args:

apps : list[str] List of application names.

Returns:

ScopeService The ScopeService instance.

Source code in sharkservers/scopes/services.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def add_default_scopes(self, apps: list[str]) -> ScopeService:
    """
    Add default scope.

    Args:
    ----
    apps : list[str]
        List of application names.

    Returns:
    -------
    ScopeService
        The ScopeService instance.

    """
    for app in apps:
        self.default_scopes.append(app)
    return self
add_extra_scope(app_name: str, scope_values: list[tuple[str, str, str]]) -> ScopeService

Add extra scope.

Parameters

app_name : str Name of the application. scope_values : list[tuple[str, str, str]] List of tuples containing scope values.

Returns

ScopeService The ScopeService instance.

Source code in sharkservers/scopes/services.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def add_extra_scope(
    self,
    app_name: str,
    scope_values: list[tuple[str, str, str]],
) -> ScopeService:
    """
    Add extra scope.

    Parameters
    ----------
    app_name : str
        Name of the application.
    scope_values : list[tuple[str, str, str]]
        List of tuples containing scope values.

    Returns
    -------
    ScopeService
        The ScopeService instance.

    """
    if not self.extra_scopes:
        self.extra_scopes = []
    for scope_value in scope_values:
        app_name = scope_value[0]
        value = scope_value[1]
        description = scope_value[2]
        self.extra_scopes.append(
            CreateScopeSchema(
                app_name=app_name,
                value=value,
                description=description,
            ),
        )
    return self
get_extra_scopes() -> list[CreateScopeSchema]

Get extra scopes.

Returns

list[CreateScopeSchema] List of extra scopes.

Source code in sharkservers/scopes/services.py
112
113
114
115
116
117
118
119
120
121
122
def get_extra_scopes(self) -> list[CreateScopeSchema]:
    """
    Get extra scopes.

    Returns
    -------
    list[CreateScopeSchema]
        List of extra scopes.

    """
    return self.extra_scopes or []
create_scopes_for_app(app_name: str, additional_scopes: list[tuple[str, str, str]]) -> None async

Create scopes for app.

Args:

app_name : str Name of the application. additional_scopes : list[tuple[str, str, str]] List of tuples containing additional scope values.

Returns:

None

Source code in sharkservers/scopes/services.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
async def create_scopes_for_app(
    self,
    app_name: str,
    additional_scopes: list[tuple[str, str, str]],
) -> None:
    """
    Create scopes for app.

    Args:
    ----
    app_name : str
        Name of the application.
    additional_scopes : list[tuple[str, str, str]]
        List of tuples containing additional scope values.

    Returns:
    -------
    None

    """
    for scope_enum in ScopeEnum:
        scope, created = await self.Meta.model.objects.get_or_create(
            app_name=app_name,
            value=scope_enum.value,
            description=f"{scope_enum.value} {app_name}s".capitalize(),
            protected=True,
        )
    for scope in additional_scopes:
        _app_name, value, description = scope
        (
            additional_scope,
            additional_scope_created,
        ) = await self.Meta.model.objects.get_or_create(
            app_name=_app_name,
            value=value,
            description=description,
        )
create_default_scopes(applications, additional=None) -> None async

Create default scopes for applications.

Parameters

applications : list List of application names. additional : list, optional List of additional scope values, by default None.

Returns

None

Source code in sharkservers/scopes/services.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
async def create_default_scopes(
    self, applications, additional=None
) -> None:  # noqa: ANN001
    """
    Create default scopes for applications.

    Parameters
    ----------
    applications : list
        List of application names.
    additional : list, optional
        List of additional scope values, by default None.

    Returns
    -------
    None

    """
    for app in applications:
        # Code for creating default scopes goes here
        await self.create_scopes_for_app(app, additional)
get_scopes_list(roles: list[Role]) -> list[str] async staticmethod

Get list of scopes for the given roles.

Args:

roles : list[Role] List of roles.

Returns:

list List of scopes.

Source code in sharkservers/scopes/services.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
@staticmethod
async def get_scopes_list(roles: list[Role]) -> list[str]:
    """
    Get list of scopes for the given roles.

    Args:
    ----
    roles : list[Role]
        List of roles.

    Returns:
    -------
    list
        List of scopes.

    """
    scopes = []
    for role in roles:
        if role.id == ProtectedDefaultRolesEnum.BANNED.value:
            return []
        for scope in role.scopes:
            scope_str = scope.get_string()
            if scope_str not in scopes:
                scopes.append(scope.get_string())
    return scopes
get_default_scopes_for_role(role_id: str) -> list[Scope] async

Get default scopes for the given role.

Args:

role_id : str ID of the role.

Returns:

list List of default scopes.

Source code in sharkservers/scopes/services.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
async def get_default_scopes_for_role(self, role_id: str) -> list[Scope]:
    """
    Get default scopes for the given role.

    Args:
    ----
    role_id : str
        ID of the role.

    Returns:
    -------
    list
        List of default scopes.

    """
    scopes = None
    if role_id == ProtectedDefaultRolesEnum.ADMIN.value:
        scopes = await self.Meta.model.objects.all()
    elif role_id in (
        ProtectedDefaultRolesEnum.USER.value,
        ProtectedDefaultRolesEnum.VIP.value,
    ):
        scopes = await self.Meta.model.objects.filter(
            or_(
                and_(app_name="users", value="me"),
                and_(app_name="users", value="me:username"),
                and_(app_name="users", value="me:password"),
                and_(app_name="users", value="me:display-role"),
                and_(app_name="threads", value="create"),
                and_(app_name="threads", value="update"),
                and_(app_name="posts", value="create"),
                and_(app_name="posts", value="update"),
            ),
        ).all()
    return scopes

views

Scopes views.

get_all_scopes(params: Params = Depends(), scopes_service: ScopeService = Depends(get_scopes_service), role_id: int | None = None) -> Page[ScopeOut] async

Retrieve all scopes.


params (Params, optional): The request parameters. Defaults to Depends().
scopes_service (ScopeService, optional): The scope service. Defaults to Depends(get_scopes_service).
role_id (int | None, optional): The role ID. Defaults to None.

Page[ScopeOut]: The list of scopes.
Source code in sharkservers/scopes/views.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@router.get("", response_model_exclude_none=True)
async def get_all_scopes(
    params: Params = Depends(),
    scopes_service: ScopeService = Depends(get_scopes_service),
    role_id: int | None = None,
) -> Page[ScopeOut]:
    """
    Retrieve all scopes.

    Args:
    ----
        params (Params, optional): The request parameters. Defaults to Depends().
        scopes_service (ScopeService, optional): The scope service. Defaults to Depends(get_scopes_service).
        role_id (int | None, optional): The role ID. Defaults to None.

    Returns:
    -------
        Page[ScopeOut]: The list of scopes.
    """
    scopes = []
    if role_id:
        scopes = await scopes_service.get_all(
            params=params,
            related=["roles"],
            roles__id=role_id,
        )
    else:
        scopes = await scopes_service.get_all(params=params)
    return scopes

views_admin

Scopes admin views.

admin_get_scopes(params: Params = Depends(), scopes_service: ScopeService = Depends(get_scopes_service)) -> Page[ScopeOut] async

Retrieve all scopes for admin users.


params (Params): The parameters for filtering and pagination.
scopes_service (ScopeService): The service for retrieving scopes.

Page[ScopeOut]: A paginated list of ScopeOut objects.
Source code in sharkservers/scopes/views_admin.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@router.get("", dependencies=[Security(get_admin_user, scopes="scopes:all")])
async def admin_get_scopes(
    params: Params = Depends(),
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> Page[ScopeOut]:
    """
    Retrieve all scopes for admin users.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        scopes_service (ScopeService): The service for retrieving scopes.

    Returns:
    -------
        Page[ScopeOut]: A paginated list of ScopeOut objects.
    """
    return await scopes_service.get_all(params=params)

admin_get_scope(scope: Scope = Depends(get_valid_scope)) -> ScopeOut async

Retrieve a scope for admin users.


scope (Scope): The scope to retrieve.

ScopeOut: The retrieved scope.
Source code in sharkservers/scopes/views_admin.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@router.get(
    "/{scope_id}",
    dependencies=[Security(get_admin_user, scopes="scopes:retrieve")],
)
async def admin_get_scope(
    scope: Scope = Depends(get_valid_scope),
) -> ScopeOut:
    """
    Retrieve a scope for admin users.

    Args:
    ----
        scope (Scope): The scope to retrieve.

    Returns:
    -------
        ScopeOut: The retrieved scope.
    """
    return scope

admin_create_scope(scope_data: CreateScopeSchema, scopes_service: ScopeService = Depends(get_scopes_service)) -> ScopeOut async

Create a new scope.


scope_data (CreateScopeSchema): The data for creating the scope.
scopes_service (ScopeService, optional): The service for managing scopes. Defaults to Depends(get_scopes_service).

ScopeOut: The created scope.
Source code in sharkservers/scopes/views_admin.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@router.post("", dependencies=[Security(get_admin_user, scopes="scopes:create")])
async def admin_create_scope(
    scope_data: CreateScopeSchema,
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> ScopeOut:
    """
    Create a new scope.

    Args:
    ----
        scope_data (CreateScopeSchema): The data for creating the scope.
        scopes_service (ScopeService, optional): The service for managing scopes. Defaults to Depends(get_scopes_service).

    Returns:
    -------
        ScopeOut: The created scope.
    """
    return await scopes_service.create(**scope_data.dict())

admin_delete_scope(scope: Scope = Depends(get_valid_scope), scopes_service: ScopeService = Depends(get_scopes_service)) -> ScopeOut async

Deletes a scope from the system.


scope (Scope): The scope to be deleted.
scopes_service (ScopeService): The service responsible for managing scopes.

ScopeOut: The deleted scope.
Source code in sharkservers/scopes/views_admin.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@router.delete(
    "/{scope_id}",
    dependencies=[Security(get_admin_user, scopes="scopes:delete")],
)
async def admin_delete_scope(
    scope: Scope = Depends(get_valid_scope),
    scopes_service: ScopeService = Depends(get_scopes_service),
) -> ScopeOut:
    """
    Deletes a scope from the system.

    Args:
    ----
        scope (Scope): The scope to be deleted.
        scopes_service (ScopeService): The service responsible for managing scopes.

    Returns:
    -------
        ScopeOut: The deleted scope.
    """  # noqa: D401
    return await scopes_service.delete(_id=scope.id)

admin_update_scope(update_scope_data: UpdateScopeSchema, scope: Scope = Depends(get_valid_scope)) -> ScopeOut async

Update the given scope with the provided data.


update_scope_data (UpdateScopeSchema): The data to update the scope with.
scope (Scope): The scope to be updated.

ScopeOut: The updated scope.
Source code in sharkservers/scopes/views_admin.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
@router.put(
    "/{scope_id}",
    dependencies=[Security(get_admin_user, scopes="scopes:update")],
)
async def admin_update_scope(
    update_scope_data: UpdateScopeSchema,
    scope: Scope = Depends(get_valid_scope),
) -> ScopeOut:
    """
    Update the given scope with the provided data.

    Args:
    ----
        update_scope_data (UpdateScopeSchema): The data to update the scope with.
        scope (Scope): The scope to be updated.

    Returns:
    -------
        ScopeOut: The updated scope.
    """
    await scope.update(**update_scope_data.dict(exclude_unset=True, exclude_none=True))
    return scope

servers

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Dependencies for the servers module.

get_servers_service() -> ServerService async

Retrieve the server service.

Returns
ServerService: The server service instance.
Source code in sharkservers/servers/dependencies.py
 8
 9
10
11
12
13
14
15
16
async def get_servers_service() -> ServerService:
    """
    Retrieve the server service.

    Returns
    -------
        ServerService: The server service instance.
    """
    return ServerService()

get_valid_server(server_id: int, servers_service: ServerService = Depends(get_servers_service)) -> Model async

Retrieve a valid server with the given server ID.


server_id (int): The ID of the server to retrieve.
servers_service (ServerService): The server service dependency.

Model: The retrieved server model.
Source code in sharkservers/servers/dependencies.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
async def get_valid_server(
    server_id: int,
    servers_service: ServerService = Depends(get_servers_service),
) -> Model:
    """
    Retrieve a valid server with the given server ID.

    Args:
    ----
        server_id (int): The ID of the server to retrieve.
        servers_service (ServerService): The server service dependency.

    Returns:
    -------
        Model: The retrieved server model.

    """
    return await servers_service.get_one(id=server_id, related=["admin_role"])

enums

Server enums.

ServerExceptionEnum

Bases: str, Enum

Enum class for server exceptions.

Source code in sharkservers/servers/enums.py
5
6
7
8
class ServerExceptionEnum(str, Enum):
    """Enum class for server exceptions."""

    NOT_FOUND = "Server not found"

ChatColorModuleExceptionEnum

Bases: str, Enum

Enum class for chat color module exceptions.

Source code in sharkservers/servers/enums.py
11
12
13
14
class ChatColorModuleExceptionEnum(str, Enum):
    """Enum class for chat color module exceptions."""

    NOT_FOUND = "Chat color not found"

exceptions

Exceptions for the servers module.

models

Servers models.

Server

Bases: Model, DateFieldsMixins

Represents a server in the system.

Attributes
id (int): The unique identifier of the server.
name (str, optional): The name of the server.
tag (str, optional): The tag of the server.
ip (str, optional): The IP address of the server.
port (int, optional): The port number of the server.
admin_role (Role, optional): The admin role associated with the server.
api_url (str, optional): The API URL of the server.
Methods
get_sourcemod_api_config(): Returns the APIConfig object for the server's Sourcemod API.
get_admins_groups(params: Params) -> Page_GroupOut_: Retrieves a page of admin groups for the server.
get_admin_group(group_id: int): Retrieves an admin group by its ID.
create_admin_group(data: CreateGroupSchema): Creates a new admin group for the server.
delete_admin_group(group_id: int): Deletes an admin group by its ID.
get_admins(params: Params): Retrieves a page of admins for the server.
get_admin(identity: str): Retrieves an admin by their identity.
create_admin(data: CreateAdminSchema): Creates a new admin for the server.
update_admin(identity: str, data: UpdateAdminSchema): Updates an admin by their identity.
delete_admin(identity: str): Deletes an admin by their identity.
Source code in sharkservers/servers/models.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
class Server(ormar.Model, DateFieldsMixins):
    """
    Represents a server in the system.

    Attributes
    ----------
        id (int): The unique identifier of the server.
        name (str, optional): The name of the server.
        tag (str, optional): The tag of the server.
        ip (str, optional): The IP address of the server.
        port (int, optional): The port number of the server.
        admin_role (Role, optional): The admin role associated with the server.
        api_url (str, optional): The API URL of the server.

    Methods
    -------
        get_sourcemod_api_config(): Returns the APIConfig object for the server's Sourcemod API.
        get_admins_groups(params: Params) -> Page_GroupOut_: Retrieves a page of admin groups for the server.
        get_admin_group(group_id: int): Retrieves an admin group by its ID.
        create_admin_group(data: CreateGroupSchema): Creates a new admin group for the server.
        delete_admin_group(group_id: int): Deletes an admin group by its ID.
        get_admins(params: Params): Retrieves a page of admins for the server.
        get_admin(identity: str): Retrieves an admin by their identity.
        create_admin(data: CreateAdminSchema): Creates a new admin for the server.
        update_admin(identity: str, data: UpdateAdminSchema): Updates an admin by their identity.
        delete_admin(identity: str): Deletes an admin by their identity.
    """

    class Meta(BaseMeta):
        """Metadata for the server model."""

        tablename = "servers"

    id: int = ormar.Integer(primary_key=True)
    name: str | None = ormar.String(max_length=64)
    tag: str | None = ormar.String(max_length=64, unique=True)
    ip: str | None = ormar.String(max_length=64)
    port: int | None = ormar.Integer()
    admin_role: Role | None = ormar.ForeignKey(Role)
    api_url: str | None = ormar.String(max_length=256)

    def get_sourcemod_api_config(self) -> APIConfig:
        """
        Return the APIConfig object for the server's Sourcemod API.

        Returns
        -------
            APIConfig: The APIConfig object for the server's Sourcemod API.
        """
        return APIConfig(
            base_path=self.api_url,
        )

    async def get_admins_groups(self, params: Params) -> Page_GroupOut_:
        """
        Get a page of admin groups for the server.

        Args:
        ----
            params (Params): The pagination parameters.

        Returns:
        -------
            Page_GroupOut_: The page of admin groups for the server.
        """
        return await admins_groups_get_groups(
            api_config_override=self.get_sourcemod_api_config(),
            page=params.page,
            size=params.size,
        )

    async def get_admin_group(self, group_id: int) -> GroupOut:
        """
        Get an admin group by its ID.

        Args:
        ----
            group_id (int): The ID of the admin group.

        Returns:
        -------
            GroupOut: The admin group.
        """
        return admins_groups_get_group(
            group_id=group_id,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def create_admin_group(self, data: CreateGroupSchema) -> GroupOut:
        """
        Create a new admin group for the server.

        Args:
        ----
            data (CreateGroupSchema): The data for the new admin group.

        Returns:
        -------
            GroupOut: The newly created admin group.
        """
        return await admins_groups_create_group(
            data=data,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def delete_admin_group(self, group_id: int) -> GroupOut:
        """
        Delete an admin group by its ID.

        Args:
        ----
            group_id (int): The ID of the admin group.

        Returns:
        -------
            GroupOut: The deleted admin group.
        """
        return await admins_groups_delete_group(
            group_id=group_id,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def get_admins(self, params: Params) -> Page_AdminOut_:
        """
        Get a page of admins for the server.

        Args:
        ----
            params (Params): The pagination parameters.

        Returns:
        -------
            Page_AdminOut_: The page of admins for the server.
        """
        return await adminss_get_admins(
            api_config_override=self.get_sourcemod_api_config(),
            page=params.page,
            size=params.size,
        )

    async def get_admin(self, identity: str) -> AdminOut:
        """
        Get an admin by their identity.

        Args:
        ----
            identity (str): The identity of the admin.
        """
        return await adminss_get_admin(
            identity=identity,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def create_admin(self, data: CreateAdminSchema) -> AdminOut:
        """
        Create a new admin for the server.

        Args:
        ----
            data (CreateAdminSchema): The data for the new admin.

        Returns:
        -------
            AdminOut: The newly created admin.
        """
        return await adminss_create_admin(
            data=data,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def update_admin(self, identity: str, data: UpdateAdminSchema) -> AdminOut:
        """
        Update an admin by their identity.

        Args:
        ----
            identity (str): The identity of the admin.
            data (UpdateAdminSchema): The data to update the admin with.

        Returns:
        -------
            AdminOut: The updated admin.
        """
        return await adminss_update_admin(
            identity=identity,
            data=data,
            api_config_override=self.get_sourcemod_api_config(),
        )

    async def delete_admin(self, identity: str) -> AdminOut:
        """
        Delete an admin by their identity.

        Args:
        ----
            identity (str): The identity of the admin.

        Returns:
        -------
            AdminOut: The deleted admin.
        """
        return await adminss_delete_admin(
            identity=identity,
            api_config_override=self.get_sourcemod_api_config(),
        )
Meta

Bases: BaseMeta

Metadata for the server model.

Source code in sharkservers/servers/models.py
67
68
69
70
class Meta(BaseMeta):
    """Metadata for the server model."""

    tablename = "servers"
get_sourcemod_api_config() -> APIConfig

Return the APIConfig object for the server's Sourcemod API.

Returns
APIConfig: The APIConfig object for the server's Sourcemod API.
Source code in sharkservers/servers/models.py
80
81
82
83
84
85
86
87
88
89
90
def get_sourcemod_api_config(self) -> APIConfig:
    """
    Return the APIConfig object for the server's Sourcemod API.

    Returns
    -------
        APIConfig: The APIConfig object for the server's Sourcemod API.
    """
    return APIConfig(
        base_path=self.api_url,
    )
get_admins_groups(params: Params) -> Page_GroupOut_ async

Get a page of admin groups for the server.


params (Params): The pagination parameters.

Page_GroupOut_: The page of admin groups for the server.
Source code in sharkservers/servers/models.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
async def get_admins_groups(self, params: Params) -> Page_GroupOut_:
    """
    Get a page of admin groups for the server.

    Args:
    ----
        params (Params): The pagination parameters.

    Returns:
    -------
        Page_GroupOut_: The page of admin groups for the server.
    """
    return await admins_groups_get_groups(
        api_config_override=self.get_sourcemod_api_config(),
        page=params.page,
        size=params.size,
    )
get_admin_group(group_id: int) -> GroupOut async

Get an admin group by its ID.


group_id (int): The ID of the admin group.

GroupOut: The admin group.
Source code in sharkservers/servers/models.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
async def get_admin_group(self, group_id: int) -> GroupOut:
    """
    Get an admin group by its ID.

    Args:
    ----
        group_id (int): The ID of the admin group.

    Returns:
    -------
        GroupOut: The admin group.
    """
    return admins_groups_get_group(
        group_id=group_id,
        api_config_override=self.get_sourcemod_api_config(),
    )
create_admin_group(data: CreateGroupSchema) -> GroupOut async

Create a new admin group for the server.


data (CreateGroupSchema): The data for the new admin group.

GroupOut: The newly created admin group.
Source code in sharkservers/servers/models.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
async def create_admin_group(self, data: CreateGroupSchema) -> GroupOut:
    """
    Create a new admin group for the server.

    Args:
    ----
        data (CreateGroupSchema): The data for the new admin group.

    Returns:
    -------
        GroupOut: The newly created admin group.
    """
    return await admins_groups_create_group(
        data=data,
        api_config_override=self.get_sourcemod_api_config(),
    )
delete_admin_group(group_id: int) -> GroupOut async

Delete an admin group by its ID.


group_id (int): The ID of the admin group.

GroupOut: The deleted admin group.
Source code in sharkservers/servers/models.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
async def delete_admin_group(self, group_id: int) -> GroupOut:
    """
    Delete an admin group by its ID.

    Args:
    ----
        group_id (int): The ID of the admin group.

    Returns:
    -------
        GroupOut: The deleted admin group.
    """
    return await admins_groups_delete_group(
        group_id=group_id,
        api_config_override=self.get_sourcemod_api_config(),
    )
get_admins(params: Params) -> Page_AdminOut_ async

Get a page of admins for the server.


params (Params): The pagination parameters.

Page_AdminOut_: The page of admins for the server.
Source code in sharkservers/servers/models.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
async def get_admins(self, params: Params) -> Page_AdminOut_:
    """
    Get a page of admins for the server.

    Args:
    ----
        params (Params): The pagination parameters.

    Returns:
    -------
        Page_AdminOut_: The page of admins for the server.
    """
    return await adminss_get_admins(
        api_config_override=self.get_sourcemod_api_config(),
        page=params.page,
        size=params.size,
    )
get_admin(identity: str) -> AdminOut async

Get an admin by their identity.


identity (str): The identity of the admin.
Source code in sharkservers/servers/models.py
179
180
181
182
183
184
185
186
187
188
189
190
async def get_admin(self, identity: str) -> AdminOut:
    """
    Get an admin by their identity.

    Args:
    ----
        identity (str): The identity of the admin.
    """
    return await adminss_get_admin(
        identity=identity,
        api_config_override=self.get_sourcemod_api_config(),
    )
create_admin(data: CreateAdminSchema) -> AdminOut async

Create a new admin for the server.


data (CreateAdminSchema): The data for the new admin.

AdminOut: The newly created admin.
Source code in sharkservers/servers/models.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
async def create_admin(self, data: CreateAdminSchema) -> AdminOut:
    """
    Create a new admin for the server.

    Args:
    ----
        data (CreateAdminSchema): The data for the new admin.

    Returns:
    -------
        AdminOut: The newly created admin.
    """
    return await adminss_create_admin(
        data=data,
        api_config_override=self.get_sourcemod_api_config(),
    )
update_admin(identity: str, data: UpdateAdminSchema) -> AdminOut async

Update an admin by their identity.


identity (str): The identity of the admin.
data (UpdateAdminSchema): The data to update the admin with.

AdminOut: The updated admin.
Source code in sharkservers/servers/models.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
async def update_admin(self, identity: str, data: UpdateAdminSchema) -> AdminOut:
    """
    Update an admin by their identity.

    Args:
    ----
        identity (str): The identity of the admin.
        data (UpdateAdminSchema): The data to update the admin with.

    Returns:
    -------
        AdminOut: The updated admin.
    """
    return await adminss_update_admin(
        identity=identity,
        data=data,
        api_config_override=self.get_sourcemod_api_config(),
    )
delete_admin(identity: str) -> AdminOut async

Delete an admin by their identity.


identity (str): The identity of the admin.

AdminOut: The deleted admin.
Source code in sharkservers/servers/models.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
async def delete_admin(self, identity: str) -> AdminOut:
    """
    Delete an admin by their identity.

    Args:
    ----
        identity (str): The identity of the admin.

    Returns:
    -------
        AdminOut: The deleted admin.
    """
    return await adminss_delete_admin(
        identity=identity,
        api_config_override=self.get_sourcemod_api_config(),
    )

create_admin_role(instance: Server) -> RoleOut async

Create an admin role for the server.


instance (Server): The server instance.

Role: The newly created admin role.
Source code in sharkservers/servers/models.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
async def create_admin_role(instance: Server) -> RoleOut:
    """
    Create an admin role for the server.

    Args:
    ----
        instance (Server): The server instance.

    Returns:
    -------
        Role: The newly created admin role.
    """
    roles_service = await get_roles_service()
    scopes_service = await get_scopes_service()
    role_name = f"Admin {instance.name.capitalize()}"
    logger.info(role_name)
    scopes = await scopes_service.get_default_scopes_for_role(
        ProtectedDefaultRolesEnum.USER.value,
    )
    logger.info(scopes)

    new_admin_role = await roles_service.create(
        tag=role_name.replace(" ", "_").lower(),
        name=role_name,
        color="#fea501",
        is_staff=True,
    )
    for scope in scopes:
        await new_admin_role.scopes.add(scope)
    return new_admin_role

on_server_created(sender: Server, instance: Server, **kwargs) -> None async

On server created event.


sender (Server): The sender of the event.
instance (Server): The server instance.
**kwargs: The keyword arguments.

None
Source code in sharkservers/servers/models.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
@ormar.post_save(Server)
async def on_server_created(
    sender: Server, instance: Server, **kwargs
) -> None:  # noqa: ANN003, ARG001
    """
    On server created event.

    Args:
    ----
        sender (Server): The sender of the event.
        instance (Server): The server instance.
        **kwargs: The keyword arguments.

    Returns:
    -------
        None
    """
    new_admin_role = await create_admin_role(instance)
    await instance.update(admin_role=new_admin_role)
    await instance.create_admin_group(
        data=CreateGroupSchema(
            name="Admin",
            immunity_level=100,
            flags="z",
        ),
    )

on_server_deleted(sender, instance: Server, **kwargs) -> None async

On server deleted event.


sender (Server): The sender of the event.
instance (Server): The server instance.
**kwargs: The keyword arguments.
Source code in sharkservers/servers/models.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
@ormar.post_delete(Server)
async def on_server_deleted(
    sender, instance: Server, **kwargs
) -> None:  # noqa: ANN001, ARG001, ANN003
    """
    On server deleted event.

    Args:
    ----
        sender (Server): The sender of the event.
        instance (Server): The server instance.
        **kwargs: The keyword arguments.
    """
    await instance.admin_role.delete()
    admins_groups = await instance.get_admins_groups(params=Params())
    admins = await instance.get_admins(params=Params())
    for admin_group in admins_groups.items:
        await instance.delete_admin_group(group_id=admin_group.id)

    for admin in admins.items:
        await instance.delete_admin(identity=admin.identity)

schemas

Schemas for the servers module.

ServerOut

Bases: server_out

Schema for retrieving a server.

Source code in sharkservers/servers/schemas.py
11
12
class ServerOut(server_out):
    """Schema for retrieving a server."""

CreateServerSchema

Bases: BaseModel

Schema for creating a server.

Source code in sharkservers/servers/schemas.py
15
16
17
18
19
20
21
22
class CreateServerSchema(BaseModel):
    """Schema for creating a server."""

    tag: str
    name: str
    ip: str
    port: int
    api_url: str

UpdateServerSchema

Bases: BaseModel

Schema for updating server information.

Source code in sharkservers/servers/schemas.py
25
26
27
28
29
30
31
32
class UpdateServerSchema(BaseModel):
    """Schema for updating server information."""

    tag: str | None
    name: str | None
    ip: str | None
    port: int | None
    api_url: str | None

ServerStatusSchema

Bases: BaseModel

Schema for retrieving a server's status.

Source code in sharkservers/servers/schemas.py
35
36
37
38
39
40
41
42
43
44
45
class ServerStatusSchema(BaseModel):
    """Schema for retrieving a server's status."""

    id: int
    name: str
    ip: str
    port: int
    players: int
    max_players: int
    map: str
    game: str

services

Services for servers app.

ServerService

Bases: BaseService

Service class for managing server operations.

Methods
get_status(): Retrieves the status of all servers.
Source code in sharkservers/servers/services.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class ServerService(BaseService):
    """
    Service class for managing server operations.

    Methods
    -------
        get_status(): Retrieves the status of all servers.

    """

    class Meta:
        """Metadata for the service."""

        model = Server
        not_found_exception = server_not_found_exception

    async def get_status(self) -> list[ServerStatusSchema]:
        """
        Retrieve the status of all servers.

        Returns
        -------
            list: A list of server status objects.
        """
        servers_with_status: list[ServerStatusSchema] = []
        servers_from_db = await self.Meta.model.objects.all()
        try:
            for server in servers_from_db:
                server_status = gs.a2s_info((server.ip, server.port))
                name = server_status.get("name", "invalid name")
                players = server_status.get("players", 0)
                max_players = server_status.get("max_players", 0)
                map = server_status.get("map", "invalid_map")  # noqa: A001
                server_status_schema = ServerStatusSchema(
                    id=server.id,
                    name=name,
                    ip=server.ip,
                    port=server.port,
                    players=players,
                    max_players=max_players,
                    map=map,
                    game="tf2",
                )
                servers_with_status.append(server_status_schema)
        except ConnectionRefusedError:
            servers_with_status.append(
                ServerStatusSchema(
                    id=server.id,
                    name=server.name,
                    ip=server.ip,
                    port=server.port,
                    players=0,
                    max_players=0,
                    map="invalid_map",
                    game="tf2",
                ),
            )

        return servers_with_status
Meta

Metadata for the service.

Source code in sharkservers/servers/services.py
24
25
26
27
28
class Meta:
    """Metadata for the service."""

    model = Server
    not_found_exception = server_not_found_exception
get_status() -> list[ServerStatusSchema] async

Retrieve the status of all servers.

Returns
list: A list of server status objects.
Source code in sharkservers/servers/services.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
async def get_status(self) -> list[ServerStatusSchema]:
    """
    Retrieve the status of all servers.

    Returns
    -------
        list: A list of server status objects.
    """
    servers_with_status: list[ServerStatusSchema] = []
    servers_from_db = await self.Meta.model.objects.all()
    try:
        for server in servers_from_db:
            server_status = gs.a2s_info((server.ip, server.port))
            name = server_status.get("name", "invalid name")
            players = server_status.get("players", 0)
            max_players = server_status.get("max_players", 0)
            map = server_status.get("map", "invalid_map")  # noqa: A001
            server_status_schema = ServerStatusSchema(
                id=server.id,
                name=name,
                ip=server.ip,
                port=server.port,
                players=players,
                max_players=max_players,
                map=map,
                game="tf2",
            )
            servers_with_status.append(server_status_schema)
    except ConnectionRefusedError:
        servers_with_status.append(
            ServerStatusSchema(
                id=server.id,
                name=server.name,
                ip=server.ip,
                port=server.port,
                players=0,
                max_players=0,
                map="invalid_map",
                game="tf2",
            ),
        )

    return servers_with_status

views

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

admin

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

admins

Admin views for servers.

admin_get_server_admins(params: Params = Depends(), server: Server = Depends(get_valid_server)) -> Page_AdminOut_ async

Retrieve the list of admins for a server.


params (Params): The query parameters for filtering and pagination.
server (Server): The server for which to retrieve the admins.

Page_AdminOut_: The paginated list of admins for the server.
Source code in sharkservers/servers/views/admin/admins.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@router.get("/{server_id}/admins")
async def admin_get_server_admins(
    params: Params = Depends(),
    server: Server = Depends(get_valid_server),
) -> Page_AdminOut_:
    """
    Retrieve the list of admins for a server.

    Args:
    ----
        params (Params): The query parameters for filtering and pagination.
        server (Server): The server for which to retrieve the admins.

    Returns:
    -------
        Page_AdminOut_: The paginated list of admins for the server.
    """
    return await server.get_admins(params=params)
admin_create_server_admin(data: CreateAdminSchema, server: Server = Depends(get_valid_server)) -> AdminOut async

Create a server admin.


data (CreateAdminSchema): The data for creating the admin.
server (Server): The server instance.

AdminOut: The created admin.
Source code in sharkservers/servers/views/admin/admins.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@router.post("/{server_id}/admins")
async def admin_create_server_admin(
    data: CreateAdminSchema,
    server: Server = Depends(get_valid_server),
) -> AdminOut:
    """
    Create a server admin.

    Args:
    ----
        data (CreateAdminSchema): The data for creating the admin.
        server (Server): The server instance.

    Returns:
    -------
        AdminOut: The created admin.
    """
    return await server.create_admin(data=data)
admin_get_server_admin(identity: str, server: Server = Depends(get_valid_server)) -> AdminOut async

Retrieve the admin information for a specific server.


identity (str): The identity of the admin.
server (Server): The server object.

AdminOut: The admin information.
Source code in sharkservers/servers/views/admin/admins.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@router.get("/{server_id}/admins/{identity}")
async def admin_get_server_admin(
    identity: str,
    server: Server = Depends(get_valid_server),
) -> AdminOut:
    """
    Retrieve the admin information for a specific server.

    Args:
    ----
        identity (str): The identity of the admin.
        server (Server): The server object.

    Returns:
    -------
        AdminOut: The admin information.

    """
    return await server.get_admin(identity=identity)
admin_update_server_admin(identity: str, data: UpdateAdminSchema, server: Server = Depends(get_valid_server)) -> AdminOut async

Update the server admin with the given identity using the provided data.


identity (str): The identity of the server admin to update.
data (UpdateAdminSchema): The updated admin data.
server (Server): The server instance.

AdminOut: The updated server admin.
Source code in sharkservers/servers/views/admin/admins.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@router.put("/{server_id}/admins/{identity}")
async def admin_update_server_admin(
    identity: str,
    data: UpdateAdminSchema,
    server: Server = Depends(get_valid_server),
) -> AdminOut:
    """
    Update the server admin with the given identity using the provided data.

    Args:
    ----
        identity (str): The identity of the server admin to update.
        data (UpdateAdminSchema): The updated admin data.
        server (Server): The server instance.

    Returns:
    -------
        AdminOut: The updated server admin.

    """
    return await server.update_admin(identity=identity, data=data)
admin_delete_server_admin(identity: str, server: Server = Depends(get_valid_server)) -> AdminOut async

Delete a server admin with the given identity from the server.


identity (str): The identity of the admin to be deleted.
server (Server): The server from which the admin should be deleted.

AdminOut: The deleted admin.
Source code in sharkservers/servers/views/admin/admins.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@router.delete("/{server_id}/admins/{identity}")
async def admin_delete_server_admin(
    identity: str,
    server: Server = Depends(get_valid_server),
) -> AdminOut:
    """
    Delete a server admin with the given identity from the server.

    Args:
    ----
        identity (str): The identity of the admin to be deleted.
        server (Server): The server from which the admin should be deleted.

    Returns:
    -------
        AdminOut: The deleted admin.

    """
    return await server.delete_admin(identity=identity)
admins_groups

Admins groups views.

admin_get_server_admins_groups(params: Params = Depends(), server: Server = Depends(get_valid_server)) -> Page_GroupOut_ async

Retrieve the admins groups for a server.


params (Params): The parameters for the request.
server (Server): The server to retrieve the admins groups for.

Page_GroupOut_: The paginated list of admins groups.
Source code in sharkservers/servers/views/admin/admins_groups.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@router.get("/{server_id}/admins/groups")
async def admin_get_server_admins_groups(
    params: Params = Depends(),
    server: Server = Depends(get_valid_server),
) -> Page_GroupOut_:
    """
    Retrieve the admins groups for a server.

    Args:
    ----
        params (Params): The parameters for the request.
        server (Server): The server to retrieve the admins groups for.

    Returns:
    -------
        Page_GroupOut_: The paginated list of admins groups.
    """
    return await server.get_admins_groups(params=params)
admin_create_server_admins_groups(data: CreateGroupSchema, server: Server = Depends(get_valid_server)) -> GroupOut async

Create a new admin group for a server.


data (CreateGroupSchema): The data for creating the group.
server (Server): The server for which the group is being created.

GroupOut: The created admin group.
Source code in sharkservers/servers/views/admin/admins_groups.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@router.post("/{server_id}/admins/groups")
async def admin_create_server_admins_groups(
    data: CreateGroupSchema,
    server: Server = Depends(get_valid_server),
) -> GroupOut:
    """
    Create a new admin group for a server.

    Args:
    ----
        data (CreateGroupSchema): The data for creating the group.
        server (Server): The server for which the group is being created.

    Returns:
    -------
        GroupOut: The created admin group.
    """
    return await server.create_admin_group(data=data)
admin_get_server_admins_group(group_id: int, server: Server = Depends(get_valid_server)) -> GroupOut async

Retrieve the admins group of a server by its group ID.


group_id (int): The ID of the admins group.
server (Server): The server object.

GroupOut: The admins group information.
Source code in sharkservers/servers/views/admin/admins_groups.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@router.get("/{server_id}/admins/groups/{group_id}")
async def admin_get_server_admins_group(
    group_id: int,
    server: Server = Depends(get_valid_server),
) -> GroupOut:
    """
    Retrieve the admins group of a server by its group ID.

    Args:
    ----
        group_id (int): The ID of the admins group.
        server (Server): The server object.

    Returns:
    -------
        GroupOut: The admins group information.

    """
    return await server.get_admins_group(group_id=group_id)
admin_delete_server_admins_group(group_id: int, server: Server = Depends(get_valid_server)) -> GroupOut async

Delete an admin group from the server.


group_id (int): The ID of the admin group to delete.
server (Server): The server object.

GroupOut: The deleted admin group.
Source code in sharkservers/servers/views/admin/admins_groups.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@router.delete("/{server_id}/admins/groups/{group_id}")
async def admin_delete_server_admins_group(
    group_id: int,
    server: Server = Depends(get_valid_server),
) -> GroupOut:
    """
    Delete an admin group from the server.

    Args:
    ----
        group_id (int): The ID of the admin group to delete.
        server (Server): The server object.

    Returns:
    -------
        GroupOut: The deleted admin group.
    """
    return await server.delete_admin_group(group_id=group_id)
servers

Admin server views.

admin_get_servers(servers_service: ServerService = Depends(get_servers_service)) -> ServerOut async

Retrieve all servers with their associated admin role.


servers_service (ServerService): The server service instance.

ServerOut: The server data with the associated admin role.
Source code in sharkservers/servers/views/admin/servers.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@router.get("")
async def admin_get_servers(
    servers_service: ServerService = Depends(get_servers_service),
) -> ServerOut:
    """
    Retrieve all servers with their associated admin role.

    Args:
    ----
        servers_service (ServerService): The server service instance.

    Returns:
    -------
        ServerOut: The server data with the associated admin role.
    """
    return await servers_service.get_all(related=["admin_role"])
admin_create_server(server_data: CreateServerSchema, servers_service: ServerService = Depends(get_servers_service)) -> ServerOut async

Create a new server using the provided server data.


server_data (CreateServerSchema): The data for creating the server.
servers_service (ServerService): The server service instance.

ServerOut: The created server.
Source code in sharkservers/servers/views/admin/servers.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@router.post("")
async def admin_create_server(
    server_data: CreateServerSchema,
    servers_service: ServerService = Depends(get_servers_service),
) -> ServerOut:
    """
    Create a new server using the provided server data.

    Args:
    ----
        server_data (CreateServerSchema): The data for creating the server.
        servers_service (ServerService): The server service instance.

    Returns:
    -------
        ServerOut: The created server.

    """
    return await servers_service.create(**server_data.dict())
admin_get_server(server: Server = Depends(get_valid_server)) -> ServerOut async

Retrieve the details of a server for admin purposes.


server (Server): The server object to retrieve details for.

ServerOut: The server details.
Source code in sharkservers/servers/views/admin/servers.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@router.get("/{server_id}")
async def admin_get_server(
    server: Server = Depends(get_valid_server),
) -> ServerOut:
    """
    Retrieve the details of a server for admin purposes.

    Args:
    ----
        server (Server): The server object to retrieve details for.

    Returns:
    -------
        ServerOut: The server details.

    """
    return server
admin_update_server(server_data: UpdateServerSchema, server: Server = Depends(get_valid_server)) -> ServerOut async

Update the server with the provided data.


server_data (UpdateServerSchema): The data to update the server with.
server (Server): The server to be updated.

ServerOut: The updated server.
Source code in sharkservers/servers/views/admin/servers.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
@router.put("/{server_id}")
async def admin_update_server(
    server_data: UpdateServerSchema,
    server: Server = Depends(get_valid_server),
) -> ServerOut:
    """
    Update the server with the provided data.

    Args:
    ----
        server_data (UpdateServerSchema): The data to update the server with.
        server (Server): The server to be updated.

    Returns:
    -------
        ServerOut: The updated server.
    """
    return await server.update(**server_data.dict())
admin_delete_server(server: Server = Depends(get_valid_server), servers_service: ServerService = Depends(get_servers_service)) -> bool async

Delete the specified server.


server (Server): The server to be deleted.
servers_service (ServerService): The server service instance.

bool: True if the server is successfully deleted, False otherwise.
Source code in sharkservers/servers/views/admin/servers.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@router.delete("/{server_id}")
async def admin_delete_server(
    server: Server = Depends(get_valid_server),
    servers_service: ServerService = Depends(get_servers_service),
) -> bool:
    """
    Delete the specified server.

    Args:
    ----
        server (Server): The server to be deleted.
        servers_service (ServerService): The server service instance.

    Returns:
    -------
        bool: True if the server is successfully deleted, False otherwise.
    """
    return await servers_service.delete(server.id)

servers

Server views.

get_servers(params: Params = Depends(), ip: str | None = None, port: int | None = None, servers_service: ServerService = Depends(get_servers_service)) -> Page[ServerOut] async

Retrieve servers based on the provided parameters.


params (Params, optional): The query parameters for filtering and pagination. Defaults to Depends().
ip (str, optional): The IP address of the server to retrieve. Defaults to None.
port (int, optional): The port of the server to retrieve. Defaults to None.
servers_service (ServerService, optional): The server service dependency. Defaults to Depends(get_servers_service).

Page[ServerOut]: A paginated list of server objects.
Source code in sharkservers/servers/views/servers.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@router.get("/")
async def get_servers(
    params: Params = Depends(),
    ip: str | None = None,
    port: int | None = None,
    servers_service: ServerService = Depends(get_servers_service),
) -> Page[ServerOut]:
    """
    Retrieve servers based on the provided parameters.

    Args:
    ----
        params (Params, optional): The query parameters for filtering and pagination. Defaults to Depends().
        ip (str, optional): The IP address of the server to retrieve. Defaults to None.
        port (int, optional): The port of the server to retrieve. Defaults to None.
        servers_service (ServerService, optional): The server service dependency. Defaults to Depends(get_servers_service).

    Returns:
    -------
        Page[ServerOut]: A paginated list of server objects.

    """
    if ip and port:
        return await servers_service.get_one(ip=ip, port=port)
    return await servers_service.get_all(params=params, related=["admin_role"])
get_servers_status(servers_service: ServerService = Depends(get_servers_service)) -> list[ServerStatusSchema] async

Retrieve the status of servers.


servers_service (ServerService): The server service instance used to retrieve server status.

list[ServerStatusSchema]: A list of server status objects.

ConnectionRefusedError: If there is an error retrieving the server status.
Source code in sharkservers/servers/views/servers.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@router.get("/status")
async def get_servers_status(
    servers_service: ServerService = Depends(get_servers_service),
) -> list[ServerStatusSchema]:
    """
    Retrieve the status of servers.

    Args:
    ----
        servers_service (ServerService): The server service instance used to retrieve server status.

    Returns:
    -------
        list[ServerStatusSchema]: A list of server status objects.

    Raises:
    ------
        ConnectionRefusedError: If there is an error retrieving the server status.
    """
    return await servers_service.get_status()
get_server(server: Model = Depends(get_valid_server)) -> ServerOut async

Retrieve a server based on the provided server model.


server (Model): The server model to retrieve.

ServerOut: The retrieved server.
Source code in sharkservers/servers/views/servers.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@router.get("/{server_id}")
async def get_server(server: Model = Depends(get_valid_server)) -> ServerOut:
    """
    Retrieve a server based on the provided server model.

    Args:
    ----
        server (Model): The server model to retrieve.

    Returns:
    -------
        ServerOut: The retrieved server.

    """
    return server

services

Services module.

EmailService

Email service class.

Source code in sharkservers/services.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class EmailService:
    """Email service class."""

    def __init__(self, resend, checker: DefaultChecker) -> None:
        """Initialize EmailService class."""
        self.resend = resend
        self.checker = checker
        self.params = {
            "from": "No-Reply <no-reply@sharkservers.pl>",
        }

    async def send_confirmation_email(self, activation_type: ActivationEmailTypeEnum, email: EmailStr, code: str) -> None:
        """
        Send a confirmation email.

        Args:
        ----
            activation_type (ActivationEmailTypeEnum): The type of the activation email.
            email (EmailStr): The email address of the recipient.
            code (str): The activation code.

        Returns:
        -------
            None
        """

        if not await self.checker.check_mx_record(email.split("@")[1]):
            logger.error(f"Email {email} mx record is invalid")
            return

        try:
            subject = None
            body = None
            if activation_type == ActivationEmailTypeEnum.ACCOUNT:
                subject = "Witamy na naszej stronie!"
            elif activation_type == ActivationEmailTypeEnum.EMAIL:
                subject = "Zmiana adresu e-mail"
            elif activation_type == ActivationEmailTypeEnum.PASSWORD:
                subject = "Reset hasła"

            html = "Twój kod to: " + code + "<br><br> Pozdrawiamy, <br> Administracja SharkServers.pl"
            params = {
                "from": self.params["from"],
                "to": email,
                "subject": subject,
                "html": html,
            }
            self.resend.Emails.send(params)
            logger.info(f"Activation email sent to {email} with subject <{subject}> data <{html}'>")
        except Exception as e:
            logger.error(f"E-mail sending error: {e}")

__init__(resend, checker: DefaultChecker) -> None

Initialize EmailService class.

Source code in sharkservers/services.py
29
30
31
32
33
34
35
def __init__(self, resend, checker: DefaultChecker) -> None:
    """Initialize EmailService class."""
    self.resend = resend
    self.checker = checker
    self.params = {
        "from": "No-Reply <no-reply@sharkservers.pl>",
    }

send_confirmation_email(activation_type: ActivationEmailTypeEnum, email: EmailStr, code: str) -> None async

Send a confirmation email.


activation_type (ActivationEmailTypeEnum): The type of the activation email.
email (EmailStr): The email address of the recipient.
code (str): The activation code.

None
Source code in sharkservers/services.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
async def send_confirmation_email(self, activation_type: ActivationEmailTypeEnum, email: EmailStr, code: str) -> None:
    """
    Send a confirmation email.

    Args:
    ----
        activation_type (ActivationEmailTypeEnum): The type of the activation email.
        email (EmailStr): The email address of the recipient.
        code (str): The activation code.

    Returns:
    -------
        None
    """

    if not await self.checker.check_mx_record(email.split("@")[1]):
        logger.error(f"Email {email} mx record is invalid")
        return

    try:
        subject = None
        body = None
        if activation_type == ActivationEmailTypeEnum.ACCOUNT:
            subject = "Witamy na naszej stronie!"
        elif activation_type == ActivationEmailTypeEnum.EMAIL:
            subject = "Zmiana adresu e-mail"
        elif activation_type == ActivationEmailTypeEnum.PASSWORD:
            subject = "Reset hasła"

        html = "Twój kod to: " + code + "<br><br> Pozdrawiamy, <br> Administracja SharkServers.pl"
        params = {
            "from": self.params["from"],
            "to": email,
            "subject": subject,
            "html": html,
        }
        self.resend.Emails.send(params)
        logger.info(f"Activation email sent to {email} with subject <{subject}> data <{html}'>")
    except Exception as e:
        logger.error(f"E-mail sending error: {e}")

settings

Settings module.

Settings

Bases: BaseSettings

The Settings class represents the application's configuration settings.

Attributes

POSTGRES_HOST (str): The hostname of the PostgreSQL database server. POSTGRES_DB (str): The name of the PostgreSQL database. POSTGRES_USER (str): The username for connecting to the PostgreSQL database. POSTGRES_PASSWORD (str): The password for connecting to the PostgreSQL database. TESTING (bool): Flag indicating whether the application is running in testing mode. Default is False. SECRET_KEY (str): The secret key used for cryptographic operations. REFRESH_SECRET_KEY (str): The secret key used for refreshing access tokens. ALGORITHM (str): The algorithm used for token generation. Default is "HS256". ACCESS_TOKEN_EXPIRES (int): The expiration time (in minutes) for access tokens. Default is 5 minutes. REFRESH_TOKEN_EXPIRES (int): The expiration time (in minutes) for refresh tokens. Default is 43829 minutes. REDIS_HOST (str): The hostname of the Redis server. Default is "redis". REDIS_PORT (int): The port number of the Redis server. Default is 6379. STEAM_API_KEY (str): The API key for accessing the Steam API. DEBUG (bool): Flag indicating whether the application is running in debug mode. Default is False. MAIL_USERNAME (str): The username for the email server. MAIL_PASSWORD (str): The password for the email server. MAIL_FROM (str): The email address used as the "From" field in outgoing emails. MAIL_PORT (int): The port number of the email server. MAIL_SERVER (str): The hostname of the email server. MAIL_FROM_NAME (str): The name used as the "From" field in outgoing emails. MAIL_STARTTLS (bool): Flag indicating whether to use STARTTLS for secure email communication. Default is True. MAIL_SSL_TLS (bool): Flag indicating whether to use SSL/TLS for secure email communication. Default is False. USE_CREDENTIALS (bool): Flag indicating whether to use credentials for email communication. Default is True. VALIDATE_CERTS (bool): Flag indicating whether to validate SSL certificates for email communication. Default is True. IMAGE_ALLOWED_EXTENSIONS (list): List of allowed image file extensions. Default is [".png", ".jpg", ".jpeg"]. AVATAR_WIDTH (int): The width of user avatars. Default is 100. STRIPE_API_KEY (str): The API key for accessing the Stripe API. STRIPE_WEBHOOK_SECRET (str): The secret key for verifying Stripe webhook events. SITE_URL (str): The base URL of the site. Default is "http://localhost:8080".

Methods

get_database_url(): Returns the database URL based on the configuration settings. get_database_url_sync(): Returns the synchronous database URL based on the configuration settings. get_redis_url(): Returns the Redis URL based on the configuration settings.

Source code in sharkservers/settings.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class Settings(BaseSettings):
    """
    The Settings class represents the application's configuration settings.

    Attributes
    ----------
    POSTGRES_HOST (str): The hostname of the PostgreSQL database server.
    POSTGRES_DB (str): The name of the PostgreSQL database.
    POSTGRES_USER (str): The username for connecting to the PostgreSQL database.
    POSTGRES_PASSWORD (str): The password for connecting to the PostgreSQL database.
    TESTING (bool): Flag indicating whether the application is running in testing mode. Default is False.
    SECRET_KEY (str): The secret key used for cryptographic operations.
    REFRESH_SECRET_KEY (str): The secret key used for refreshing access tokens.
    ALGORITHM (str): The algorithm used for token generation. Default is "HS256".
    ACCESS_TOKEN_EXPIRES (int): The expiration time (in minutes) for access tokens. Default is 5 minutes.
    REFRESH_TOKEN_EXPIRES (int): The expiration time (in minutes) for refresh tokens. Default is 43829 minutes.
    REDIS_HOST (str): The hostname of the Redis server. Default is "redis".
    REDIS_PORT (int): The port number of the Redis server. Default is 6379.
    STEAM_API_KEY (str): The API key for accessing the Steam API.
    DEBUG (bool): Flag indicating whether the application is running in debug mode. Default is False.
    MAIL_USERNAME (str): The username for the email server.
    MAIL_PASSWORD (str): The password for the email server.
    MAIL_FROM (str): The email address used as the "From" field in outgoing emails.
    MAIL_PORT (int): The port number of the email server.
    MAIL_SERVER (str): The hostname of the email server.
    MAIL_FROM_NAME (str): The name used as the "From" field in outgoing emails.
    MAIL_STARTTLS (bool): Flag indicating whether to use STARTTLS for secure email communication. Default is True.
    MAIL_SSL_TLS (bool): Flag indicating whether to use SSL/TLS for secure email communication. Default is False.
    USE_CREDENTIALS (bool): Flag indicating whether to use credentials for email communication. Default is True.
    VALIDATE_CERTS (bool): Flag indicating whether to validate SSL certificates for email communication. Default is True.
    IMAGE_ALLOWED_EXTENSIONS (list): List of allowed image file extensions. Default is [".png", ".jpg", ".jpeg"].
    AVATAR_WIDTH (int): The width of user avatars. Default is 100.
    STRIPE_API_KEY (str): The API key for accessing the Stripe API.
    STRIPE_WEBHOOK_SECRET (str): The secret key for verifying Stripe webhook events.
    SITE_URL (str): The base URL of the site. Default is "http://localhost:8080".

    Methods
    -------
    get_database_url(): Returns the database URL based on the configuration settings.
    get_database_url_sync(): Returns the synchronous database URL based on the configuration settings.
    get_redis_url(): Returns the Redis URL based on the configuration settings.
    """

    POSTGRES_HOST: str = ""
    POSTGRES_DB: str = ""
    POSTGRES_USER: str = ""
    POSTGRES_PASSWORD: str = ""
    TESTING: bool = False
    SECRET_KEY: str = ""
    REFRESH_SECRET_KEY: str = ""
    ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRES: int = 5
    REFRESH_TOKEN_EXPIRES: int = 43829
    REDIS_HOST: str = "redis"
    REDIS_PORT: int = 6379
    STEAM_API_KEY: str = ""
    DEBUG: bool = False
    MAIL_USERNAME: str = ""
    MAIL_PASSWORD: str = ""
    MAIL_FROM: EmailStr = ""
    MAIL_PORT: int = 587
    MAIL_SERVER: str = ""
    MAIL_FROM_NAME: str = ""
    MAIL_STARTTLS: bool = True
    MAIL_SSL_TLS: bool = False
    USE_CREDENTIALS: bool = True
    VALIDATE_CERTS: bool = True
    IMAGE_ALLOWED_EXTENSIONS: list[str] = [".png", ".jpg", ".jpeg"]
    AVATAR_WIDTH: int = 100
    STRIPE_API_KEY: str = ""
    STRIPE_WEBHOOK_SECRET: str = ""
    SITE_URL: str = "http://localhost:8080"
    RESEND_API_KEY: str = ""

    class Config:
        """The Config class represents the configuration settings for the Settings class."""

        env_file = ".env"

    def get_database_url(self) -> str:
        """
        Return the database URL based on the configuration settings.

        Returns
        -------
        str: The database URL.
        """
        if self.TESTING:
            return "sqlite:///../test.db"
        return f"postgresql+asyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}/{self.POSTGRES_DB}"

    def get_database_url_sync(self) -> str:
        """
        Return the synchronous database URL based on the configuration settings.

        Returns
        -------
        str: The synchronous database URL.
        """
        if self.TESTING:
            return "sqlite:///test.db"
        return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}/{self.POSTGRES_DB}"

    def get_redis_url(self) -> str:
        """
        Return the Redis URL based on the configuration settings.

        Returns
        -------
        str: The Redis URL.
        """
        if self.TESTING:
            return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/1"
        return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/0"

Config

The Config class represents the configuration settings for the Settings class.

Source code in sharkservers/settings.py
83
84
85
86
class Config:
    """The Config class represents the configuration settings for the Settings class."""

    env_file = ".env"

get_database_url() -> str

Return the database URL based on the configuration settings.

Returns

str: The database URL.

Source code in sharkservers/settings.py
88
89
90
91
92
93
94
95
96
97
98
def get_database_url(self) -> str:
    """
    Return the database URL based on the configuration settings.

    Returns
    -------
    str: The database URL.
    """
    if self.TESTING:
        return "sqlite:///../test.db"
    return f"postgresql+asyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}/{self.POSTGRES_DB}"

get_database_url_sync() -> str

Return the synchronous database URL based on the configuration settings.

Returns

str: The synchronous database URL.

Source code in sharkservers/settings.py
100
101
102
103
104
105
106
107
108
109
110
def get_database_url_sync(self) -> str:
    """
    Return the synchronous database URL based on the configuration settings.

    Returns
    -------
    str: The synchronous database URL.
    """
    if self.TESTING:
        return "sqlite:///test.db"
    return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}/{self.POSTGRES_DB}"

get_redis_url() -> str

Return the Redis URL based on the configuration settings.

Returns

str: The Redis URL.

Source code in sharkservers/settings.py
112
113
114
115
116
117
118
119
120
121
122
def get_redis_url(self) -> str:
    """
    Return the Redis URL based on the configuration settings.

    Returns
    -------
    str: The Redis URL.
    """
    if self.TESTING:
        return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/1"
    return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/0"

get_settings() -> Settings cached

Return an instance of the Settings class.

Returns

Settings: An instance of the Settings class.

Source code in sharkservers/settings.py
125
126
127
128
129
130
131
132
133
134
@lru_cache
def get_settings() -> Settings:
    """
    Return an instance of the Settings class.

    Returns
    -------
    Settings: An instance of the Settings class.
    """
    return Settings()

subscryptions

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

models

services

views

users

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

dependencies

Module contains dependency functions for user-related operations.

Dependencies are functions that are used to inject objects or services into other functions or endpoints. These functions are used as parameters in FastAPI's Depends decorator.

Functions: - get_users_service: Returns an instance of the UserService class. - get_valid_user: Validates a user ID and returns the corresponding user model. - get_users_sessions_service: Returns an instance of the UserSessionService class. - get_valid_user_session: Validates a user session ID and returns the corresponding user session model.

get_users_service() -> UserService async

Return an instance of the UserService class.

Returns
UserService: An instance of the UserService class.
Source code in sharkservers/users/dependencies.py
23
24
25
26
27
28
29
30
31
async def get_users_service() -> UserService:
    """
    Return an instance of the UserService class.

    Returns
    -------
        UserService: An instance of the UserService class.
    """
    return UserService()

get_valid_user(user_id: int, users_service: UserService = Depends(get_users_service)) -> Model async

Retrieves a valid user by their ID.


user_id (int): The ID of the user.
users_service (UserService, optional): The user service dependency. Defaults to Depends(get_users_service).

Model: The user model.

user_not_found_exception: If the user is not found.
Source code in sharkservers/users/dependencies.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def get_valid_user(
    user_id: int,
    users_service: UserService = Depends(get_users_service),
) -> Model:
    """
    Retrieves a valid user by their ID.

    Args:
    ----
        user_id (int): The ID of the user.
        users_service (UserService, optional): The user service dependency. Defaults to Depends(get_users_service).

    Returns:
    -------
        Model: The user model.

    Raises:
    ------
        user_not_found_exception: If the user is not found.
    """  # noqa: D401
    try:
        return await users_service.get_one(
            id=user_id,
            related=["roles", "display_role", "player", "player__steamrep_profile"],
        )
    except NoMatch as err:
        raise user_not_found_exception from err

get_users_sessions_service() -> UserSessionService async

Get the UserSessionService instance.

Returns
UserSessionService: The UserSessionService instance.
Source code in sharkservers/users/dependencies.py
63
64
65
66
67
68
69
70
71
async def get_users_sessions_service() -> UserSessionService:
    """
    Get the UserSessionService instance.

    Returns
    -------
        UserSessionService: The UserSessionService instance.
    """
    return UserSessionService()

get_valid_user_session(session_id: UUID, users_sessions_service: UserSessionService = Depends(get_users_sessions_service)) -> UserSession async

Retrieve a valid user session by session ID.


session_id (UUID): The ID of the session to retrieve.
users_sessions_service (UserSessionService, optional): The user sessions service. Defaults to the result of `get_users_sessions_service`.

UserSession: The retrieved user session.

user_not_found_exception: If the user session is not found.
Source code in sharkservers/users/dependencies.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
async def get_valid_user_session(
    session_id: UUID,
    users_sessions_service: UserSessionService = Depends(get_users_sessions_service),
) -> UserSession:
    """
    Retrieve a valid user session by session ID.

    Args:
    ----
        session_id (UUID): The ID of the session to retrieve.
        users_sessions_service (UserSessionService, optional): The user sessions service. Defaults to the result of `get_users_sessions_service`.

    Returns:
    -------
        UserSession: The retrieved user session.

    Raises:
    ------
        user_not_found_exception: If the user session is not found.
    """
    try:
        return await users_sessions_service.get_one(
            id=session_id,
        )
    except NoMatch:
        raise user_not_found_exception from None

enums

Module contains the enum classes used in the Sharkservers API for users.

UsersExceptionsDetailEnum

Bases: str, Enum

Represents the detail of the exceptions raised in the Sharkservers API for users.

Source code in sharkservers/users/enums.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class UsersExceptionsDetailEnum(str, Enum):
    """Represents the detail of the exceptions raised in the Sharkservers API for users."""

    USER_NOT_FOUND = "User not found"
    USERNAME_NOT_AVAILABLE = "Username not available"
    INVALID_CURRENT_PASSWORD = "Invalid current password"  # noqa: S105
    CANNOT_CHANGE_DISPLAY_ROLE = (
        "U cannot change your display role if u don't have this role"
    )
    USER_NOT_BANNED = "User not banned"
    USER_ALREADY_BANNED = "User already banned"
    USER_EMAIL_IS_THE_SAME = "Email is the same"

UsersEventsEnum

Bases: str, Enum

Represents the events raised in the Sharkservers API for users.

Source code in sharkservers/users/enums.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class UsersEventsEnum(str, Enum):
    """Represents the events raised in the Sharkservers API for users."""

    GET_ALL_PRE = "USERS_GET_ALL_PRE"
    GET_ALL_POST = "USERS_GET_ALL_POST"
    GET_ONE_PRE = "USERS_GET_ONE_PRE"
    GET_ONE_POST = "USERS_GET_ONE_POST"
    ME_PRE = "USERS_ME_PRE"
    ME_POST = "USERS_ME_POST"
    CHANGE_USERNAME_PRE = "USERS_CHANGE_USERNAME_PRE"
    CHANGE_USERNAME_POST = "USERS_CHANGE_USERNAME_POST"
    CHANGE_PASSWORD_PRE = "USERS_CHANGE_PASSWORD_PRE"  # noqa: S105
    CHANGE_PASSWORD_POST = "USERS_CHANGE_PASSWORD_POST"  # noqa: S105
    CHANGE_DISPLAY_ROLE_PRE = "USERS_CHANGE_DISPLAY_ROLE_PRE"
    CHANGE_DISPLAY_ROLE_POST = "USERS_CHANGE_DISPLAY_ROLE_POST"
    GET_LAST_LOGGED_PRE = "USERS_GET_LAST_LOGGED_PRE"
    GET_LAST_LOGGED_POST = "USERS_GET_LAST_LOGGED_POST"
    GET_ONLINE_PRE = "USERS_GET_ONLINE_PRE"
    GET_ONLINE_POST = "USERS_GET_ONLINE_POST"

UsersAdminEventsEnum

Bases: str, Enum

Represents the events raised in the Sharkservers API for users.

Source code in sharkservers/users/enums.py
40
41
42
43
44
45
46
class UsersAdminEventsEnum(str, Enum):
    """Represents the events raised in the Sharkservers API for users."""

    GET_ALL = "USERS_ADMIN_GET_ALL"
    GET_ONE = "USERS_ADMIN_GET_ONE"
    CREATE = "USERS_ADMIN_CREATE"
    DELETE = "USERS_ADMIN_DELETE"

exceptions

This module contains custom exceptions related to user operations.

Exceptions:
  • user_not_found_exception: Raised when a user is not found.
  • username_not_available_exception: Raised when a username is not available.
  • invalid_current_password_exception: Raised when the current password is invalid.
  • cannot_change_display_role_exception: Raised when a user tries to change their display role.
  • user_already_banned_exception: Raised when a user is already banned.
  • user_not_banned_exception: Raised when a user is not banned.
  • user_email_is_the_same_exception: Raised when a user tries to change their email to the same email.

models

Module contains the models for users, user sessions, and bans in the SharkServers API.

The User model represents a user in the system and includes fields such as username, email, password, and various statistics like threads_count, posts_count, and likes_count. It also has relationships with roles, player, and sessions.

The UserSession model represents a session of a user and includes fields for user IP and user agent.

The Ban model represents a ban on a user and includes fields for the banned user, reason, ban time, and the user who issued the ban.

The module also includes a pre_update hook for the User model that updates the "updated_at" field whenever a user is updated.

UserSession

Bases: Model, DateFieldsMixins

Represent a user session.

Attributes
id (str): The unique identifier for the user session.
user_ip (str): The IP address of the user.
user_agent (str): The user agent string of the client.
Source code in sharkservers/users/models.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class UserSession(ormar.Model, DateFieldsMixins):
    """
    Represent a user session.

    Attributes
    ----------
        id (str): The unique identifier for the user session.
        user_ip (str): The IP address of the user.
        user_agent (str): The user agent string of the client.
    """

    class Meta(BaseMeta):
        """Meta class for defining metadata options for the UserSessions model."""

        tablename = "user_sessions"

    id: str = ormar.UUID(primary_key=True, default=uuid.uuid4)
    user_ip: str = ormar.String(max_length=255)
    user_agent: str = ormar.String(max_length=255)
Meta

Bases: BaseMeta

Meta class for defining metadata options for the UserSessions model.

Source code in sharkservers/users/models.py
41
42
43
44
class Meta(BaseMeta):
    """Meta class for defining metadata options for the UserSessions model."""

    tablename = "user_sessions"

User

Bases: Model, DateFieldsMixins

Represents a user in the system.

Attributes
id (int): The unique identifier of the user.
username (str, optional): The username of the user. Max length is 64 characters.
email (str, optional): The email address of the user. Max length is 255 characters.
password (str, optional): The password of the user. Max length is 255 characters.
is_activated (bool, optional): Indicates if the user is activated.
is_superuser (bool, optional): Indicates if the user is a superuser.
avatar (str, optional): The path to the user's avatar image.
roles (List[Role], optional): The roles assigned to the user.
display_role (Role, optional): The role to be displayed for the user.
last_login (datetime.datetime, optional): The timestamp of the user's last login.
last_online (datetime.datetime, optional): The timestamp of the user's last online activity.
secret_salt (str, optional): The secret salt used for password hashing. Max length is 255 characters.
threads_count (int): The number of threads created by the user.
posts_count (int): The number of posts created by the user.
likes_count (int): The number of likes received by the user.
player (Player, optional): The associated player for the user.
sessions (List[UserSession], optional): The sessions associated with the user.
Source code in sharkservers/users/models.py
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
class User(ormar.Model, DateFieldsMixins):
    """
    Represents a user in the system.

    Attributes
    ----------
        id (int): The unique identifier of the user.
        username (str, optional): The username of the user. Max length is 64 characters.
        email (str, optional): The email address of the user. Max length is 255 characters.
        password (str, optional): The password of the user. Max length is 255 characters.
        is_activated (bool, optional): Indicates if the user is activated.
        is_superuser (bool, optional): Indicates if the user is a superuser.
        avatar (str, optional): The path to the user's avatar image.
        roles (List[Role], optional): The roles assigned to the user.
        display_role (Role, optional): The role to be displayed for the user.
        last_login (datetime.datetime, optional): The timestamp of the user's last login.
        last_online (datetime.datetime, optional): The timestamp of the user's last online activity.
        secret_salt (str, optional): The secret salt used for password hashing. Max length is 255 characters.
        threads_count (int): The number of threads created by the user.
        posts_count (int): The number of posts created by the user.
        likes_count (int): The number of likes received by the user.
        player (Player, optional): The associated player for the user.
        sessions (List[UserSession], optional): The sessions associated with the user.
    """

    class Meta(BaseMeta):
        """Meta class for defining metadata options for the User model."""

        tablename = "users"

    id: int = ormar.Integer(primary_key=True)
    username: str | None = ormar.String(max_length=64, unique=True)
    email: EmailStr | None = ormar.String(max_length=255, unique=True)
    password: str | None = ormar.String(max_length=255)
    is_activated: bool | None = ormar.Boolean(default=False)
    is_superuser: bool | None = ormar.Boolean(default=False)
    avatar: str | None = ormar.String(
        max_length=255,
        default="/static/avatars/avatars/default_avatar.png",
    )
    roles: list[Role] | None = ormar.ManyToMany(Role, related_name="user_roles")
    display_role: Role | None = ormar.ForeignKey(
        Role,
        related_name="user_display_role",
    )
    last_login: datetime.datetime | None = ormar.DateTime(nullable=True)
    last_online: datetime.datetime | None = ormar.DateTime(nullable=True)
    secret_salt: str | None = ormar.String(max_length=255, unique=True)
    threads_count: int = ormar.Integer(default=0)
    posts_count: int = ormar.Integer(default=0)
    likes_count: int = ormar.Integer(default=0)
    player: Player | None = ormar.ForeignKey(Player, related_name="player")
    sessions: UserSession | None = ormar.ManyToMany(
        UserSession,
        related_name="users_sessions",
    )
Meta

Bases: BaseMeta

Meta class for defining metadata options for the User model.

Source code in sharkservers/users/models.py
76
77
78
79
class Meta(BaseMeta):
    """Meta class for defining metadata options for the User model."""

    tablename = "users"

Ban

Bases: Model, DateFieldsMixins

Represent a ban in the system.

Attributes
id (int): The unique identifier for the ban.
user (Optional[User]): The user who is banned.
reason (Optional[str]): The reason for the ban.
ban_time (Optional[datetime.datetime]): The time when the ban was imposed.
banned_by (Optional[User]): The user who imposed the ban.
Source code in sharkservers/users/models.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
class Ban(ormar.Model, DateFieldsMixins):
    """
    Represent a ban in the system.

    Attributes
    ----------
        id (int): The unique identifier for the ban.
        user (Optional[User]): The user who is banned.
        reason (Optional[str]): The reason for the ban.
        ban_time (Optional[datetime.datetime]): The time when the ban was imposed.
        banned_by (Optional[User]): The user who imposed the ban.
    """

    class Meta(BaseMeta):
        """Meta class for defining metadata options for the Ban model."""

        tablename = "banned"

    id: int = ormar.Integer(primary_key=True)
    user: User | None = ormar.ForeignKey(User, related_name="banned_user")
    reason: str | None = ormar.String(max_length=255)
    ban_time: datetime.datetime | None = ormar.DateTime(nullable=True, timezone=True)
    banned_by: User | None = ormar.ForeignKey(User, related_name="banned_by")
Meta

Bases: BaseMeta

Meta class for defining metadata options for the Ban model.

Source code in sharkservers/users/models.py
122
123
124
125
class Meta(BaseMeta):
    """Meta class for defining metadata options for the Ban model."""

    tablename = "banned"

update_user_updated_at(sender, instance: User, **kwargs) -> None async

Update the 'updated_at' field of a User instance with the current datetime.


sender: The sender of the signal.
instance: The User instance being updated.
kwargs: Additional keyword arguments.
Source code in sharkservers/users/models.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@ormar.pre_update(User)
async def update_user_updated_at(
    sender, instance: User, **kwargs
) -> None:  # noqa: ARG001, ANN001, ANN003
    """
    Update the 'updated_at' field of a User instance with the current datetime.

    Args:
    ----
        sender: The sender of the signal.
        instance: The User instance being updated.
        kwargs: Additional keyword arguments.
    """
    instance.updated_at = now_datetime()

schemas

Module contains the schema definitions for the users in the SharkServers API.

It includes Pydantic models for various user-related operations such as creating a user, changing username, changing password, etc.

The module also defines query parameters for filtering and ordering user data.

UserSessionOut

Bases: user_session_out

Represents the output schema for a user session.

Source code in sharkservers/users/schemas.py
37
38
class UserSessionOut(user_session_out):
    """Represents the output schema for a user session."""

UserOut

Bases: user_out

Represents the output schema for a user.

Source code in sharkservers/users/schemas.py
41
42
class UserOut(user_out):
    """Represents the output schema for a user."""

UserOutWithEmail

Bases: user_out_with_email

Represents the output schema for a user with email.

Source code in sharkservers/users/schemas.py
45
46
class UserOutWithEmail(user_out_with_email):
    """Represents the output schema for a user with email."""

ChangeUsernameSchema

Bases: UsernameRegex

Schema for changing the username.

Attributes
new_username (str): The new username to be set.
Source code in sharkservers/users/schemas.py
49
50
51
52
53
54
55
56
57
58
class ChangeUsernameSchema(UsernameRegex):
    """
    Schema for changing the username.

    Attributes
    ----------
        new_username (str): The new username to be set.
    """

    new_username: str

SuccessChangeUsernameSchema

Bases: BaseModel

Schema for successful username change response.

Source code in sharkservers/users/schemas.py
61
62
63
64
65
class SuccessChangeUsernameSchema(BaseModel):
    """Schema for successful username change response."""

    old_username: str
    new_username: str

ChangePasswordSchema

Bases: BaseModel

Schema for changing password.

Attributes
current_password (str): The current password.
new_password (str): The new password.
new_password2 (str): The confirmation of the new password.
Raises
ValueError: If the new_password2 does not match the new_password.
Source code in sharkservers/users/schemas.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class ChangePasswordSchema(BaseModel):
    """
    Schema for changing password.

    Attributes
    ----------
        current_password (str): The current password.
        new_password (str): The new password.
        new_password2 (str): The confirmation of the new password.

    Raises
    ------
        ValueError: If the new_password2 does not match the new_password.
    """

    current_password: str
    new_password: str
    new_password2: str

    @validator("new_password2")
    def passwords_match(
        cls, value, values, **kwargs
    ) -> None:  # noqa: ANN001, ANN003, ARG002, N805
        """Validate that the new_password2 matches the new_password."""
        if "new_password" in values and value != values["new_password"]:
            msg = "Passwords do not match"
            raise ValueError(msg)
passwords_match(value, values, **kwargs) -> None

Validate that the new_password2 matches the new_password.

Source code in sharkservers/users/schemas.py
87
88
89
90
91
92
93
94
@validator("new_password2")
def passwords_match(
    cls, value, values, **kwargs
) -> None:  # noqa: ANN001, ANN003, ARG002, N805
    """Validate that the new_password2 matches the new_password."""
    if "new_password" in values and value != values["new_password"]:
        msg = "Passwords do not match"
        raise ValueError(msg)

ChangeEmailSchema

Bases: BaseModel

Schema for changing email.

Attributes
email (str): The new email address.
Source code in sharkservers/users/schemas.py
 97
 98
 99
100
101
102
103
104
105
106
class ChangeEmailSchema(BaseModel):
    """
    Schema for changing email.

    Attributes
    ----------
        email (str): The new email address.
    """

    email: EmailStr

ChangeDisplayRoleSchema

Bases: BaseModel

Schema for changing the display role of a user.

Source code in sharkservers/users/schemas.py
109
110
111
112
class ChangeDisplayRoleSchema(BaseModel):
    """Schema for changing the display role of a user."""

    role_id: int

SuccessChangeDisplayRoleSchema

Bases: BaseModel

Schema for representing a successful change in display role.

Source code in sharkservers/users/schemas.py
115
116
117
118
119
class SuccessChangeDisplayRoleSchema(BaseModel):
    """Schema for representing a successful change in display role."""

    old_role_id: int
    new_role_id: int

CreateUserSchema

Bases: BaseModel

Schema for creating a user.

Source code in sharkservers/users/schemas.py
122
123
124
125
126
127
128
129
class CreateUserSchema(BaseModel):
    """Schema for creating a user."""

    username: str
    email: str
    password: str
    is_activated: bool = False
    is_superuser: bool = False

CreateAdminUserSchema

Bases: CreateUserSchema

Schema for creating an admin user.

Source code in sharkservers/users/schemas.py
132
133
134
135
class CreateAdminUserSchema(CreateUserSchema):
    """Schema for creating an admin user."""

    is_admin: bool = True

BanUserSchema

Bases: BaseModel

Schema for banning a user.

Source code in sharkservers/users/schemas.py
138
139
140
141
142
class BanUserSchema(BaseModel):
    """Schema for banning a user."""

    reason: str
    ban_time: int

AdminUpdateUserSchema

Bases: BaseModel

Schema for updating a user.

Source code in sharkservers/users/schemas.py
145
146
147
148
149
150
151
152
153
154
155
156
class AdminUpdateUserSchema(BaseModel):
    """Schema for updating a user."""

    username: str | None
    email: str | None
    password: str | None
    is_activated: bool | None
    is_superuser: bool | None
    avatar: str | None
    roles: list[int] | None
    display_role: int | None
    secret_salt: str | None

UserQuery

Bases: OrderQuery

Query parameters for filtering and ordering users.

Source code in sharkservers/users/schemas.py
159
160
161
162
class UserQuery(OrderQuery):
    """Query parameters for filtering and ordering users."""

    username: str | None = Query(None, description="Username")

services

Module contains the services for managing user-related operations.

It includes the following classes: - UserService: Provides methods for managing user data, such as changing username, password, and display role. - UserSessionService: Provides methods for managing user session data.

UserService

Bases: BaseService

Service class for managing user-related operations.

Source code in sharkservers/users/services.py
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
class UserService(BaseService):
    """Service class for managing user-related operations."""

    class Meta:
        """Meta class for defining metadata options for the User model."""

        model = User
        not_found_exception = user_not_found_exception

    async def get_last_online_users(self, params: Params) -> Page[UserOut]:
        """
        Get the last online users.

        Args:
        ----
            params (Params): Pagination parameters.

        Returns:
        -------
            Page[UserOut]: A paginated list of UserOut objects.

        """
        filter_after = now_datetime() - timedelta(minutes=15)
        return await self.get_all(
            params=params,
            related=["display_role", "player", "player__steamrep_profile"],
            last_online__gt=filter_after,
        )

    @staticmethod
    async def change_username(
        user: User,
        change_username_data: ChangeUsernameSchema,
    ) -> User:
        """
        Change the username of a user.

        Args:
        ----
            user (User): The user object.
            change_username_data (ChangeUsernameSchema): The new username.

        Returns:
        -------
            User: The updated user object.

        Raises:
        ------
            username_not_available_exception: If the new username is not available.

        """
        try:
            await user.update(username=change_username_data.username)
        except (UniqueViolationError, IntegrityError, SQLIntegrityError) as err:
            raise username_not_available_exception from err
        else:
            return user

    @staticmethod
    async def change_password(
        user: User,
        change_password_data: ChangePasswordSchema,
    ) -> User:
        """
        Change the password of a user.

        Args:
        ----
            user (User): The user object.
            change_password_data (ChangePasswordSchema): The new password.

        Returns:
        -------
            User: The updated user object.

        Raises:
        ------
            invalid_current_password_exception: If the current password is invalid.

        """
        if not verify_password(change_password_data.current_password, user.password):
            raise invalid_current_password_exception
        new_password = get_password_hash(change_password_data.new_password)
        await user.update(password=new_password)
        return user

    @staticmethod
    async def change_display_role(
        user: User,
        change_display_role_data: ChangeDisplayRoleSchema,
    ) -> (User, int):
        """
        Change the display role of a user.

        Args:
        ----
            user (User): The user object.
            change_display_role_data (ChangeDisplayRoleSchema): The new display role.

        Returns:
        -------
            Tuple[User, int]: The updated user object and the old display role ID.

        Raises:
        ------
            cannot_change_display_role_exception: If the new display role is not available.

        """
        display_role_exists_in_user_roles = False
        old_user_display_role = user.display_role.id
        for role in user.roles:
            if role.id == change_display_role_data.role_id:
                display_role_exists_in_user_roles = True
                break
        if not display_role_exists_in_user_roles:
            raise cannot_change_display_role_exception
        await user.update(display_role=change_display_role_data.role_id)
        return user, old_user_display_role

    @staticmethod
    async def create_confirm_email_code(
        code_service: CodeService,
        user: User,
        new_email: EmailStr,
    ) -> (str, EmailStr):
        """
        Create a confirmation code for changing the user's email.

        Args:
        ----
            code_service (CodeService): The code service object.
            user (User): The user object.
            new_email (EmailStr): The new email address.

        Returns:
        -------
            Tuple[str, EmailStr]: The confirmation code and the new email address.

        Raises:
        ------
            user_email_is_the_same_exception: If the new email is the same as the current email.

        """
        if user.email == new_email:
            raise user_email_is_the_same_exception
        redis_data = {
            "user_id": user.id,
            "new_email": new_email,
        }
        confirm_code, _data = await code_service.create(
            data=json.dumps(redis_data),
            code_len=5,
            expire=900,
        )
        return confirm_code, new_email

    async def confirm_change_email(self, code_service: CodeService, code: str) -> User:
        """
        Confirm the change of the user's email.

        Args:
        ----
            code_service (CodeService): The code service object.
            code (str): The confirmation code.

        Returns:
        -------
            User: The updated user object.

        Raises:
        ------
            invalid_activation_code_exception: If the activation code is invalid.

        """
        user_data = await code_service.get(code)
        if user_data is None:
            raise invalid_activation_code_exception
        user_data = json.loads(user_data)
        user_id = user_data.get("user_id", None)
        new_email = user_data.get("new_email", None)
        user_id = int(user_id)

        user = await self.get_one(id=user_id, related=["display_role", "roles"])
        await user.update(email=new_email)
        await code_service.delete(code)
        return user

    async def upload_avatar(  # noqa: PLR0913
        self,
        user: User,
        avatar: UploadFile,
        request: Request,
        upload_service: UploadService,
        settings: Settings,
    ) -> dict:
        """
        Upload and update the user's avatar.

        Args:
        ----
            user (User): The user object.
            avatar (UploadFile): The avatar file to upload.
            request (Request): The request object.
            upload_service (UploadService): The upload service object.
            settings (Settings): The application settings.

        Returns:
        -------
            dict: A dictionary containing the old and new avatar URLs.

        Raises:
        ------
            HTTPException: If there is an error during the avatar upload.

        """
        try:
            file_data = await upload_service.upload_avatar(file=avatar)
            file_name = file_data.get("file_name")
            default_avatar_url = request.url_for(
                "static",
                path="images/default_avatar.png",
            )
            avatar_url = request.url_for("static", path=f"uploads/avatars/{file_name}")
            old_avatar_url = user.avatar
            await user.update(avatar=str(avatar_url))
            if old_avatar_url != default_avatar_url:
                old_avatar_filename = old_avatar_url.split("/")[-1]
                upload_service.delete_avatar(old_avatar_filename)

            if settings.TESTING:
                upload_service.delete_avatar(file_name)
            else:
                return {
                    "old_avatar_url": old_avatar_url,
                    "avatar_url": avatar_url,
                }
        except HTTPException:  # noqa: TRY302
            raise

    async def sync_counters(
        self,
        threads_service: ThreadService,
        posts_service: PostService,
    ) -> None:
        """
        Synchronize the thread and post counters for all users.

        Args:
        ----
            threads_service (ThreadService): The thread service object.
            posts_service (PostService): The post service object.

        """
        try:
            users = await self.Meta.model.objects.select_related(
                ["user_threads", "user_posts", "user_reputation"],
            ).all()
            for user in users:
                threads_count = (
                    await threads_service.Meta.model.objects.select_related("posts")
                    .filter(author=user)
                    .count()
                )
                posts_count = (
                    await posts_service.Meta.model.objects.select_related("likes")
                    .filter(author=user)
                    .count()
                )
                await user.update(threads_count=threads_count, posts_count=posts_count)
            logger.info(f"Finished sync counters to users -> {len(users)}")
        except Exception as e:  # noqa: BLE001
            logger.error(e.message)
Meta

Meta class for defining metadata options for the User model.

Source code in sharkservers/users/services.py
47
48
49
50
51
class Meta:
    """Meta class for defining metadata options for the User model."""

    model = User
    not_found_exception = user_not_found_exception
get_last_online_users(params: Params) -> Page[UserOut] async

Get the last online users.


params (Params): Pagination parameters.

Page[UserOut]: A paginated list of UserOut objects.
Source code in sharkservers/users/services.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
async def get_last_online_users(self, params: Params) -> Page[UserOut]:
    """
    Get the last online users.

    Args:
    ----
        params (Params): Pagination parameters.

    Returns:
    -------
        Page[UserOut]: A paginated list of UserOut objects.

    """
    filter_after = now_datetime() - timedelta(minutes=15)
    return await self.get_all(
        params=params,
        related=["display_role", "player", "player__steamrep_profile"],
        last_online__gt=filter_after,
    )
change_username(user: User, change_username_data: ChangeUsernameSchema) -> User async staticmethod

Change the username of a user.


user (User): The user object.
change_username_data (ChangeUsernameSchema): The new username.

User: The updated user object.

username_not_available_exception: If the new username is not available.
Source code in sharkservers/users/services.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@staticmethod
async def change_username(
    user: User,
    change_username_data: ChangeUsernameSchema,
) -> User:
    """
    Change the username of a user.

    Args:
    ----
        user (User): The user object.
        change_username_data (ChangeUsernameSchema): The new username.

    Returns:
    -------
        User: The updated user object.

    Raises:
    ------
        username_not_available_exception: If the new username is not available.

    """
    try:
        await user.update(username=change_username_data.username)
    except (UniqueViolationError, IntegrityError, SQLIntegrityError) as err:
        raise username_not_available_exception from err
    else:
        return user
change_password(user: User, change_password_data: ChangePasswordSchema) -> User async staticmethod

Change the password of a user.


user (User): The user object.
change_password_data (ChangePasswordSchema): The new password.

User: The updated user object.

invalid_current_password_exception: If the current password is invalid.
Source code in sharkservers/users/services.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@staticmethod
async def change_password(
    user: User,
    change_password_data: ChangePasswordSchema,
) -> User:
    """
    Change the password of a user.

    Args:
    ----
        user (User): The user object.
        change_password_data (ChangePasswordSchema): The new password.

    Returns:
    -------
        User: The updated user object.

    Raises:
    ------
        invalid_current_password_exception: If the current password is invalid.

    """
    if not verify_password(change_password_data.current_password, user.password):
        raise invalid_current_password_exception
    new_password = get_password_hash(change_password_data.new_password)
    await user.update(password=new_password)
    return user
change_display_role(user: User, change_display_role_data: ChangeDisplayRoleSchema) -> (User, int) async staticmethod

Change the display role of a user.


user (User): The user object.
change_display_role_data (ChangeDisplayRoleSchema): The new display role.

Tuple[User, int]: The updated user object and the old display role ID.

cannot_change_display_role_exception: If the new display role is not available.
Source code in sharkservers/users/services.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
@staticmethod
async def change_display_role(
    user: User,
    change_display_role_data: ChangeDisplayRoleSchema,
) -> (User, int):
    """
    Change the display role of a user.

    Args:
    ----
        user (User): The user object.
        change_display_role_data (ChangeDisplayRoleSchema): The new display role.

    Returns:
    -------
        Tuple[User, int]: The updated user object and the old display role ID.

    Raises:
    ------
        cannot_change_display_role_exception: If the new display role is not available.

    """
    display_role_exists_in_user_roles = False
    old_user_display_role = user.display_role.id
    for role in user.roles:
        if role.id == change_display_role_data.role_id:
            display_role_exists_in_user_roles = True
            break
    if not display_role_exists_in_user_roles:
        raise cannot_change_display_role_exception
    await user.update(display_role=change_display_role_data.role_id)
    return user, old_user_display_role
create_confirm_email_code(code_service: CodeService, user: User, new_email: EmailStr) -> (str, EmailStr) async staticmethod

Create a confirmation code for changing the user's email.


code_service (CodeService): The code service object.
user (User): The user object.
new_email (EmailStr): The new email address.

Tuple[str, EmailStr]: The confirmation code and the new email address.

user_email_is_the_same_exception: If the new email is the same as the current email.
Source code in sharkservers/users/services.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@staticmethod
async def create_confirm_email_code(
    code_service: CodeService,
    user: User,
    new_email: EmailStr,
) -> (str, EmailStr):
    """
    Create a confirmation code for changing the user's email.

    Args:
    ----
        code_service (CodeService): The code service object.
        user (User): The user object.
        new_email (EmailStr): The new email address.

    Returns:
    -------
        Tuple[str, EmailStr]: The confirmation code and the new email address.

    Raises:
    ------
        user_email_is_the_same_exception: If the new email is the same as the current email.

    """
    if user.email == new_email:
        raise user_email_is_the_same_exception
    redis_data = {
        "user_id": user.id,
        "new_email": new_email,
    }
    confirm_code, _data = await code_service.create(
        data=json.dumps(redis_data),
        code_len=5,
        expire=900,
    )
    return confirm_code, new_email
confirm_change_email(code_service: CodeService, code: str) -> User async

Confirm the change of the user's email.


code_service (CodeService): The code service object.
code (str): The confirmation code.

User: The updated user object.

invalid_activation_code_exception: If the activation code is invalid.
Source code in sharkservers/users/services.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
async def confirm_change_email(self, code_service: CodeService, code: str) -> User:
    """
    Confirm the change of the user's email.

    Args:
    ----
        code_service (CodeService): The code service object.
        code (str): The confirmation code.

    Returns:
    -------
        User: The updated user object.

    Raises:
    ------
        invalid_activation_code_exception: If the activation code is invalid.

    """
    user_data = await code_service.get(code)
    if user_data is None:
        raise invalid_activation_code_exception
    user_data = json.loads(user_data)
    user_id = user_data.get("user_id", None)
    new_email = user_data.get("new_email", None)
    user_id = int(user_id)

    user = await self.get_one(id=user_id, related=["display_role", "roles"])
    await user.update(email=new_email)
    await code_service.delete(code)
    return user
upload_avatar(user: User, avatar: UploadFile, request: Request, upload_service: UploadService, settings: Settings) -> dict async

Upload and update the user's avatar.


user (User): The user object.
avatar (UploadFile): The avatar file to upload.
request (Request): The request object.
upload_service (UploadService): The upload service object.
settings (Settings): The application settings.

dict: A dictionary containing the old and new avatar URLs.

HTTPException: If there is an error during the avatar upload.
Source code in sharkservers/users/services.py
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
async def upload_avatar(  # noqa: PLR0913
    self,
    user: User,
    avatar: UploadFile,
    request: Request,
    upload_service: UploadService,
    settings: Settings,
) -> dict:
    """
    Upload and update the user's avatar.

    Args:
    ----
        user (User): The user object.
        avatar (UploadFile): The avatar file to upload.
        request (Request): The request object.
        upload_service (UploadService): The upload service object.
        settings (Settings): The application settings.

    Returns:
    -------
        dict: A dictionary containing the old and new avatar URLs.

    Raises:
    ------
        HTTPException: If there is an error during the avatar upload.

    """
    try:
        file_data = await upload_service.upload_avatar(file=avatar)
        file_name = file_data.get("file_name")
        default_avatar_url = request.url_for(
            "static",
            path="images/default_avatar.png",
        )
        avatar_url = request.url_for("static", path=f"uploads/avatars/{file_name}")
        old_avatar_url = user.avatar
        await user.update(avatar=str(avatar_url))
        if old_avatar_url != default_avatar_url:
            old_avatar_filename = old_avatar_url.split("/")[-1]
            upload_service.delete_avatar(old_avatar_filename)

        if settings.TESTING:
            upload_service.delete_avatar(file_name)
        else:
            return {
                "old_avatar_url": old_avatar_url,
                "avatar_url": avatar_url,
            }
    except HTTPException:  # noqa: TRY302
        raise
sync_counters(threads_service: ThreadService, posts_service: PostService) -> None async

Synchronize the thread and post counters for all users.


threads_service (ThreadService): The thread service object.
posts_service (PostService): The post service object.
Source code in sharkservers/users/services.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
async def sync_counters(
    self,
    threads_service: ThreadService,
    posts_service: PostService,
) -> None:
    """
    Synchronize the thread and post counters for all users.

    Args:
    ----
        threads_service (ThreadService): The thread service object.
        posts_service (PostService): The post service object.

    """
    try:
        users = await self.Meta.model.objects.select_related(
            ["user_threads", "user_posts", "user_reputation"],
        ).all()
        for user in users:
            threads_count = (
                await threads_service.Meta.model.objects.select_related("posts")
                .filter(author=user)
                .count()
            )
            posts_count = (
                await posts_service.Meta.model.objects.select_related("likes")
                .filter(author=user)
                .count()
            )
            await user.update(threads_count=threads_count, posts_count=posts_count)
        logger.info(f"Finished sync counters to users -> {len(users)}")
    except Exception as e:  # noqa: BLE001
        logger.error(e.message)

UserSessionService

Bases: BaseService

Service class for managing user session-related operations.

Source code in sharkservers/users/services.py
318
319
320
321
322
323
324
325
class UserSessionService(BaseService):
    """Service class for managing user session-related operations."""

    class Meta:
        """Meta class for defining metadata options for the UserSession model."""

        model = UserSession
        not_found_exception = user_not_found_exception
Meta

Meta class for defining metadata options for the UserSession model.

Source code in sharkservers/users/services.py
321
322
323
324
325
class Meta:
    """Meta class for defining metadata options for the UserSession model."""

    model = UserSession
    not_found_exception = user_not_found_exception

views

sharkservers_api.

sharkservers-api

(C) 2023-present Adrian Ciołek (Qwizi)

admin

Module contains the API endpoints related to admin users.

Functions: - admin_get_users: Retrieves a paginated list of users. - admin_get_user: Retrieves a specific user. - admin_create_user: Creates a user. - admin_delete_user: Deletes a user. - admin_update_user: Updates a user.

admin_get_users(params: Params = Depends(), users_service: UserService = Depends(get_users_service)) -> Page[UserOutWithEmail] async

Retrieve all users with their associated data.


params (Params, optional): The parameters for filtering and pagination. Defaults to Depends().
users_service (UserService, optional): The service for retrieving user data. Defaults to Depends(get_users_service).

Page[UserOutWithEmail]: The paginated list of users with their associated data.
Source code in sharkservers/users/views/admin.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@router.get(
    "",
    response_model_exclude_none=True,
    dependencies=[Security(get_admin_user, scopes=["users:all"])],
)
async def admin_get_users(
    params: Params = Depends(),
    users_service: UserService = Depends(get_users_service),
) -> Page[UserOutWithEmail]:
    """
    Retrieve all users with their associated data.

    Args:
    ----
        params (Params, optional): The parameters for filtering and pagination. Defaults to Depends().
        users_service (UserService, optional): The service for retrieving user data. Defaults to Depends(get_users_service).

    Returns:
    -------
        Page[UserOutWithEmail]: The paginated list of users with their associated data.
    """
    users = await users_service.get_all(
        params=params,
        related=["display_role", "player", "player__steamrep_profile"],
    )
    dispatch(UsersAdminEventsEnum.GET_ALL, payload={"data": users})
    return users
admin_get_user(user: User = Depends(get_valid_user)) -> UserOutWithEmail async

Retrieve the user information for an admin user.


user (User): The admin user object.

UserOutWithEmail: The user information with email.
Source code in sharkservers/users/views/admin.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@router.get(
    "/{user_id}",
    dependencies=[Security(get_admin_user, scopes=["users:retrieve"])],
)
async def admin_get_user(
    user: User = Depends(get_valid_user),
) -> UserOutWithEmail:
    """
    Retrieve the user information for an admin user.

    Args:
    ----
        user (User): The admin user object.

    Returns:
    -------
        UserOutWithEmail: The user information with email.

    """
    return user
admin_create_user(user_data: CreateUserSchema, auth_service: AuthService = Depends(get_auth_service), settings: Settings = Depends(get_settings)) -> UserOutWithEmail async

Create a new user with the provi print(roles) ded user data.


user_data (CreateUserSchema): The data for creating a new user.
auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).
settings (Settings, optional): The application settings. Defaults to Depends(get_settings).

UserOutWithEmail: The created user with email.
Source code in sharkservers/users/views/admin.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@router.post(
    "",
    response_model=UserOutWithEmail,
    dependencies=[Security(get_admin_user, scopes=["users:create"])],
)
async def admin_create_user(
    user_data: CreateUserSchema,
    auth_service: AuthService = Depends(get_auth_service),
    settings: Settings = Depends(get_settings),
) -> UserOutWithEmail:
    """
    Create a new user with the provi    print(roles)
    ded user data.


    Args:
    ----
        user_data (CreateUserSchema): The data for creating a new user.
        auth_service (AuthService, optional): The authentication service. Defaults to Depends(get_auth_service).
        settings (Settings, optional): The application settings. Defaults to Depends(get_settings).

    Returns:
    -------
        UserOutWithEmail: The created user with email.
    """  # noqa: D205
    return await auth_service.register(
        RegisterUserSchema(
            username=user_data.username,
            email=user_data.email,
            password=user_data.password,
            password2=user_data.password,
        ),
        is_activated=user_data.is_activated,
        is_superuser=user_data.is_superuser,
        settings=settings,
    )
admin_delete_user(validate_user: User = Depends(get_valid_user), users_service: UserService = Depends(get_users_service)) -> UserOutWithEmail async

Deletes a user from the system.


validate_user (User): The user to be deleted.
users_service (UserService): The service responsible for user operations.

UserOutWithEmail: The deleted user with email.
Source code in sharkservers/users/views/admin.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
@router.delete(
    "/{user_id}",
    dependencies=[Security(get_admin_user, scopes=["users:delete"])],
)
async def admin_delete_user(
    validate_user: User = Depends(get_valid_user),
    users_service: UserService = Depends(get_users_service),
) -> UserOutWithEmail:
    """
    Deletes a user from the system.

    Args:
    ----
        validate_user (User): The user to be deleted.
        users_service (UserService): The service responsible for user operations.

    Returns:
    -------
        UserOutWithEmail: The deleted user with email.
    """  # noqa: D401
    return await users_service.delete(_id=validate_user.id)
admin_update_user(update_user_data: AdminUpdateUserSchema, validate_user: User = Depends(get_valid_user), roles_service: RoleService = Depends(get_roles_service)) -> UserOutWithEmail async

Admin function to update a user's information.


update_user_data (AdminUpdateUserSchema): The updated user data.
validate_user (User): The user to be updated.
roles_service (RoleService): The service for managing roles.

UserOutWithEmail: The updated user with email.
Source code in sharkservers/users/views/admin.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@router.put(
    "/{user_id}",
    dependencies=[Security(get_admin_user, scopes=["users:update"])],
)
async def admin_update_user(
    update_user_data: AdminUpdateUserSchema,
    validate_user: User = Depends(get_valid_user),
    roles_service: RoleService = Depends(get_roles_service),
) -> UserOutWithEmail:
    """
    Admin function to update a user's information.

    Args:
    ----
        update_user_data (AdminUpdateUserSchema): The updated user data.
        validate_user (User): The user to be updated.
        roles_service (RoleService): The service for managing roles.

    Returns:
    -------
        UserOutWithEmail: The updated user with email.

    """
    update_user_data_dict = update_user_data.dict()
    filtered = {k: v for k, v in update_user_data_dict.items() if v is not None}
    update_user_data_dict.clear()
    update_user_data_dict.update(filtered)
    password = update_user_data_dict.pop("password", None)
    if password:
        password_hash = get_password_hash(password)
        update_user_data_dict.update(password=password_hash)
    roles_ids: list[int] | None = update_user_data_dict.pop("roles", None)
    display_role_id: int | None = update_user_data_dict.pop("display_role", None)
    roles = []
    if roles_ids:
        for role_id in roles_ids:
            role = await roles_service.get_one(id=role_id)
            roles.append(role)
    if roles:
        for _role in validate_user.roles:
            await validate_user.roles.remove(_role)
        for role in roles:
            await validate_user.roles.add(role)
    if display_role_id:
        display_role = await roles_service.get_one(id=display_role_id)
        await validate_user.update(display_role=display_role)
    await validate_user.update(**update_user_data_dict)
    return validate_user

me

Module contains the API endpoints related to the currently logged-in user.

Functions: - get_logged_user: Retrieves the currently logged-in user. - get_logged_user_posts: Retrieves all posts created by the logged-in user. - get_logged_user_threads: Retrieves all threads created by the logged-in user. - change_user_username: Changes the username of the current user. - change_user_password: Changes the password of the current user. - request_change_user_email: Requests a change of user email. - confirm_change_user_email: Confirms the change of user email. - change_user_display_role: Changes the display role of the current user. - upload_user_avatar: Uploads the user's avatar. - connect_steam_profile: Connects a Steam profile to the user's account. - get_user_sessions: Retrieves the sessions of the current user. - delete_user_session: Deletes the user session.

get_logged_user(user: User = Security(get_current_active_user, scopes=['users:me'])) -> UserOutWithEmail async

Retrieve the currently logged-in user.


user (User): The currently logged-in user.

UserOutWithEmail: The logged-in user with email.
Source code in sharkservers/users/views/me.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@router.get(
    "",
)
async def get_logged_user(
    user: User = Security(get_current_active_user, scopes=["users:me"]),
) -> UserOutWithEmail:
    """
    Retrieve the currently logged-in user.

    Args:
    ----
        user (User): The currently logged-in user.

    Returns:
    -------
        UserOutWithEmail: The logged-in user with email.

    """
    return user
get_logged_user_posts(params: Params = Depends(), queries: OrderQuery = Depends(), user: User = Security(get_current_active_user, scopes=['users:me']), posts_service: PostService = Depends(get_posts_service)) -> Page[PostOut] async

Retrieves all posts created by the logged-in user.


params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
queries (OrderQuery, optional): The query parameters for ordering the posts. Defaults to Depends().
user (User, optional): The logged-in user. Defaults to Security(get_current_active_user, scopes=["users:me"]).
posts_service (PostService, optional): The service for retrieving posts. Defaults to Depends(get_posts_service).

List[Post]: The list of posts created by the logged-in user.
Source code in sharkservers/users/views/me.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@router.get("/posts")
async def get_logged_user_posts(
    params: Params = Depends(),
    queries: OrderQuery = Depends(),
    user: User = Security(get_current_active_user, scopes=["users:me"]),
    posts_service: PostService = Depends(get_posts_service),
) -> Page[PostOut]:
    """
    Retrieves all posts created by the logged-in user.

    Args:
    ----
        params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
        queries (OrderQuery, optional): The query parameters for ordering the posts. Defaults to Depends().
        user (User, optional): The logged-in user. Defaults to Security(get_current_active_user, scopes=["users:me"]).
        posts_service (PostService, optional): The service for retrieving posts. Defaults to Depends(get_posts_service).

    Returns:
    -------
        List[Post]: The list of posts created by the logged-in user.
    """  # noqa: D401
    return await posts_service.get_all(
        params=params,
        author__id=user.id,
        order_by=queries.order_by,
    )
get_logged_user_threads(params: Params = Depends(), queries: OrderQuery = Depends(), user: User = Security(get_current_active_user, scopes=['users:me']), threads_service: ThreadService = Depends(get_threads_service)) -> Page[ThreadOut] async

Retrieve all threads for the logged-in user.


params (Params): The parameters for filtering and pagination.
queries (OrderQuery): The query parameters for ordering.
user (User): The logged-in user.
threads_service (ThreadService): The service for handling threads.

List[Thread]: The list of threads for the logged-in user.
Source code in sharkservers/users/views/me.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
@router.get("/threads")
async def get_logged_user_threads(
    params: Params = Depends(),
    queries: OrderQuery = Depends(),
    user: User = Security(get_current_active_user, scopes=["users:me"]),
    threads_service: ThreadService = Depends(get_threads_service),
) -> Page[ThreadOut]:
    """
    Retrieve all threads for the logged-in user.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        queries (OrderQuery): The query parameters for ordering.
        user (User): The logged-in user.
        threads_service (ThreadService): The service for handling threads.

    Returns:
    -------
        List[Thread]: The list of threads for the logged-in user.
    """
    return await threads_service.get_all(
        params=params,
        author__id=user.id,
        order_by=queries.order_by,
    )
change_user_username(change_username_data: ChangeUsernameSchema, user: User = Security(get_current_active_user, scopes=['users:me:username']), users_service: UserService = Depends(get_users_service)) -> SuccessChangeUsernameSchema async

Change the username of the current user.


change_username_data (ChangeUsernameSchema): The data containing the new username.
user (User): The current authenticated user.
users_service (UserService): The service responsible for handling user-related operations.

SuccessChangeUsernameSchema: The response containing the old and new usernames.
Source code in sharkservers/users/views/me.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
@router.post("/username")
async def change_user_username(
    change_username_data: ChangeUsernameSchema,
    user: User = Security(get_current_active_user, scopes=["users:me:username"]),
    users_service: UserService = Depends(get_users_service),
) -> SuccessChangeUsernameSchema:
    """
    Change the username of the current user.

    Args:
    ----
        change_username_data (ChangeUsernameSchema): The data containing the new username.
        user (User): The current authenticated user.
        users_service (UserService): The service responsible for handling user-related operations.

    Returns:
    -------
        SuccessChangeUsernameSchema: The response containing the old and new usernames.
    """
    old_username = user.username
    user = await users_service.change_username(user, change_username_data)
    return SuccessChangeUsernameSchema(
        old_username=old_username,
        new_username=change_username_data.username,
    )
change_user_password(change_password_data: ChangePasswordSchema, user: User = Security(get_current_active_user, scopes=['users:me:password']), users_service: UserService = Depends(get_users_service)) -> dict async

Change the password of the current user.


change_password_data (ChangePasswordSchema): The new password data.
user (User): The current user.
users_service (UserService): The service for managing users.

dict: A dictionary with a success message.
Source code in sharkservers/users/views/me.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@router.post("/password")
async def change_user_password(
    change_password_data: ChangePasswordSchema,
    user: User = Security(get_current_active_user, scopes=["users:me:password"]),
    users_service: UserService = Depends(get_users_service),
) -> dict:
    """
    Change the password of the current user.

    Args:
    ----
        change_password_data (ChangePasswordSchema): The new password data.
        user (User): The current user.
        users_service (UserService): The service for managing users.

    Returns:
    -------
        dict: A dictionary with a success message.
    """
    user = await users_service.change_password(user, change_password_data)
    return {"msg": "Successfully changed password"}
request_change_user_email(change_email_data: ChangeEmailSchema, background_tasks: BackgroundTasks, user: User = Security(get_current_active_user, scopes=['users:me']), email_service: EmailService = Depends(get_email_service), code_service: CodeService = Depends(get_change_account_email_code_service), users_service: UserService = Depends(get_users_service)) -> dict[str, str] async

Request a change of user email.


change_email_data (ChangeEmailSchema): The data for the email change request.
background_tasks (BackgroundTasks): The background tasks manager.
user (User): The current authenticated user.
email_service (EmailService): The email service.
code_service (CodeService): The code service for email confirmation codes.
users_service (UserService): The user service.

dict: A dictionary with a success message indicating that the request for email change was sent.
Source code in sharkservers/users/views/me.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
@router.post("/email", dependencies=[Depends(limiter)])
async def request_change_user_email(  # noqa: PLR0913
    change_email_data: ChangeEmailSchema,
    background_tasks: BackgroundTasks,
    user: User = Security(get_current_active_user, scopes=["users:me"]),
    email_service: EmailService = Depends(get_email_service),
    code_service: CodeService = Depends(get_change_account_email_code_service),
    users_service: UserService = Depends(get_users_service),
) -> dict[str, str]:
    """
    Request a change of user email.

    Args:
    ----
        change_email_data (ChangeEmailSchema): The data for the email change request.
        background_tasks (BackgroundTasks): The background tasks manager.
        user (User): The current authenticated user.
        email_service (EmailService): The email service.
        code_service (CodeService): The code service for email confirmation codes.
        users_service (UserService): The user service.

    Returns:
    -------
        dict: A dictionary with a success message indicating that the request for email change was sent.
    """
    confirm_code, _data = await users_service.create_confirm_email_code(
        code_service=code_service,
        user=user,
        new_email=change_email_data.email,
    )
    background_tasks.add_task(
        email_service.send_confirmation_email,
        ActivationEmailTypeEnum.EMAIL,
        change_email_data.email,
        confirm_code,
    )
    return {"msg": "Request for change email was sent"}
confirm_change_user_email(activate_code_data: ActivateUserCodeSchema, code_service: CodeService = Depends(get_change_account_email_code_service), users_service: UserService = Depends(get_users_service)) -> UserOutWithEmail async

Confirm the change of user's email address using the activation code.


activate_code_data (ActivateUserCodeSchema): The activation code data.
code_service (CodeService, optional): The code service dependency. Defaults to Depends(get_change_account_email_code_service).
users_service (UserService, optional): The users service dependency. Defaults to Depends(get_users_service).

UserOutWithEmail: The updated user with the new email address.
Source code in sharkservers/users/views/me.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
@router.post(
    "/email/confirm",
    dependencies=[Security(get_current_active_user, scopes=["users:me"])],
)
async def confirm_change_user_email(
    activate_code_data: ActivateUserCodeSchema,
    code_service: CodeService = Depends(get_change_account_email_code_service),
    users_service: UserService = Depends(get_users_service),
) -> UserOutWithEmail:
    """
    Confirm the change of user's email address using the activation code.

    Args:
    ----
        activate_code_data (ActivateUserCodeSchema): The activation code data.
        code_service (CodeService, optional): The code service dependency. Defaults to Depends(get_change_account_email_code_service).
        users_service (UserService, optional): The users service dependency. Defaults to Depends(get_users_service).

    Returns:
    -------
        UserOutWithEmail: The updated user with the new email address.
    """
    return await users_service.confirm_change_email(
        code_service=code_service,
        code=activate_code_data.code,
    )
change_user_display_role(change_display_role_data: ChangeDisplayRoleSchema, user: User = Security(get_current_active_user, scopes=['users:me:display-role']), users_service: UserService = Depends(get_users_service)) -> UserOutWithEmail async

Change the display role of the current user.


change_display_role_data (ChangeDisplayRoleSchema): The data for changing the display role.
user (User): The current user.
users_service (UserService): The service for managing users.

UserOutWithEmail: The updated user object.
Source code in sharkservers/users/views/me.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
@router.post("/display-role")
async def change_user_display_role(
    change_display_role_data: ChangeDisplayRoleSchema,
    user: User = Security(get_current_active_user, scopes=["users:me:display-role"]),
    users_service: UserService = Depends(get_users_service),
) -> UserOutWithEmail:
    """
    Change the display role of the current user.

    Args:
    ----
        change_display_role_data (ChangeDisplayRoleSchema): The data for changing the display role.
        user (User): The current user.
        users_service (UserService): The service for managing users.

    Returns:
    -------
        UserOutWithEmail: The updated user object.
    """
    user, old_user_display_role = await users_service.change_display_role(
        user,
        change_display_role_data,
    )
    return user
upload_user_avatar(request: Request, avatar: UploadFile = File(...), user: User = Security(get_current_active_user, scopes=['users:me']), users_service: UserService = Depends(get_users_service), upload_service: UploadService = Depends(get_upload_service), settings: Settings = Depends(get_settings)) -> dict[str, str] async

Upload the user's avatar. sharkservers.

Returns
dict: A dictionary with a success message.
Source code in sharkservers/users/views/me.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
@router.post("/avatar")
async def upload_user_avatar(  # noqa: PLR0913
    request: Request,
    avatar: UploadFile = File(...),
    user: User = Security(get_current_active_user, scopes=["users:me"]),
    users_service: UserService = Depends(get_users_service),
    upload_service: UploadService = Depends(get_upload_service),
    settings: Settings = Depends(get_settings),
) -> dict[str, str]:
    """
    Upload the user's avatar.
    sharkservers.

    Returns
    -------
        dict: A dictionary with a success message.

    """  # noqa: D205
    await users_service.upload_avatar(
        user,
        avatar,
        request,
        upload_service,
        settings,
    )
    return {"msg": "Avatar was uploaded"}
connect_steam_profile(params: SteamAuthSchema, user: User = Security(get_current_active_user, scopes=['users:me']), steam_auth_service: SteamAuthService = Depends(get_steam_auth_service)) -> None async

Connect a Steam profile to the user's account.


params (SteamAuthSchema): The parameters for Steam authentication.
user (User, optional): The authenticated user. Defaults to the current active user.
steam_auth_service (SteamAuthService, optional): The Steam authentication service. Defaults to the injected service.

None: Nothing.
Source code in sharkservers/users/views/me.py
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
@router.post("/connect/steam")
async def connect_steam_profile(
    params: SteamAuthSchema,
    user: User = Security(get_current_active_user, scopes=["users:me"]),
    steam_auth_service: SteamAuthService = Depends(get_steam_auth_service),
) -> None:
    """
    Connect a Steam profile to the user's account.

    Args:
    ----
        params (SteamAuthSchema): The parameters for Steam authentication.
        user (User, optional): The authenticated user. Defaults to the current active user.
        steam_auth_service (SteamAuthService, optional): The Steam authentication service. Defaults to the injected service.

    Returns:
    -------
        None: Nothing.
    """
    return await steam_auth_service.authenticate(user, params)
get_user_sessions(user: User = Security(get_current_active_user, scopes=['users:me'])) -> list[UserSessionOut] async

Retrieve the sessions of the current user.


user (User): The current user.

list[UserSessionOut]: A list of user sessions.
Source code in sharkservers/users/views/me.py
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
@router.get("/sessions")
async def get_user_sessions(
    user: User = Security(get_current_active_user, scopes=["users:me"]),
) -> list[UserSessionOut]:
    """
    Retrieve the sessions of the current user.

    Args:
    ----
        user (User): The current user.

    Returns:
    -------
        list[UserSessionOut]: A list of user sessions.
    """
    return user.sessions
delete_user_session(user_session: UserSession = Depends(get_valid_user_session)) -> UserSessionOut async

Delete the user session.


user_session (UserSession): The user session to be deleted.

UserSession: The deleted user session.
Source code in sharkservers/users/views/me.py
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
@router.delete("/sessions/{session_id}")
async def delete_user_session(
    user_session: UserSession = Depends(get_valid_user_session),
) -> UserSessionOut:
    """
    Delete the user session.

    Args:
    ----
        user_session (UserSession): The user session to be deleted.

    Returns:
    -------
        UserSession: The deleted user session.
    """
    await user_session.delete()
    return user_session

users

Module contains the API endpoints related to users.

Endpoints: - GET /users: Retrieves a paginated list of users based on the provided parameters. - GET /users/staff: Retrieves a paginated list of staff users. - GET /users/online: Retrieves a paginated list of users who were last online. - GET /users/{user_id}: Retrieves a specific user based on the provided user ID. - GET /users/{user_id}/posts: Retrieves a paginated list of posts made by a specific user. - GET /users/{user_id}/threads: Retrieves a paginated list of threads created by a specific user.

get_users(params: Params = Depends(), queries: UserQuery = Depends(), users_service: UserService = Depends(get_users_service)) -> Page[UserOut] async

Retrieve a list of users based on the provided parameters and queries.


params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
queries (UserQuery, optional): The queries for filtering and ordering. Defaults to Depends().
users_service (UserService, optional): The service for retrieving user data. Defaults to Depends(get_users_service).

Page[UserOut]: The paginated list of users.
Source code in sharkservers/users/views/users.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@router.get("")
async def get_users(
    params: Params = Depends(),
    queries: UserQuery = Depends(),
    users_service: UserService = Depends(get_users_service),
) -> Page[UserOut]:
    """
    Retrieve a list of users based on the provided parameters and queries.

    Args:
    ----
        params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
        queries (UserQuery, optional): The queries for filtering and ordering. Defaults to Depends().
        users_service (UserService, optional): The service for retrieving user data. Defaults to Depends(get_users_service).

    Returns:
    -------
        Page[UserOut]: The paginated list of users.

    """
    kwargs = {}
    if queries.username:
        kwargs["username__contains"] = queries.username
    return await users_service.get_all(
        params=params,
        related=["display_role", "player", "player__steamrep_profile"],
        order_by=queries.order_by,
        **kwargs,
    )
get_staff_users(params: Params = Depends(), roles_service: RoleService = Depends(get_roles_service)) -> Page[StaffRolesSchema] async

Retrieve staff users based on the provided parameters.


params (Params): The parameters for filtering and pagination.
roles_service (RoleService): The service for retrieving staff roles.

Page[StaffRolesSchema]: A paginated list of staff roles.
Source code in sharkservers/users/views/users.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@router.get("/staff")
async def get_staff_users(
    params: Params = Depends(),
    roles_service: RoleService = Depends(get_roles_service),
) -> Page[StaffRolesSchema]:
    """
    Retrieve staff users based on the provided parameters.

    Args:
    ----
        params (Params): The parameters for filtering and pagination.
        roles_service (RoleService): The service for retrieving staff roles.

    Returns:
    -------
        Page[StaffRolesSchema]: A paginated list of staff roles.

    """
    return await roles_service.get_staff_roles(params)
get_last_online_users(params: Params = Depends(), users_service: UserService = Depends(get_users_service)) -> Page[UserOut] async

Retrieve the last online users based on the provided parameters.


params (Params): The parameters for filtering the users.
users_service (UserService): The service for retrieving user data.

Page[UserOut]: A paginated list of UserOut objects representing the last online users.
Source code in sharkservers/users/views/users.py
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@router.get("/online")
async def get_last_online_users(
    params: Params = Depends(),
    users_service: UserService = Depends(get_users_service),
) -> Page[UserOut]:
    """
    Retrieve the last online users based on the provided parameters.

    Args:
    ----
        params (Params): The parameters for filtering the users.
        users_service (UserService): The service for retrieving user data.

    Returns:
    -------
        Page[UserOut]: A paginated list of UserOut objects representing the last online users.
    """
    return await users_service.get_last_online_users(params=params)
get_user(user: User = Depends(get_valid_user)) -> UserOut async

Retrieve the authenticated user.


user (User): The authenticated user.

UserOut: The user object with restricted information.
Source code in sharkservers/users/views/users.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
@router.get("/{user_id}", response_model=UserOut)
async def get_user(user: User = Depends(get_valid_user)) -> UserOut:
    """
    Retrieve the authenticated user.

    Args:
    ----
        user (User): The authenticated user.

    Returns:
    -------
        UserOut: The user object with restricted information.

    """
    return user
get_user_posts(params: Params = Depends(), queries: OrderQuery = Depends(), user: User = Depends(get_valid_user), posts_service: PostService = Depends(get_posts_service)) -> Page[PostOut] async

Retrieve all posts authored by a specific user.


params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
queries (OrderQuery, optional): The query parameters for ordering. Defaults to Depends().
user (User, optional): The authenticated user. Defaults to Depends(get_valid_user).
posts_service (PostService, optional): The service for retrieving posts. Defaults to Depends(get_posts_service).

Page[PostOut]: A paginated list of PostOut objects.
Source code in sharkservers/users/views/users.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@router.get("/{user_id}/posts")
async def get_user_posts(
    params: Params = Depends(),
    queries: OrderQuery = Depends(),
    user: User = Depends(get_valid_user),
    posts_service: PostService = Depends(get_posts_service),
) -> Page[PostOut]:
    """
    Retrieve all posts authored by a specific user.

    Args:
    ----
        params (Params, optional): The parameters for pagination and filtering. Defaults to Depends().
        queries (OrderQuery, optional): The query parameters for ordering. Defaults to Depends().
        user (User, optional): The authenticated user. Defaults to Depends(get_valid_user).
        posts_service (PostService, optional): The service for retrieving posts. Defaults to Depends(get_posts_service).

    Returns:
    -------
        Page[PostOut]: A paginated list of PostOut objects.

    """
    return await posts_service.get_all(
        params=params,
        author__id=user.id,
        order_by=queries.order_by,
    )
get_user_threads(params: Params = Depends(), queries: OrderQuery = Depends(), user: User = Depends(get_valid_user), threads_service: ThreadService = Depends(get_threads_service)) -> Page[ThreadOut] async

Retrieves all threads belonging to a specific user.


params (Params): The parameters for pagination and filtering.
queries (OrderQuery): The query parameters for ordering.
user (User): The authenticated user.
threads_service (ThreadService): The service for managing threads.

Page[ThreadOut]: A paginated list of threads belonging to the user.
Source code in sharkservers/users/views/users.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
@router.get("/{user_id}/threads")
async def get_user_threads(
    params: Params = Depends(),
    queries: OrderQuery = Depends(),
    user: User = Depends(get_valid_user),
    threads_service: ThreadService = Depends(get_threads_service),
) -> Page[ThreadOut]:
    """
    Retrieves all threads belonging to a specific user.

    Args:
    ----
        params (Params): The parameters for pagination and filtering.
        queries (OrderQuery): The query parameters for ordering.
        user (User): The authenticated user.
        threads_service (ThreadService): The service for managing threads.

    Returns:
    -------
        Page[ThreadOut]: A paginated list of threads belonging to the user.
    """  # noqa: D401
    return await threads_service.get_all(
        params=params,
        author__id=user.id,
        order_by=queries.order_by,
    )

utils

Utility functions and context managers used in the Sharkservers API.

Functions: - custom_generate_unique_id: Generates a unique ID based on the route's tags and name. - init_limiter: Initializes the rate limiter for the FastAPI application. - close_limiter: Closes the rate limiter for the FastAPI application.

- connect_db: Connects to the database and initializes the Redis connection and rate limiter.

  • disconnect_db: Disconnects from the database and closes the Redis connection.
  • connect_broadcast: Connects to the broadcast service.
  • disconnect_broadcast: Disconnects from the broadcast service.

Context Managers: - app_lifespan: Manages the lifespan of the FastAPI application.

custom_generate_unique_id(route: APIRouter) -> str

Generate a unique ID based on the route's tags and name.

Args:

route (APIRouter): The route for which to generate the unique ID.


str: The generated unique ID.
Source code in sharkservers/utils.py
33
34
35
36
37
38
39
40
41
42
43
44
45
def custom_generate_unique_id(route: APIRouter) -> str:
    """
    Generate a unique ID based on the route's tags and name.

    Args:
    ----
    route (APIRouter): The route for which to generate the unique ID.

    Returns:
    -------
        str: The generated unique ID.
    """
    return f"{route.tags[0]}-{route.name}"

init_limiter(_app: FastAPI) -> FastAPI async

Initialize the rate limiter for the FastAPI application.


_app (FastAPI): The FastAPI application.

FastAPI: The updated FastAPI application.
Source code in sharkservers/utils.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
async def init_limiter(_app: FastAPI) -> FastAPI:
    """
    Initialize the rate limiter for the FastAPI application.

    Args:
    ----
        _app (FastAPI): The FastAPI application.

    Returns:
    -------
        FastAPI: The updated FastAPI application.
    """
    await FastAPILimiter.init(_app.state.redis)
    return _app

close_limiter(_app: FastAPI) -> FastAPI async

Close the rate limiter for the FastAPI application.


_app (FastAPI): The FastAPI application.

FastAPI: The updated FastAPI application.
Source code in sharkservers/utils.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
async def close_limiter(_app: FastAPI) -> FastAPI:
    """
    Close the rate limiter for the FastAPI application.

    Args:
    ----
        _app (FastAPI): The FastAPI application.

    Returns:
    -------
        FastAPI: The updated FastAPI application.
    """
    await FastAPILimiter.close()
    return _app

connect_db(_app: FastAPI) -> FastAPI async

Connect to the database and initializes the Redis connection and rate limiter.


_app (FastAPI): The FastAPI application.

FastAPI: The updated FastAPI application.
Source code in sharkservers/utils.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
async def connect_db(_app: FastAPI) -> FastAPI:
    """
    Connect to the database and initializes the Redis connection and rate limiter.

    Args:
    ----
        _app (FastAPI): The FastAPI application.

    Returns:
    -------
        FastAPI: The updated FastAPI application.
    """
    _app.state.database = database
    database_ = _app.state.database
    if not database_.is_connected:
        await database.connect()
    _app.state.redis = await create_redis_pool()
    await FastAPILimiter.init(_app.state.redis)
    return _app

disconnect_db(_app: FastAPI) -> None async

Disconnect from the database and closes the Redis connection.


_app (FastAPI): The FastAPI application.
Source code in sharkservers/utils.py
101
102
103
104
105
106
107
108
109
110
111
112
async def disconnect_db(_app: FastAPI) -> None:
    """
    Disconnect from the database and closes the Redis connection.

    Args:
    ----
        _app (FastAPI): The FastAPI application.
    """
    database_ = _app.state.database
    if database_.is_connected:
        await database_.disconnect()
    await _app.state.redis.close()

connect_broadcast(_app: FastAPI) -> FastAPI async

Connect to the broadcast service.


_app (FastAPI): The FastAPI application.

FastAPI: The updated FastAPI application.
Source code in sharkservers/utils.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
async def connect_broadcast(_app: FastAPI) -> FastAPI:
    """
    Connect to the broadcast service.

    Args:
    ----
        _app (FastAPI): The FastAPI application.

    Returns:
    -------
        FastAPI: The updated FastAPI application.
    """
    _app.state.broadcast = broadcast
    await broadcast.connect()
    return _app

disconnect_broadcast(_app: FastAPI) -> None async

Disconnect from the broadcast service.


_app (FastAPI): The FastAPI application.
Source code in sharkservers/utils.py
132
133
134
135
136
137
138
139
140
141
async def disconnect_broadcast(_app: FastAPI) -> None:
    """
    Disconnect from the broadcast service.

    Args:
    ----
        _app (FastAPI): The FastAPI application.
    """
    broadcast_ = _app.state.broadcast
    await broadcast_.disconnect()

app_lifespan(_app: FastAPI) -> AsyncIterator[None] async

Manage the lifespan of the FastAPI application.


_app (FastAPI): The FastAPI application.

None
Source code in sharkservers/utils.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@asynccontextmanager
async def app_lifespan(_app: FastAPI) -> AsyncIterator[None]:
    """
    Manage the lifespan of the FastAPI application.

    Args:
    ----
        _app (FastAPI): The FastAPI application.

    Yields:
    ------
        None
    """
    await connect_db(_app)
    await connect_broadcast(_app)
    await init_limiter(_app)
    yield
    await disconnect_db(_app)
    await disconnect_broadcast(_app)
    await close_limiter(_app)

views

Module containing the API routes for the SharkServers application.

Includes routes for installation, generating OpenAPI documentation, and generating random data.

Routes: - /install: POST route for installing the application. - /generate-openapi: GET route for generating the OpenAPI documentation. - /generate-random-data: GET route for generating random data for testing purposes.

install(user_data: RegisterUserSchema, scopes_service: ScopeService = Depends(get_scopes_service), roles_service: RoleService = Depends(get_roles_service), auth_service: AuthService = Depends(get_auth_service), settings: Settings = Depends(get_settings)) -> dict async

Endpoint for installing the SharkServers application.

Args:
  • user_data: User data for the admin user.
  • scopes_service: Service for managing scopes.
  • roles_service: Service for managing roles.
  • auth_service: Service for authentication.
  • settings: Application settings.
Returns:
  • A dictionary with a "msg" key indicating the success of the installation.
Source code in sharkservers/views.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@router.post("/install")
async def install(  # noqa: D417
    user_data: RegisterUserSchema,
    scopes_service: ScopeService = Depends(get_scopes_service),
    roles_service: RoleService = Depends(get_roles_service),
    auth_service: AuthService = Depends(get_auth_service),
    settings: Settings = Depends(get_settings),
) -> dict:
    """
    Endpoint for installing the SharkServers application.

    Args:
    ----
    - user_data: User data for the admin user.
    - scopes_service: Service for managing scopes.
    - roles_service: Service for managing roles.
    - auth_service: Service for authentication.
    - settings: Application settings.

    Returns:
    -------
    - A dictionary with a "msg" key indicating the success of the installation.
    """
    await MainService.install(
        file_path=installed_file_path,
        admin_user_data=user_data,
        scopes_service=scopes_service,
        roles_service=roles_service,
        auth_service=auth_service,
        settings=settings,
    )
    return {"msg": "Successfully installed"}

generate_openapi() -> dict[str, str] async

Endpoint for generating the OpenAPI documentation.

Returns
  • A dictionary with a "msg" key indicating the success of the generation.
Source code in sharkservers/views.py
73
74
75
76
77
78
79
80
81
82
83
@router.get("/generate-openapi")
async def generate_openapi() -> dict[str, str]:
    """
    Endpoint for generating the OpenAPI documentation.

    Returns
    -------
    - A dictionary with a "msg" key indicating the success of the generation.
    """
    await MainService.generate_openapi_file()
    return {"msg": "Done"}

generate_random_data(auth_service: AuthService = Depends(get_auth_service), roles_service: RoleService = Depends(get_roles_service), categories_service: CategoryService = Depends(get_categories_service), threads_service: ThreadService = Depends(get_threads_service), posts_service: PostService = Depends(get_posts_service), servers_service: ServerService = Depends(get_servers_service)) -> dict[str, str] async

Endpoint for generating random data for testing purposes.

Args:
  • auth_service: Service for authentication.
  • roles_service: Service for managing roles.
  • categories_service: Service for managing categories.
  • threads_service: Service for managing threads.
  • posts_service: Service for managing posts.
  • servers_service: Service for managing servers.
Returns:
  • A dictionary with a "msg" key indicating the success of the generation.
Source code in sharkservers/views.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
@router.get("/generate-random-data")
async def generate_random_data(  # noqa: PLR0913, D417
    auth_service: AuthService = Depends(get_auth_service),
    roles_service: RoleService = Depends(get_roles_service),
    categories_service: CategoryService = Depends(get_categories_service),
    threads_service: ThreadService = Depends(get_threads_service),
    posts_service: PostService = Depends(get_posts_service),
    servers_service: ServerService = Depends(get_servers_service),
) -> dict[str, str]:
    """
    Endpoint for generating random data for testing purposes.

    Args:
    ----
    - auth_service: Service for authentication.
    - roles_service: Service for managing roles.
    - categories_service: Service for managing categories.
    - threads_service: Service for managing threads.
    - posts_service: Service for managing posts.
    - servers_service: Service for managing servers.

    Returns:
    -------
    - A dictionary with a "msg" key indicating the success of the generation.
    """
    dispatch(
        event_name="GENERATE_RANDOM_DATA",
        payload={
            "auth_service": auth_service,
            "roles_service": roles_service,
            "categories_service": categories_service,
            "threads_service": threads_service,
            "posts_service": posts_service,
            "servers_service": servers_service,
        },
    )
    return {"msg": "Done"}