mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-28 03:50:10 +00:00
Compare commits
5 Commits
ab0b796053
...
da2d680334
Author | SHA1 | Date | |
---|---|---|---|
|
da2d680334 | ||
4fcb58aefa | |||
8c2a35f755 | |||
a66c522b73 | |||
|
a7e05c5943 |
@ -45,14 +45,24 @@ func (app *appContext) checkInvites() {
|
|||||||
app.storage.SetInvitesKey(data.Code, data)
|
app.storage.SetInvitesKey(data.Code, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.IsReferral {
|
if data.IsReferral && (!data.UseReferralExpiry || data.ReferrerJellyfinID == "") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expiry := data.ValidTill
|
expiry := data.ValidTill
|
||||||
if !currentTime.After(expiry) {
|
if !currentTime.After(expiry) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
app.debug.Printf("Housekeeping: Deleting old invite %s", data.Code)
|
app.debug.Printf("Housekeeping: Deleting old invite %s", data.Code)
|
||||||
|
|
||||||
|
// Disable referrals for the user if UseReferralExpiry is enabled, so no new ones are made.
|
||||||
|
if data.IsReferral && data.UseReferralExpiry && data.ReferrerJellyfinID != "" {
|
||||||
|
user, ok := app.storage.GetEmailsKey(data.ReferrerJellyfinID)
|
||||||
|
if ok {
|
||||||
|
user.ReferralTemplateKey = ""
|
||||||
|
app.storage.SetEmailsKey(data.ReferrerJellyfinID, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
notify := data.Notify
|
notify := data.Notify
|
||||||
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
||||||
app.debug.Printf("%s: Expiry notification", data.Code)
|
app.debug.Printf("%s: Expiry notification", data.Code)
|
||||||
@ -136,6 +146,13 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
}
|
}
|
||||||
wait.Wait()
|
wait.Wait()
|
||||||
}
|
}
|
||||||
|
if inv.IsReferral && inv.ReferrerJellyfinID != "" && inv.UseReferralExpiry {
|
||||||
|
user, ok := app.storage.GetEmailsKey(inv.ReferrerJellyfinID)
|
||||||
|
if ok {
|
||||||
|
user.ReferralTemplateKey = ""
|
||||||
|
app.storage.SetEmailsKey(inv.ReferrerJellyfinID, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
match = false
|
match = false
|
||||||
app.storage.DeleteInvitesKey(code)
|
app.storage.DeleteInvitesKey(code)
|
||||||
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
@ -36,8 +36,8 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
|||||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
||||||
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
||||||
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpired").Enabled},
|
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpired").Enabled},
|
||||||
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.MustGetCustomContentKey("Login").Enabled},
|
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.MustGetCustomContentKey("UserLogin").Enabled},
|
||||||
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.MustGetCustomContentKey("Page").Enabled},
|
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.MustGetCustomContentKey("UserPage").Enabled},
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := gc.Query("filter")
|
filter := gc.Query("filter")
|
||||||
|
@ -130,15 +130,17 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param profile path string true "name of profile to enable referrals for."
|
// @Param profile path string true "name of profile to enable referrals for."
|
||||||
// @Param invite path string true "invite code to create referral template from."
|
// @Param invite path string true "invite code to create referral template from."
|
||||||
|
// @Param useExpiry path string true "with-expiry or none."
|
||||||
// @Success 200 {object} boolResponse
|
// @Success 200 {object} boolResponse
|
||||||
// @Failure 400 {object} stringResponse
|
// @Failure 400 {object} stringResponse
|
||||||
// @Failure 500 {object} stringResponse
|
// @Failure 500 {object} stringResponse
|
||||||
// @Router /profiles/referral/{profile}/{invite} [post]
|
// @Router /profiles/referral/{profile}/{invite}/{useExpiry} [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Profiles & Settings
|
// @tags Profiles & Settings
|
||||||
func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
|
func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
|
||||||
profileName := gc.Param("profile")
|
profileName := gc.Param("profile")
|
||||||
invCode := gc.Param("invite")
|
invCode := gc.Param("invite")
|
||||||
|
useExpiry := gc.Param("useExpiry") == "with-expiry"
|
||||||
inv, ok := app.storage.GetInvitesKey(invCode)
|
inv, ok := app.storage.GetInvitesKey(invCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
respond(400, "Invalid invite code", gc)
|
respond(400, "Invalid invite code", gc)
|
||||||
@ -154,9 +156,15 @@ func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
|
|||||||
|
|
||||||
// Generate new code for referral template
|
// Generate new code for referral template
|
||||||
inv.Code = GenerateInviteCode()
|
inv.Code = GenerateInviteCode()
|
||||||
|
expiryDelta := inv.ValidTill.Sub(inv.Created)
|
||||||
inv.Created = time.Now()
|
inv.Created = time.Now()
|
||||||
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
if useExpiry {
|
||||||
|
inv.ValidTill = inv.Created.Add(expiryDelta)
|
||||||
|
} else {
|
||||||
|
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
||||||
|
}
|
||||||
inv.IsReferral = true
|
inv.IsReferral = true
|
||||||
|
inv.UseReferralExpiry = useExpiry
|
||||||
// Since this is a template for multiple users, ReferrerJellyfinID is not set.
|
// Since this is a template for multiple users, ReferrerJellyfinID is not set.
|
||||||
// inv.ReferrerJellyfinID = ...
|
// inv.ReferrerJellyfinID = ...
|
||||||
|
|
||||||
|
@ -746,21 +746,37 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
|
|||||||
// Since this key is shared between users in a profile, we make a copy.
|
// Since this key is shared between users in a profile, we make a copy.
|
||||||
user, ok := app.storage.GetEmailsKey(gc.GetString("jfId"))
|
user, ok := app.storage.GetEmailsKey(gc.GetString("jfId"))
|
||||||
err = app.storage.db.Get(user.ReferralTemplateKey, &inv)
|
err = app.storage.db.Get(user.ReferralTemplateKey, &inv)
|
||||||
if !ok || err != nil {
|
if !ok || err != nil || user.ReferralTemplateKey == "" {
|
||||||
app.debug.Printf("Ignoring referral request, couldn't find template.")
|
app.debug.Printf("Ignoring referral request, couldn't find template.")
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inv.Code = GenerateInviteCode()
|
inv.Code = GenerateInviteCode()
|
||||||
|
expiryDelta := inv.ValidTill.Sub(inv.Created)
|
||||||
inv.Created = time.Now()
|
inv.Created = time.Now()
|
||||||
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
if inv.UseReferralExpiry {
|
||||||
|
inv.ValidTill = inv.Created.Add(expiryDelta)
|
||||||
|
} else {
|
||||||
|
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
||||||
|
}
|
||||||
inv.IsReferral = true
|
inv.IsReferral = true
|
||||||
inv.ReferrerJellyfinID = gc.GetString("jfId")
|
inv.ReferrerJellyfinID = gc.GetString("jfId")
|
||||||
app.storage.SetInvitesKey(inv.Code, inv)
|
app.storage.SetInvitesKey(inv.Code, inv)
|
||||||
} else if time.Now().After(inv.ValidTill) {
|
} else if time.Now().After(inv.ValidTill) {
|
||||||
// 3. We found an invite for us, but it's expired.
|
// 3. We found an invite for us, but it's expired.
|
||||||
// We delete it from storage, and put it back with a fresh code and expiry.
|
// We delete it from storage, and put it back with a fresh code and expiry.
|
||||||
|
// If UseReferralExpiry is enabled, we delete it and return nothing.
|
||||||
app.storage.DeleteInvitesKey(inv.Code)
|
app.storage.DeleteInvitesKey(inv.Code)
|
||||||
|
if inv.UseReferralExpiry {
|
||||||
|
user, ok := app.storage.GetEmailsKey(gc.GetString("jfId"))
|
||||||
|
if ok {
|
||||||
|
user.ReferralTemplateKey = ""
|
||||||
|
app.storage.SetEmailsKey(gc.GetString("jfId"), user)
|
||||||
|
}
|
||||||
|
app.debug.Printf("Ignoring referral request, expired.")
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
inv.Code = GenerateInviteCode()
|
inv.Code = GenerateInviteCode()
|
||||||
inv.Created = time.Now()
|
inv.Created = time.Now()
|
||||||
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
||||||
@ -771,5 +787,6 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
|
|||||||
RemainingUses: inv.RemainingUses,
|
RemainingUses: inv.RemainingUses,
|
||||||
NoLimit: inv.NoLimit,
|
NoLimit: inv.NoLimit,
|
||||||
Expiry: inv.ValidTill.Unix(),
|
Expiry: inv.ValidTill.Unix(),
|
||||||
|
UseExpiry: inv.UseReferralExpiry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
26
api-users.go
26
api-users.go
@ -367,6 +367,19 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
emailStore.ReferralTemplateKey = profile.ReferralTemplateKey
|
emailStore.ReferralTemplateKey = profile.ReferralTemplateKey
|
||||||
// Store here, just incase email are disabled (whether this is even possible, i don't know)
|
// Store here, just incase email are disabled (whether this is even possible, i don't know)
|
||||||
app.storage.SetEmailsKey(id, emailStore)
|
app.storage.SetEmailsKey(id, emailStore)
|
||||||
|
|
||||||
|
// If UseReferralExpiry is enabled, create the ref now so the clock starts ticking
|
||||||
|
refInv := Invite{}
|
||||||
|
err = app.storage.db.Get(profile.ReferralTemplateKey, &refInv)
|
||||||
|
if refInv.UseReferralExpiry {
|
||||||
|
refInv.Code = GenerateInviteCode()
|
||||||
|
expiryDelta := refInv.ValidTill.Sub(refInv.Created)
|
||||||
|
refInv.Created = time.Now()
|
||||||
|
refInv.ValidTill = refInv.Created.Add(expiryDelta)
|
||||||
|
refInv.IsReferral = true
|
||||||
|
refInv.ReferrerJellyfinID = id
|
||||||
|
app.storage.SetInvitesKey(refInv.Code, refInv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
// if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
||||||
@ -729,10 +742,11 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
|||||||
// @Param EnableDisableReferralDTO body EnableDisableReferralDTO true "List of users"
|
// @Param EnableDisableReferralDTO body EnableDisableReferralDTO true "List of users"
|
||||||
// @Param mode path string true "mode of template sourcing from 'invite' or 'profile'."
|
// @Param mode path string true "mode of template sourcing from 'invite' or 'profile'."
|
||||||
// @Param source path string true "invite code or profile name, depending on what mode is."
|
// @Param source path string true "invite code or profile name, depending on what mode is."
|
||||||
|
// @Param useExpiry path string true "with-expiry or none."
|
||||||
// @Success 200 {object} boolResponse
|
// @Success 200 {object} boolResponse
|
||||||
// @Failure 400 {object} boolResponse
|
// @Failure 400 {object} boolResponse
|
||||||
// @Failure 500 {object} boolResponse
|
// @Failure 500 {object} boolResponse
|
||||||
// @Router /users/referral/{mode}/{source} [post]
|
// @Router /users/referral/{mode}/{source}/{useExpiry} [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Users
|
// @tags Users
|
||||||
func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
||||||
@ -740,7 +754,7 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
|||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
mode := gc.Param("mode")
|
mode := gc.Param("mode")
|
||||||
source := gc.Param("source")
|
source := gc.Param("source")
|
||||||
|
useExpiry := gc.Param("useExpiry") == "with-expiry"
|
||||||
baseInv := Invite{}
|
baseInv := Invite{}
|
||||||
if mode == "profile" {
|
if mode == "profile" {
|
||||||
profile, ok := app.storage.GetProfileKey(source)
|
profile, ok := app.storage.GetProfileKey(source)
|
||||||
@ -768,10 +782,16 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
|||||||
// 2. Generate referral invite.
|
// 2. Generate referral invite.
|
||||||
inv := baseInv
|
inv := baseInv
|
||||||
inv.Code = GenerateInviteCode()
|
inv.Code = GenerateInviteCode()
|
||||||
|
expiryDelta := inv.ValidTill.Sub(inv.Created)
|
||||||
inv.Created = time.Now()
|
inv.Created = time.Now()
|
||||||
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
if useExpiry {
|
||||||
|
inv.ValidTill = inv.Created.Add(expiryDelta)
|
||||||
|
} else {
|
||||||
|
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
||||||
|
}
|
||||||
inv.IsReferral = true
|
inv.IsReferral = true
|
||||||
inv.ReferrerJellyfinID = u
|
inv.ReferrerJellyfinID = u
|
||||||
|
inv.UseReferralExpiry = useExpiry
|
||||||
app.storage.SetInvitesKey(inv.Code, inv)
|
app.storage.SetInvitesKey(inv.Code, inv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,11 @@
|
|||||||
<div class="select ~neutral @low mb-4 unfocused">
|
<div class="select ~neutral @low mb-4 unfocused">
|
||||||
<select id="enable-referrals-user-invites"></select>
|
<select id="enable-referrals-user-invites"></select>
|
||||||
</div>
|
</div>
|
||||||
|
<label class="switch mb-4">
|
||||||
|
<input type="checkbox" id="enable-referrals-user-expiry">
|
||||||
|
<span>{{ .strings.useInviteExpiry }}</span>
|
||||||
|
<span class="flex flex-row support mt-2">{{ .strings.useInviteExpiryNote }}</span>
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="submit" class="unfocused">
|
<input type="submit" class="unfocused">
|
||||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
|
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
|
||||||
@ -144,6 +149,11 @@
|
|||||||
<div class="select ~neutral @low mb-4 mt-2">
|
<div class="select ~neutral @low mb-4 mt-2">
|
||||||
<select id="enable-referrals-profile-invites"></select>
|
<select id="enable-referrals-profile-invites"></select>
|
||||||
</div>
|
</div>
|
||||||
|
<label class="switch mb-4">
|
||||||
|
<input type="checkbox" id="enable-referrals-profile-expiry">
|
||||||
|
<span>{{ .strings.useInviteExpiry }}</span>
|
||||||
|
<span class="flex flex-row support mt-2">{{ .strings.useInviteExpiryNote }}</span>
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="submit" class="unfocused">
|
<input type="submit" class="unfocused">
|
||||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
|
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
|
||||||
|
@ -155,7 +155,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="card @low dark:~d_neutral unfocused" id="card-referrals">
|
<div class="card @low dark:~d_neutral unfocused" id="card-referrals">
|
||||||
<span class="heading mb-2">{{ .strings.referrals }}</span>
|
<span class="heading mb-2">{{ .strings.referrals }}</span>
|
||||||
<aside class="aside ~neutral my-4 col">{{ .strings.referralsDescription }}</aside>
|
<aside class="aside ~neutral my-4 col user-referrals-description"></aside>
|
||||||
<div class="row flex-expand">
|
<div class="row flex-expand">
|
||||||
<div class="user-referrals-info"></div>
|
<div class="user-referrals-info"></div>
|
||||||
<div class="grid my-2">
|
<div class="grid my-2">
|
||||||
|
@ -76,6 +76,8 @@
|
|||||||
"disableReferrals": "Disable Referrals",
|
"disableReferrals": "Disable Referrals",
|
||||||
"enableReferralsDescription": "Give users a personal referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an existing invite.",
|
"enableReferralsDescription": "Give users a personal referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an existing invite.",
|
||||||
"enableReferralsProfileDescription": "Give users created with this profile a personal referral link similiar to an invite, to send to friends/family. Create an invite with the desired settings, then select it here. Each referral will then be based on this invite. You can delete the invite once complete.",
|
"enableReferralsProfileDescription": "Give users created with this profile a personal referral link similiar to an invite, to send to friends/family. Create an invite with the desired settings, then select it here. Each referral will then be based on this invite. You can delete the invite once complete.",
|
||||||
|
"useInviteExpiry": "Set expiry from profile/invite",
|
||||||
|
"useInviteExpiryNote": "By default, invites expire after 90 days but can be renewed by the user. Enable for the referral to be disabled after the time set.",
|
||||||
"applyHomescreenLayout": "Apply homescreen layout",
|
"applyHomescreenLayout": "Apply homescreen layout",
|
||||||
"sendDeleteNotificationEmail": "Send notification message",
|
"sendDeleteNotificationEmail": "Send notification message",
|
||||||
"sendDeleteNotifiationExample": "Your account has been deleted.",
|
"sendDeleteNotifiationExample": "Your account has been deleted.",
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.",
|
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.",
|
||||||
"changePassword": "Change Password",
|
"changePassword": "Change Password",
|
||||||
"referralsDescription": "Invite friends & family to Jellyfin with this link. Come back here for a new one if it expires.",
|
"referralsDescription": "Invite friends & family to Jellyfin with this link. Come back here for a new one if it expires.",
|
||||||
|
"referralsWithExpiryDescription": "Invite friends & family to Jellyfin with this link. The link will be disabled once it expires.",
|
||||||
"copyReferral": "Copy Link",
|
"copyReferral": "Copy Link",
|
||||||
"invitedBy": "You were invited by user {user}."
|
"invitedBy": "You were invited by user {user}."
|
||||||
},
|
},
|
||||||
|
@ -424,7 +424,8 @@ type GetMyReferralRespDTO struct {
|
|||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
RemainingUses int `json:"remaining_uses"`
|
RemainingUses int `json:"remaining_uses"`
|
||||||
NoLimit bool `json:"no_limit"`
|
NoLimit bool `json:"no_limit"`
|
||||||
Expiry int64 `json:"expiry"` // Come back after this time to get a new referral
|
Expiry int64 `json:"expiry"` // Come back after this time to get a new referral (if UseExpiry, a new one can't be made).
|
||||||
|
UseExpiry bool `json:"use_expiry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnableDisableReferralDTO struct {
|
type EnableDisableReferralDTO struct {
|
||||||
|
@ -229,9 +229,9 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
}
|
}
|
||||||
api.POST(p+"/matrix/login", app.MatrixLogin)
|
api.POST(p+"/matrix/login", app.MatrixLogin)
|
||||||
if app.config.Section("user_page").Key("referrals").MustBool(false) {
|
if app.config.Section("user_page").Key("referrals").MustBool(false) {
|
||||||
api.POST(p+"/users/referral/:mode/:source", app.EnableReferralForUsers)
|
api.POST(p+"/users/referral/:mode/:source/:useExpiry", app.EnableReferralForUsers)
|
||||||
api.DELETE(p+"/users/referral", app.DisableReferralForUsers)
|
api.DELETE(p+"/users/referral", app.DisableReferralForUsers)
|
||||||
api.POST(p+"/profiles/referral/:profile/:invite", app.EnableReferralForProfile)
|
api.POST(p+"/profiles/referral/:profile/:invite/:useExpiry", app.EnableReferralForProfile)
|
||||||
api.DELETE(p+"/profiles/referral/:profile", app.DisableReferralForProfile)
|
api.DELETE(p+"/profiles/referral/:profile", app.DisableReferralForProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
site/package-lock.json
generated
12
site/package-lock.json
generated
@ -4462,9 +4462,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -7828,9 +7828,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA=="
|
||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
18
storage.go
18
storage.go
@ -659,15 +659,15 @@ type Invite struct {
|
|||||||
UserMinutes int `json:"user-minutes,omitempty"`
|
UserMinutes int `json:"user-minutes,omitempty"`
|
||||||
SendTo string `json:"email"`
|
SendTo string `json:"email"`
|
||||||
// Used to be stored as formatted time, now as Unix.
|
// Used to be stored as formatted time, now as Unix.
|
||||||
UsedBy [][]string `json:"used-by"`
|
UsedBy [][]string `json:"used-by"`
|
||||||
Notify map[string]map[string]bool `json:"notify"`
|
Notify map[string]map[string]bool `json:"notify"`
|
||||||
Profile string `json:"profile"`
|
Profile string `json:"profile"`
|
||||||
Label string `json:"label,omitempty"`
|
Label string `json:"label,omitempty"`
|
||||||
UserLabel string `json:"user_label,omitempty" example:"Friend"` // Label to apply to users created w/ this invite.
|
UserLabel string `json:"user_label,omitempty" example:"Friend"` // Label to apply to users created w/ this invite.
|
||||||
Captchas map[string]Captcha // Map of Captcha IDs to images & answers
|
Captchas map[string]Captcha // Map of Captcha IDs to images & answers
|
||||||
IsReferral bool `json:"is_referral" badgerhold:"index"`
|
IsReferral bool `json:"is_referral" badgerhold:"index"`
|
||||||
ReferrerJellyfinID string `json:"referrer_id"`
|
ReferrerJellyfinID string `json:"referrer_id"`
|
||||||
ReferrerTemplateForProfile string
|
UseReferralExpiry bool `json:"use_referral_expiry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Captcha struct {
|
type Captcha struct {
|
||||||
|
@ -783,6 +783,7 @@ export class accountsList {
|
|||||||
private _userSelect = document.getElementById("modify-user-users") as HTMLSelectElement;
|
private _userSelect = document.getElementById("modify-user-users") as HTMLSelectElement;
|
||||||
private _referralsProfileSelect = document.getElementById("enable-referrals-user-profiles") as HTMLSelectElement;
|
private _referralsProfileSelect = document.getElementById("enable-referrals-user-profiles") as HTMLSelectElement;
|
||||||
private _referralsInviteSelect = document.getElementById("enable-referrals-user-invites") as HTMLSelectElement;
|
private _referralsInviteSelect = document.getElementById("enable-referrals-user-invites") as HTMLSelectElement;
|
||||||
|
private _referralsExpiry = document.getElementById("enable-referrals-user-expiry") as HTMLInputElement;
|
||||||
private _searchBox = document.getElementById("accounts-search") as HTMLInputElement;
|
private _searchBox = document.getElementById("accounts-search") as HTMLInputElement;
|
||||||
private _search: Search;
|
private _search: Search;
|
||||||
|
|
||||||
@ -1578,7 +1579,7 @@ export class accountsList {
|
|||||||
send["from"] = "invite";
|
send["from"] = "invite";
|
||||||
send["id"] = this._referralsInviteSelect.value;
|
send["id"] = this._referralsInviteSelect.value;
|
||||||
}
|
}
|
||||||
_post("/users/referral/" + send["from"] + "/" + (send["id"] ? send["id"] : send["profile"]), send, (req: XMLHttpRequest) => {
|
_post("/users/referral/" + send["from"] + "/" + (send["id"] ? send["id"] : send["profile"]) + "/" + (this._referralsExpiry.checked ? "with-expiry" : "none"), send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
toggleLoader(button);
|
toggleLoader(button);
|
||||||
if (req.status == 400) {
|
if (req.status == 400) {
|
||||||
@ -1593,6 +1594,7 @@ export class accountsList {
|
|||||||
};
|
};
|
||||||
this._enableReferralsProfile.checked = true;
|
this._enableReferralsProfile.checked = true;
|
||||||
this._enableReferralsInvite.checked = false;
|
this._enableReferralsInvite.checked = false;
|
||||||
|
this._referralsExpiry.checked = false;
|
||||||
window.modals.enableReferralsUser.show();
|
window.modals.enableReferralsUser.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,6 +225,7 @@ export class ProfileEditor {
|
|||||||
|
|
||||||
enableReferrals = (name: string) => {
|
enableReferrals = (name: string) => {
|
||||||
const referralsInviteSelect = document.getElementById("enable-referrals-profile-invites") as HTMLSelectElement;
|
const referralsInviteSelect = document.getElementById("enable-referrals-profile-invites") as HTMLSelectElement;
|
||||||
|
const referralsExpiry = document.getElementById("enable-referrals-profile-expiry") as HTMLInputElement;
|
||||||
_get("/invites", null, (req: XMLHttpRequest) => {
|
_get("/invites", null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState != 4 || req.status != 200) return;
|
if (req.readyState != 4 || req.status != 200) return;
|
||||||
|
|
||||||
@ -257,7 +258,7 @@ export class ProfileEditor {
|
|||||||
"invite": referralsInviteSelect.value
|
"invite": referralsInviteSelect.value
|
||||||
};
|
};
|
||||||
|
|
||||||
_post("/profiles/referral/" + send["profile"] + "/" + send["invite"], send, (req: XMLHttpRequest) => {
|
_post("/profiles/referral/" + send["profile"] + "/" + send["invite"] + "/" + (referralsExpiry.checked ? "with-expiry" : "none"), send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
toggleLoader(button);
|
toggleLoader(button);
|
||||||
if (req.status == 400) {
|
if (req.status == 400) {
|
||||||
@ -270,6 +271,7 @@ export class ProfileEditor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
referralsExpiry.checked = false;
|
||||||
window.modals.profiles.close();
|
window.modals.profiles.close();
|
||||||
window.modals.enableReferralsProfile.show();
|
window.modals.enableReferralsProfile.show();
|
||||||
};
|
};
|
||||||
|
18
ts/user.ts
18
ts/user.ts
@ -116,6 +116,7 @@ interface MyReferral {
|
|||||||
remaining_uses: number;
|
remaining_uses: number;
|
||||||
no_limit: boolean;
|
no_limit: boolean;
|
||||||
expiry: number;
|
expiry: number;
|
||||||
|
use_expiry: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContactDTO {
|
interface ContactDTO {
|
||||||
@ -252,6 +253,7 @@ class ReferralCard {
|
|||||||
private _url: string;
|
private _url: string;
|
||||||
private _expiry: Date;
|
private _expiry: Date;
|
||||||
private _expiryUnix: number;
|
private _expiryUnix: number;
|
||||||
|
private _useExpiry: boolean;
|
||||||
private _remainingUses: number;
|
private _remainingUses: number;
|
||||||
private _noLimit: boolean;
|
private _noLimit: boolean;
|
||||||
|
|
||||||
@ -259,6 +261,7 @@ class ReferralCard {
|
|||||||
private _infoArea: HTMLDivElement;
|
private _infoArea: HTMLDivElement;
|
||||||
private _remainingUsesEl: HTMLSpanElement;
|
private _remainingUsesEl: HTMLSpanElement;
|
||||||
private _expiryEl: HTMLSpanElement;
|
private _expiryEl: HTMLSpanElement;
|
||||||
|
private _descriptionEl: HTMLSpanElement;
|
||||||
|
|
||||||
get code(): string { return this._code; }
|
get code(): string { return this._code; }
|
||||||
set code(c: string) {
|
set code(c: string) {
|
||||||
@ -294,11 +297,22 @@ class ReferralCard {
|
|||||||
this._expiry = new Date(expiryUnix * 1000);
|
this._expiry = new Date(expiryUnix * 1000);
|
||||||
this._expiryEl.textContent = toDateString(this._expiry);
|
this._expiryEl.textContent = toDateString(this._expiry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get use_expiry(): boolean { return this._useExpiry; }
|
||||||
|
set use_expiry(v: boolean) {
|
||||||
|
this._useExpiry = v;
|
||||||
|
if (v) {
|
||||||
|
this._descriptionEl.textContent = window.lang.strings("referralsWithExpiryDescription");
|
||||||
|
} else {
|
||||||
|
this._descriptionEl.textContent = window.lang.strings("referralsDescription");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(card: HTMLElement) {
|
constructor(card: HTMLElement) {
|
||||||
this._card = card;
|
this._card = card;
|
||||||
this._button = this._card.querySelector(".user-referrals-button") as HTMLButtonElement;
|
this._button = this._card.querySelector(".user-referrals-button") as HTMLButtonElement;
|
||||||
this._infoArea = this._card.querySelector(".user-referrals-info") as HTMLDivElement;
|
this._infoArea = this._card.querySelector(".user-referrals-info") as HTMLDivElement;
|
||||||
|
this._descriptionEl = this._card.querySelector(".user-referrals-description") as HTMLSpanElement;
|
||||||
|
|
||||||
this._infoArea.innerHTML = `
|
this._infoArea.innerHTML = `
|
||||||
<div class="row my-3">
|
<div class="row my-3">
|
||||||
@ -344,6 +358,7 @@ class ReferralCard {
|
|||||||
this.no_limit = referral.no_limit;
|
this.no_limit = referral.no_limit;
|
||||||
this.expiry = referral.expiry;
|
this.expiry = referral.expiry;
|
||||||
this._card.classList.remove("unfocused");
|
this._card.classList.remove("unfocused");
|
||||||
|
this.use_expiry = referral.use_expiry;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,9 +635,8 @@ document.addEventListener("details-reload", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageCard.innerHTML = messageCard.innerHTML.replace(new RegExp("{username}", "g"), details.username);
|
|
||||||
|
|
||||||
if (typeof(messageCard) != "undefined" && messageCard != null) {
|
if (typeof(messageCard) != "undefined" && messageCard != null) {
|
||||||
|
messageCard.innerHTML = messageCard.innerHTML.replace(new RegExp("{username}", "g"), details.username);
|
||||||
setBestRowSpan(messageCard, false);
|
setBestRowSpan(messageCard, false);
|
||||||
// contactCard.querySelector(".content").classList.add("h-100");
|
// contactCard.querySelector(".content").classList.add("h-100");
|
||||||
} else if (!statusCard.classList.contains("unfocused")) {
|
} else if (!statusCard.classList.contains("unfocused")) {
|
||||||
|
Loading…
Reference in New Issue
Block a user