diff --git a/api-userpage.go b/api-userpage.go index 367448a..2eaf22c 100644 --- a/api-userpage.go +++ b/api-userpage.go @@ -643,7 +643,7 @@ func (app *appContext) GetMyReferral(gc *gin.Context) { // If one exists, that means its just for us and so we // can use it directly. inv := Invite{} - err := app.storage.db.Find(&inv, badgerhold.Where("ReferrerJellyfinID").Eq(gc.GetString("jfId"))) + err := app.storage.db.FindOne(&inv, badgerhold.Where("ReferrerJellyfinID").Eq(gc.GetString("jfId"))) if err != nil { // 2. Look for a template matching the key found in the user storage // Since this key is shared between users in a profile, we make a copy. @@ -664,6 +664,7 @@ func (app *appContext) GetMyReferral(gc *gin.Context) { inv.Created = time.Now() inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour) inv.IsReferral = true + inv.ReferrerJellyfinID = gc.GetString("jfId") app.storage.SetInvitesKey(inv.Code, inv) } else if time.Now().After(inv.ValidTill) { // 3. We found an invite for us, but it's expired. diff --git a/api-users.go b/api-users.go index ce5441a..c3d38f3 100644 --- a/api-users.go +++ b/api-users.go @@ -11,6 +11,7 @@ import ( "github.com/golang-jwt/jwt" "github.com/hrfee/mediabrowser" "github.com/lithammer/shortuuid/v3" + "github.com/timshannon/badgerhold/v4" ) // @Summary Creates a new Jellyfin user without an invite. @@ -668,6 +669,10 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) { } } for _, u := range req.Users { + // 1. Wipe out any existing referral codes. + app.storage.db.DeleteMatching(Invite{}, badgerhold.Where("ReferrerJellyfinID").Eq(u)) + + // 2. Generate referral invite. inv := baseInv inv.Code = shortuuid.New() // make sure code doesn't begin with number @@ -888,13 +893,15 @@ func (app *appContext) GetUsers(gc *gin.Context) { } adminOnly := app.config.Section("ui").Key("admin_only").MustBool(true) allowAll := app.config.Section("ui").Key("allow_all").MustBool(false) + referralsEnabled := app.config.Section("user_page").Key("referrals").MustBool(false) i := 0 for _, jfUser := range users { user := respUser{ - ID: jfUser.ID, - Name: jfUser.Name, - Admin: jfUser.Policy.IsAdministrator, - Disabled: jfUser.Policy.IsDisabled, + ID: jfUser.ID, + Name: jfUser.Name, + Admin: jfUser.Policy.IsAdministrator, + Disabled: jfUser.Policy.IsDisabled, + ReferralsEnabled: false, } if !jfUser.LastActivityDate.IsZero() { user.LastActive = jfUser.LastActivityDate.Unix() @@ -923,6 +930,18 @@ func (app *appContext) GetUsers(gc *gin.Context) { user.DiscordID = dcUser.ID user.NotifyThroughDiscord = dcUser.Contact } + // FIXME: Send referral data + referrerInv := Invite{} + if referralsEnabled { + // 1. Directly attached invite. + err := app.storage.db.FindOne(&referrerInv, badgerhold.Where("ReferrerJellyfinID").Eq(jfUser.ID)) + if err == nil { + user.ReferralsEnabled = true + // 2. Referrals via profile template. Shallow check, doesn't look for the thing in the database. + } else if email, ok := app.storage.GetEmailsKey(jfUser.ID); ok && email.ReferralTemplateKey != "" { + user.ReferralsEnabled = true + } + } resp.UserList[i] = user i++ } diff --git a/html/admin.html b/html/admin.html index 8eac284..690a59a 100644 --- a/html/admin.html +++ b/html/admin.html @@ -693,6 +693,9 @@ {{ if .discordEnabled }} Discord {{ end }} + {{ if .referralsEnabled }} + {{ .strings.referrals }} + {{ end }} {{ .strings.expiry }} {{ .strings.lastActiveTime }} diff --git a/models.go b/models.go index 357b0b7..ee6e089 100644 --- a/models.go +++ b/models.go @@ -151,6 +151,7 @@ type respUser struct { NotifyThroughMatrix bool `json:"notify_matrix"` Label string `json:"label"` // Label of user, shown next to their name. AccountsAdmin bool `json:"accounts_admin"` // Whether or not the user is a jfa-go admin. + ReferralsEnabled bool `json:"referrals_enabled"` } type getUsersDTO struct { diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index 563589c..a35694e 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -23,6 +23,7 @@ interface User { notify_matrix: boolean; label: string; accounts_admin: boolean; + referrals_enabled: boolean; } interface getPinResponse { @@ -69,6 +70,8 @@ class user implements User { private _labelEditButton: HTMLElement; private _accounts_admin: HTMLInputElement private _selected: boolean; + private _referralsEnabled: boolean; + private _referralsEnabledCheck: HTMLElement; lastNotifyMethod = (): string => { // Telegram, Matrix, Discord @@ -162,6 +165,17 @@ class user implements User { } } + get referrals_enabled(): boolean { return this._referralsEnabled; } + set referrals_enabled(v: boolean) { + this._referralsEnabled = v; + if (!window.referralsEnabled) return; + if (!v) { + this._referralsEnabledCheck.textContent = ``; + } else { + this._referralsEnabledCheck.innerHTML = ``; + } + } + private _constructDropdown = (): HTMLDivElement => { const el = document.createElement("div") as HTMLDivElement; const telegram = this._telegramUsername != ""; @@ -506,6 +520,11 @@ class user implements User { `; } + if (window.referralsEnabled) { + innerHTML += ` + + `; + } innerHTML += ` @@ -544,6 +563,10 @@ class user implements User { }); }; } + + if (window.referralsEnabled) { + this._referralsEnabledCheck = this._row.querySelector(".accounts-referrals"); + } this._notifyDropdown = this._constructDropdown(); @@ -716,6 +739,7 @@ class user implements User { this.discord_id = user.discord_id; this.label = user.label; this.accounts_admin = user.accounts_admin; + this.referrals_enabled = user.referrals_enabled; } asElement = (): HTMLTableRowElement => { return this._row; } @@ -1061,7 +1085,7 @@ export class accountsList { let attempt: { year?: number, month?: number, day?: number, hour?: number, minute?: number } = dateParser.attempt(split[1]); // Month in Date objects is 0-based, so make our parsed date that way too - if ("month" in attempt) attempt["month"] -= 1; + if ("month" in attempt) attempt.month -= 1; let date: Date = (Date as any).fromString(split[1]) as Date; console.log("Read", attempt, "and", date);