mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
Move api models to separate file
This commit is contained in:
parent
9fec714da7
commit
7628e5d71d
127
api.go
127
api.go
@ -24,16 +24,6 @@ func respond(code int, message string, gc *gin.Context) {
|
|||||||
gc.Abort()
|
gc.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
type stringResponse struct {
|
|
||||||
Response string `json:"response" example:"message"`
|
|
||||||
Error string `json:"error" example:"errorDescription"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type boolResponse struct {
|
|
||||||
Success bool `json:"success" example:"false"`
|
|
||||||
Error bool `json:"error" example:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func respondBool(code int, val bool, gc *gin.Context) {
|
func respondBool(code int, val bool, gc *gin.Context) {
|
||||||
resp := boolResponse{}
|
resp := boolResponse{}
|
||||||
if !val {
|
if !val {
|
||||||
@ -206,13 +196,6 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
|
|
||||||
// Routes from now on!
|
// Routes from now on!
|
||||||
|
|
||||||
type newUserDTO struct {
|
|
||||||
Username string `json:"username" example:"jeff" binding:"required"` // User's username
|
|
||||||
Password string `json:"password" example:"guest" binding:"required"` // User's password
|
|
||||||
Email string `json:"email" example:"jeff@jellyf.in"` // User's email address
|
|
||||||
Code string `json:"code" example:"abc0933jncjkcjj"` // Invite code (required on /newUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Creates a new Jellyfin user without an invite.
|
// @Summary Creates a new Jellyfin user without an invite.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param newUserDTO body newUserDTO true "New user request object"
|
// @Param newUserDTO body newUserDTO true "New user request object"
|
||||||
@ -389,12 +372,6 @@ func (app *appContext) NewUser(gc *gin.Context) {
|
|||||||
gc.JSON(code, validation)
|
gc.JSON(code, validation)
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteUserDTO struct {
|
|
||||||
Users []string `json:"users" binding:"required"` // List of usernames to delete
|
|
||||||
Notify bool `json:"notify"` // Whether to notify users of deletion
|
|
||||||
Reason string `json:"reason"` // Account deletion reason (for notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Delete a list of users, optionally notifying them why.
|
// @Summary Delete a list of users, optionally notifying them why.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param deleteUserDTO body deleteUserDTO true "User deletion request object"
|
// @Param deleteUserDTO body deleteUserDTO true "User deletion request object"
|
||||||
@ -443,17 +420,6 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type generateInviteDTO struct {
|
|
||||||
Days int `json:"days" example:"1"` // Number of days
|
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours
|
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes
|
|
||||||
Email string `json:"email" example:"jeff@jellyf.in"` // Send invite to this address
|
|
||||||
MultipleUses bool `json:"multiple-uses" example:"true"` // Allow multiple uses
|
|
||||||
NoLimit bool `json:"no-limit" example:"false"` // No invite use limit
|
|
||||||
RemainingUses int `json:"remaining-uses" example:"5"` // Remaining invite uses
|
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Name of profile to apply on this invite
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Create a new invite.
|
// @Summary Create a new invite.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param generateInviteDTO body generateInviteDTO true "New invite request object"
|
// @Param generateInviteDTO body generateInviteDTO true "New invite request object"
|
||||||
@ -516,11 +482,6 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inviteProfileDTO struct {
|
|
||||||
Invite string `json:"invite" example:"slakdaslkdl2342"` // Invite to apply to
|
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Profile to use
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Set profile for an invite
|
// @Summary Set profile for an invite
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param inviteProfileDTO body inviteProfileDTO true "Invite profile object"
|
// @Param inviteProfileDTO body inviteProfileDTO true "Invite profile object"
|
||||||
@ -546,17 +507,6 @@ func (app *appContext) SetProfile(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type profileDTO struct {
|
|
||||||
Admin bool `json:"admin" example:"false"` // Whether profile has admin rights or not
|
|
||||||
LibraryAccess string `json:"libraries" example:"all"` // Number of libraries profile has access to
|
|
||||||
FromUser string `json:"fromUser" example:"jeff"` // The user the profile is based on
|
|
||||||
}
|
|
||||||
|
|
||||||
type getProfilesDTO struct {
|
|
||||||
Profiles map[string]profileDTO `json:"profiles"`
|
|
||||||
DefaultProfile string `json:"default_profile"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Get a list of profiles
|
// @Summary Get a list of profiles
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} getProfilesDTO
|
// @Success 200 {object} getProfilesDTO
|
||||||
@ -580,10 +530,6 @@ func (app *appContext) GetProfiles(gc *gin.Context) {
|
|||||||
gc.JSON(200, out)
|
gc.JSON(200, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
type profileChangeDTO struct {
|
|
||||||
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Set the default profile to use.
|
// @Summary Set the default profile to use.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param profileChangeDTO body profileChangeDTO true "Default profile object"
|
// @Param profileChangeDTO body profileChangeDTO true "Default profile object"
|
||||||
@ -613,12 +559,6 @@ func (app *appContext) SetDefaultProfile(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type newProfileDTO struct {
|
|
||||||
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
|
||||||
ID string `json:"id" example:"kasdjlaskjd342342" binding:"required"` // ID of user to source settings from
|
|
||||||
Homescreen bool `json:"homescreen" example:"true"` // Whether to store homescreen layout or not
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Create a profile based on a Jellyfin user's settings.
|
// @Summary Create a profile based on a Jellyfin user's settings.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param newProfileDTO body newProfileDTO true "New profile object"
|
// @Param newProfileDTO body newProfileDTO true "New profile object"
|
||||||
@ -678,26 +618,6 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inviteDTO struct {
|
|
||||||
Code string `json:"code" example:"sajdlj23423j23"` // Invite code
|
|
||||||
Days int `json:"days" example:"1"` // Number of days till expiry
|
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours till expiry
|
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry
|
|
||||||
Created string `json:"created" example:"01/01/20 12:00"` // Date of creation
|
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
|
||||||
UsedBy [][]string `json:"used-by,omitempty"` // Users who have used this invite
|
|
||||||
NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times
|
|
||||||
RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable)
|
|
||||||
Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable)
|
|
||||||
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
|
||||||
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
|
||||||
}
|
|
||||||
|
|
||||||
type getInvitesDTO struct {
|
|
||||||
Profiles []string `json:"profiles"` // List of profiles (name only)
|
|
||||||
Invites []inviteDTO `json:"invites"` // List of invites
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Get invites.
|
// @Summary Get invites.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} getInvitesDTO
|
// @Success 200 {object} getInvitesDTO
|
||||||
@ -772,14 +692,6 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fake DTO, if i actually used this the code would be a lot longer
|
|
||||||
type setNotifyValues map[string]struct {
|
|
||||||
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
|
||||||
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
|
||||||
}
|
|
||||||
|
|
||||||
type setNotifyDTO map[string]setNotifyValues
|
|
||||||
|
|
||||||
// @Summary Set notification preferences for an invite.
|
// @Summary Set notification preferences for an invite.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param setNotifyDTO body setNotifyDTO true "Map of invite codes to notification settings objects"
|
// @Param setNotifyDTO body setNotifyDTO true "Map of invite codes to notification settings objects"
|
||||||
@ -843,10 +755,6 @@ func (app *appContext) SetNotify(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteInviteDTO struct {
|
|
||||||
Code string `json:"code" example:"skjadajd43234s"` // Code of invite to delete
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Delete an invite.
|
// @Summary Delete an invite.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param deleteInviteDTO body deleteInviteDTO true "Delete invite object"
|
// @Param deleteInviteDTO body deleteInviteDTO true "Delete invite object"
|
||||||
@ -884,18 +792,6 @@ func parseDt(date string) time.Time {
|
|||||||
return parsed.Parsed
|
return parsed.Parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
type respUser struct {
|
|
||||||
ID string `json:"id" example:"fdgsdfg45534fa"` // userID of user
|
|
||||||
Name string `json:"name" example:"jeff"` // Username of user
|
|
||||||
Email string `json:"email,omitempty" example:"jeff@jellyf.in"` // Email address of user (if available)
|
|
||||||
LastActive string `json:"last_active"` // Time of last activity on Jellyfin
|
|
||||||
Admin bool `json:"admin" example:"false"` // Whether or not the user is Administrator
|
|
||||||
}
|
|
||||||
|
|
||||||
type getUsersDTO struct {
|
|
||||||
UserList []respUser `json:"users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Get a list of Jellyfin users.
|
// @Summary Get a list of Jellyfin users.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} getUsersDTO
|
// @Success 200 {object} getUsersDTO
|
||||||
@ -933,15 +829,6 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
|||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ombiUser struct {
|
|
||||||
Name string `json:"name,omitempty" example:"jeff"` // Name of Ombi user
|
|
||||||
ID string `json:"id" example:"djgkjdg7dkjfsj8"` // userID of Ombi user
|
|
||||||
}
|
|
||||||
|
|
||||||
type ombiUsersDTO struct {
|
|
||||||
Users []ombiUser `json:"users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary Get a list of Ombi users.
|
// @Summary Get a list of Ombi users.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} ombiUsersDTO
|
// @Success 200 {object} ombiUsersDTO
|
||||||
@ -991,8 +878,6 @@ func (app *appContext) SetOmbiDefaults(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
type modifyEmailsDTO map[string]string
|
|
||||||
|
|
||||||
// @Summary Modify user's email addresses.
|
// @Summary Modify user's email addresses.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param modifyEmailsDTO body modifyEmailsDTO true "Map of userIDs to email addresses"
|
// @Param modifyEmailsDTO body modifyEmailsDTO true "Map of userIDs to email addresses"
|
||||||
@ -1059,16 +944,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
|||||||
gc.JSON(200, map[string]bool{"success": true})
|
gc.JSON(200, map[string]bool{"success": true})
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
type userSettingsDTO struct {
|
|
||||||
From string `json:"from"` // Whether to apply from "user" or "profile"
|
|
||||||
Profile string `json:"profile"` // Name of profile (if from = "profile")
|
|
||||||
ApplyTo []string `json:"apply_to"` // Users to apply settings to
|
|
||||||
ID string `json:"id"` // ID of user (if from = "user")
|
|
||||||
Homescreen bool `json:"homescreen"` // Whether to apply homescreen layout or not
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorListDTO map[string]map[string]string
|
|
||||||
|
|
||||||
// @Summary Apply settings to a list of users, either from a profile or from another user.
|
// @Summary Apply settings to a list of users, either from a profile or from another user.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param userSettingsDTO body userSettingsDTO true "Parameters for applying settings"
|
// @Param userSettingsDTO body userSettingsDTO true "Parameters for applying settings"
|
||||||
@ -1194,8 +1069,6 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
|||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type configDTO map[string]interface{}
|
|
||||||
|
|
||||||
// @Summary Modify app config.
|
// @Summary Modify app config.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param appConfig body configDTO true "Config split into sections as in config.ini, all values as strings."
|
// @Param appConfig body configDTO true "Config split into sections as in config.ini, all values as strings."
|
||||||
|
129
models.go
Normal file
129
models.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type stringResponse struct {
|
||||||
|
Response string `json:"response" example:"message"`
|
||||||
|
Error string `json:"error" example:"errorDescription"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type boolResponse struct {
|
||||||
|
Success bool `json:"success" example:"false"`
|
||||||
|
Error bool `json:"error" example:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type newUserDTO struct {
|
||||||
|
Username string `json:"username" example:"jeff" binding:"required"` // User's username
|
||||||
|
Password string `json:"password" example:"guest" binding:"required"` // User's password
|
||||||
|
Email string `json:"email" example:"jeff@jellyf.in"` // User's email address
|
||||||
|
Code string `json:"code" example:"abc0933jncjkcjj"` // Invite code (required on /newUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
type deleteUserDTO struct {
|
||||||
|
Users []string `json:"users" binding:"required"` // List of usernames to delete
|
||||||
|
Notify bool `json:"notify"` // Whether to notify users of deletion
|
||||||
|
Reason string `json:"reason"` // Account deletion reason (for notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
type generateInviteDTO struct {
|
||||||
|
Days int `json:"days" example:"1"` // Number of days
|
||||||
|
Hours int `json:"hours" example:"2"` // Number of hours
|
||||||
|
Minutes int `json:"minutes" example:"3"` // Number of minutes
|
||||||
|
Email string `json:"email" example:"jeff@jellyf.in"` // Send invite to this address
|
||||||
|
MultipleUses bool `json:"multiple-uses" example:"true"` // Allow multiple uses
|
||||||
|
NoLimit bool `json:"no-limit" example:"false"` // No invite use limit
|
||||||
|
RemainingUses int `json:"remaining-uses" example:"5"` // Remaining invite uses
|
||||||
|
Profile string `json:"profile" example:"DefaultProfile"` // Name of profile to apply on this invite
|
||||||
|
}
|
||||||
|
|
||||||
|
type inviteProfileDTO struct {
|
||||||
|
Invite string `json:"invite" example:"slakdaslkdl2342"` // Invite to apply to
|
||||||
|
Profile string `json:"profile" example:"DefaultProfile"` // Profile to use
|
||||||
|
}
|
||||||
|
|
||||||
|
type profileDTO struct {
|
||||||
|
Admin bool `json:"admin" example:"false"` // Whether profile has admin rights or not
|
||||||
|
LibraryAccess string `json:"libraries" example:"all"` // Number of libraries profile has access to
|
||||||
|
FromUser string `json:"fromUser" example:"jeff"` // The user the profile is based on
|
||||||
|
}
|
||||||
|
|
||||||
|
type getProfilesDTO struct {
|
||||||
|
Profiles map[string]profileDTO `json:"profiles"`
|
||||||
|
DefaultProfile string `json:"default_profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type profileChangeDTO struct {
|
||||||
|
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
||||||
|
}
|
||||||
|
|
||||||
|
type newProfileDTO struct {
|
||||||
|
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
||||||
|
ID string `json:"id" example:"kasdjlaskjd342342" binding:"required"` // ID of user to source settings from
|
||||||
|
Homescreen bool `json:"homescreen" example:"true"` // Whether to store homescreen layout or not
|
||||||
|
}
|
||||||
|
|
||||||
|
type inviteDTO struct {
|
||||||
|
Code string `json:"code" example:"sajdlj23423j23"` // Invite code
|
||||||
|
Days int `json:"days" example:"1"` // Number of days till expiry
|
||||||
|
Hours int `json:"hours" example:"2"` // Number of hours till expiry
|
||||||
|
Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry
|
||||||
|
Created string `json:"created" example:"01/01/20 12:00"` // Date of creation
|
||||||
|
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
||||||
|
UsedBy [][]string `json:"used-by,omitempty"` // Users who have used this invite
|
||||||
|
NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times
|
||||||
|
RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable)
|
||||||
|
Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable)
|
||||||
|
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
||||||
|
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
||||||
|
}
|
||||||
|
|
||||||
|
type getInvitesDTO struct {
|
||||||
|
Profiles []string `json:"profiles"` // List of profiles (name only)
|
||||||
|
Invites []inviteDTO `json:"invites"` // List of invites
|
||||||
|
}
|
||||||
|
|
||||||
|
// fake DTO, if i actually used this the code would be a lot longer
|
||||||
|
type setNotifyValues map[string]struct {
|
||||||
|
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
||||||
|
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
||||||
|
}
|
||||||
|
|
||||||
|
type setNotifyDTO map[string]setNotifyValues
|
||||||
|
|
||||||
|
type deleteInviteDTO struct {
|
||||||
|
Code string `json:"code" example:"skjadajd43234s"` // Code of invite to delete
|
||||||
|
}
|
||||||
|
|
||||||
|
type respUser struct {
|
||||||
|
ID string `json:"id" example:"fdgsdfg45534fa"` // userID of user
|
||||||
|
Name string `json:"name" example:"jeff"` // Username of user
|
||||||
|
Email string `json:"email,omitempty" example:"jeff@jellyf.in"` // Email address of user (if available)
|
||||||
|
LastActive string `json:"last_active"` // Time of last activity on Jellyfin
|
||||||
|
Admin bool `json:"admin" example:"false"` // Whether or not the user is Administrator
|
||||||
|
}
|
||||||
|
|
||||||
|
type getUsersDTO struct {
|
||||||
|
UserList []respUser `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ombiUser struct {
|
||||||
|
Name string `json:"name,omitempty" example:"jeff"` // Name of Ombi user
|
||||||
|
ID string `json:"id" example:"djgkjdg7dkjfsj8"` // userID of Ombi user
|
||||||
|
}
|
||||||
|
|
||||||
|
type ombiUsersDTO struct {
|
||||||
|
Users []ombiUser `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type modifyEmailsDTO map[string]string
|
||||||
|
|
||||||
|
type userSettingsDTO struct {
|
||||||
|
From string `json:"from"` // Whether to apply from "user" or "profile"
|
||||||
|
Profile string `json:"profile"` // Name of profile (if from = "profile")
|
||||||
|
ApplyTo []string `json:"apply_to"` // Users to apply settings to
|
||||||
|
ID string `json:"id"` // ID of user (if from = "user")
|
||||||
|
Homescreen bool `json:"homescreen"` // Whether to apply homescreen layout or not
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorListDTO map[string]map[string]string
|
||||||
|
|
||||||
|
type configDTO map[string]interface{}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user