mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 17:10:10 +00:00
implement frontend for user expiry/duration
this will add an optional validity period to users, where their account will be disabled (or deleted) a specified amount of time after they created it.
This commit is contained in:
parent
3635b6a367
commit
2934832a98
26
api.go
26
api.go
@ -634,6 +634,12 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
invite.RemainingUses = 1
|
invite.RemainingUses = 1
|
||||||
}
|
}
|
||||||
|
invite.UserDuration = req.UserDuration
|
||||||
|
if invite.UserDuration {
|
||||||
|
invite.UserDays = req.UserDays
|
||||||
|
invite.UserHours = req.UserHours
|
||||||
|
invite.UserMinutes = req.UserMinutes
|
||||||
|
}
|
||||||
invite.ValidTill = validTill
|
invite.ValidTill = validTill
|
||||||
if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
||||||
app.debug.Printf("%s: Sending invite email", inviteCode)
|
app.debug.Printf("%s: Sending invite email", inviteCode)
|
||||||
@ -813,14 +819,18 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
for code, inv := range app.storage.invites {
|
for code, inv := range app.storage.invites {
|
||||||
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
|
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
|
||||||
invite := inviteDTO{
|
invite := inviteDTO{
|
||||||
Code: code,
|
Code: code,
|
||||||
Days: days,
|
Days: days,
|
||||||
Hours: hours,
|
Hours: hours,
|
||||||
Minutes: minutes,
|
Minutes: minutes,
|
||||||
Created: app.formatDatetime(inv.Created),
|
UserDuration: inv.UserDuration,
|
||||||
Profile: inv.Profile,
|
UserDays: inv.UserDays,
|
||||||
NoLimit: inv.NoLimit,
|
UserHours: inv.UserHours,
|
||||||
Label: inv.Label,
|
UserMinutes: inv.UserMinutes,
|
||||||
|
Created: app.formatDatetime(inv.Created),
|
||||||
|
Profile: inv.Profile,
|
||||||
|
NoLimit: inv.NoLimit,
|
||||||
|
Label: inv.Label,
|
||||||
}
|
}
|
||||||
if len(inv.UsedBy) != 0 {
|
if len(inv.UsedBy) != 0 {
|
||||||
invite.UsedBy = inv.UsedBy
|
invite.UsedBy = inv.UsedBy
|
||||||
|
@ -259,23 +259,62 @@
|
|||||||
<span class="heading">{{ .strings.create }}</span>
|
<span class="heading">{{ .strings.create }}</span>
|
||||||
<div class="row" id="create-inv">
|
<div class="row" id="create-inv">
|
||||||
<div class="card ~neutral !normal col">
|
<div class="card ~neutral !normal col">
|
||||||
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
|
<div class="flex-row mb-1">
|
||||||
<div class="select ~neutral !normal mb-1 mt-half">
|
<label class="flex-row-group mr-1">
|
||||||
<select id="create-days">
|
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
|
||||||
<option>0</option>
|
<span class="button ~neutral !high supra full-width center">{{ .strings.inviteDuration }}</span>
|
||||||
</select>
|
</label>
|
||||||
|
<label class="flex-row-group ml-1">
|
||||||
|
<input type="radio" name="duration" class="unfocused" id="radio-user-duration">
|
||||||
|
<span class="button ~neutral !normal supra full-width center">{{ .strings.userDuration }}</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
|
<div id="inv-duration">
|
||||||
<div class="select ~neutral !normal mb-1 mt-half">
|
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
|
||||||
<select id="create-hours">
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
<option>0</option>
|
<select id="create-days">
|
||||||
</select>
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
|
||||||
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
|
<select id="create-hours">
|
||||||
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
|
||||||
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
|
<select id="create-minutes">
|
||||||
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
|
<div id="user-duration" class="unfocused">
|
||||||
<div class="select ~neutral !normal mb-1 mt-half">
|
<p class="support">{{ .strings.userDurationDescription }}</p>
|
||||||
<select id="create-minutes">
|
<div class="mb-half">
|
||||||
<option>0</option>
|
<label for="create-user-duration-enabled" class="button ~neutral !normal">
|
||||||
</select>
|
<input type="checkbox" id="create-user-duration-enabled" aria-label="User duration enabled">
|
||||||
|
<span class="ml-half">{{ .strings.enabled }} </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
|
||||||
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
|
<select id="user-days">
|
||||||
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
|
||||||
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
|
<select id="user-hours">
|
||||||
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
|
||||||
|
<div class="select ~neutral !normal mb-1 mt-half">
|
||||||
|
<select id="user-minutes">
|
||||||
|
<option>0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
|
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
|
||||||
<input type="text" id="create-label" class="input ~neutral !normal mb-1 mt-half">
|
<input type="text" id="create-label" class="input ~neutral !normal mb-1 mt-half">
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
window.code = "{{ .code }}";
|
window.code = "{{ .code }}";
|
||||||
window.messages = JSON.parse({{ .notifications }});
|
window.messages = JSON.parse({{ .notifications }});
|
||||||
window.confirmation = {{ .confirmation }};
|
window.confirmation = {{ .confirmation }};
|
||||||
|
window.userDurationEnabled = {{ .userDuration }};
|
||||||
|
window.userDurationDays = {{ .userDurationDays }};
|
||||||
|
window.userDurationHours = {{ .userDurationHours }};
|
||||||
|
window.userDurationMinutes = {{ .userDurationMinutes }};
|
||||||
|
window.userDurationMessage = {{ .userDurationMessage }};
|
||||||
</script>
|
</script>
|
||||||
<script src="js/form.js" type="module"></script>
|
<script src="js/form.js" type="module"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -37,6 +37,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
{{ if .userDuration }}
|
||||||
|
<aside class="col aside sm ~warning" id="user-duration-message"></aside>
|
||||||
|
{{ end }}
|
||||||
<form class="card ~neutral !normal" id="form-create" href="">
|
<form class="card ~neutral !normal" id="form-create" href="">
|
||||||
<label class="label supra">
|
<label class="label supra">
|
||||||
{{ .strings.username }}
|
{{ .strings.username }}
|
||||||
@ -44,13 +47,13 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
|
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
|
||||||
<input type="email" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
<input type="email" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
||||||
|
|
||||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
||||||
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
||||||
|
|
||||||
<label class="label supra" for="create-reenter-password">{{ .strings.reEnterPassword }}</label>
|
<label class="label supra" for="create-reenter-password">{{ .strings.reEnterPassword }}</label>
|
||||||
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
||||||
<label>
|
<label>
|
||||||
<input type="submit" class="unfocused">
|
<input type="submit" class="unfocused">
|
||||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.createAccountButton }}</span>
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.createAccountButton }}</span>
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"inviteHours": "Hours",
|
"inviteHours": "Hours",
|
||||||
"inviteMinutes": "Minutes",
|
"inviteMinutes": "Minutes",
|
||||||
"inviteNumberOfUses": "Number of uses",
|
"inviteNumberOfUses": "Number of uses",
|
||||||
|
"inviteDuration": "Invite Duration",
|
||||||
"warning": "Warning",
|
"warning": "Warning",
|
||||||
"inviteInfiniteUsesWarning": "invites with infinite uses can be used abusively",
|
"inviteInfiniteUsesWarning": "invites with infinite uses can be used abusively",
|
||||||
"inviteSendToEmail": "Send to",
|
"inviteSendToEmail": "Send to",
|
||||||
@ -20,9 +21,12 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"userDurationDescription": "A specified amount of time after each signup, jfa-go will delete/disable the account. You can change this behaviour in settings.",
|
||||||
"lastActiveTime": "Last Active",
|
"lastActiveTime": "Last Active",
|
||||||
"from": "From",
|
"from": "From",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
|
"userDuration": "User Duration",
|
||||||
"aboutProgram": "About",
|
"aboutProgram": "About",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"commitNoun": "Commit",
|
"commitNoun": "Commit",
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
"successHeader": "Success!",
|
"successHeader": "Success!",
|
||||||
"successContinueButton": "Continue",
|
"successContinueButton": "Continue",
|
||||||
"confirmationRequired": "Email confirmation required",
|
"confirmationRequired": "Email confirmation required",
|
||||||
"confirmationRequiredMessage": "Please check your email inbox to verify your address."
|
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
|
||||||
|
"yourAccountIsValidUntil": "Your account will be valid until {date}."
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorUserExists": "User already exists.",
|
"errorUserExists": "User already exists.",
|
||||||
|
26
models.go
26
models.go
@ -30,15 +30,19 @@ type deleteUserDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type generateInviteDTO struct {
|
type generateInviteDTO struct {
|
||||||
Days int `json:"days" example:"1"` // Number of days
|
Days int `json:"days" example:"1"` // Number of days
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours
|
Hours int `json:"hours" example:"2"` // Number of hours
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes
|
Minutes int `json:"minutes" example:"3"` // Number of minutes
|
||||||
Email string `json:"email" example:"jeff@jellyf.in"` // Send invite to this address
|
UserDuration bool `json:"user-duration"` // Whether or not user duration is enabled
|
||||||
MultipleUses bool `json:"multiple-uses" example:"true"` // Allow multiple uses
|
UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry
|
||||||
NoLimit bool `json:"no-limit" example:"false"` // No invite use limit
|
UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry
|
||||||
RemainingUses int `json:"remaining-uses" example:"5"` // Remaining invite uses
|
UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Name of profile to apply on this invite
|
Email string `json:"email" example:"jeff@jellyf.in"` // Send invite to this address
|
||||||
Label string `json:"label" example:"For Friends"` // Optional label for the invite
|
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
|
||||||
|
Label string `json:"label" example:"For Friends"` // Optional label for the invite
|
||||||
}
|
}
|
||||||
|
|
||||||
type inviteProfileDTO struct {
|
type inviteProfileDTO struct {
|
||||||
@ -72,6 +76,10 @@ type inviteDTO struct {
|
|||||||
Days int `json:"days" example:"1"` // Number of days till expiry
|
Days int `json:"days" example:"1"` // Number of days till expiry
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours till expiry
|
Hours int `json:"hours" example:"2"` // Number of hours till expiry
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry
|
Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry
|
||||||
|
UserDuration bool `json:"user-duration"` // Whether or not user duration is enabled
|
||||||
|
UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry
|
||||||
|
UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry
|
||||||
|
UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry
|
||||||
Created string `json:"created" example:"01/01/20 12:00"` // Date of creation
|
Created string `json:"created" example:"01/01/20 12:00"` // Date of creation
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
||||||
UsedBy [][]string `json:"used-by,omitempty"` // Users who have used this invite
|
UsedBy [][]string `json:"used-by,omitempty"` // Users who have used this invite
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -236,9 +236,9 @@
|
|||||||
"integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY="
|
"integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY="
|
||||||
},
|
},
|
||||||
"esbuild": {
|
"esbuild": {
|
||||||
"version": "0.8.50",
|
"version": "0.8.53",
|
||||||
"resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.8.50.tgz",
|
"resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.8.53.tgz",
|
||||||
"integrity": "sha1-6/JP3gza0aNpeJ3W/XqCCwoB5Gw="
|
"integrity": "sha1-tAi7DKGynasT2Lv31Z9Zr+Z3boY="
|
||||||
},
|
},
|
||||||
"escalade": {
|
"escalade": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ts-stack/markdown": "^1.3.0",
|
"@ts-stack/markdown": "^1.3.0",
|
||||||
"a17t": "^0.4.0",
|
"a17t": "^0.4.0",
|
||||||
"esbuild": "^0.8.50",
|
"esbuild": "^0.8.53",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"mjml": "^4.8.0",
|
"mjml": "^4.8.0",
|
||||||
"remixicon": "^2.5.0",
|
"remixicon": "^2.5.0",
|
||||||
|
@ -59,6 +59,10 @@ type Invite struct {
|
|||||||
NoLimit bool `json:"no-limit"`
|
NoLimit bool `json:"no-limit"`
|
||||||
RemainingUses int `json:"remaining-uses"`
|
RemainingUses int `json:"remaining-uses"`
|
||||||
ValidTill time.Time `json:"valid_till"`
|
ValidTill time.Time `json:"valid_till"`
|
||||||
|
UserDuration bool `json:"user-duration"`
|
||||||
|
UserDays int `json:"user-days,omitempty"`
|
||||||
|
UserHours int `json:"user-hours,omitempty"`
|
||||||
|
UserMinutes int `json:"user-minutes,omitempty"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
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"`
|
||||||
|
18
ts/form.ts
18
ts/form.ts
@ -10,6 +10,11 @@ interface formWindow extends Window {
|
|||||||
messages: { [key: string]: string };
|
messages: { [key: string]: string };
|
||||||
confirmation: boolean;
|
confirmation: boolean;
|
||||||
confirmationModal: Modal
|
confirmationModal: Modal
|
||||||
|
userDurationEnabled: boolean;
|
||||||
|
userDurationDays: number;
|
||||||
|
userDurationHours: number;
|
||||||
|
userDurationMinutes: number;
|
||||||
|
userDurationMessage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface pwValString {
|
interface pwValString {
|
||||||
@ -34,6 +39,19 @@ if (window.confirmation) {
|
|||||||
}
|
}
|
||||||
declare var window: formWindow;
|
declare var window: formWindow;
|
||||||
|
|
||||||
|
if (window.userDurationEnabled) {
|
||||||
|
const messageEl = document.getElementById("user-duration-message") as HTMLElement;
|
||||||
|
const calculateTime = () => {
|
||||||
|
let time = new Date()
|
||||||
|
time.setDate(time.getDate() + window.userDurationDays);
|
||||||
|
time.setHours(time.getHours() + window.userDurationHours);
|
||||||
|
time.setMinutes(time.getMinutes() + window.userDurationMinutes);
|
||||||
|
messageEl.textContent = window.userDurationMessage.replace("{date}", time.toDateString() + " " + time.toLocaleTimeString());
|
||||||
|
setTimeout(calculateTime, 1000);
|
||||||
|
};
|
||||||
|
calculateTime();
|
||||||
|
}
|
||||||
|
|
||||||
var defaultPwValStrings: pwValStrings = {
|
var defaultPwValStrings: pwValStrings = {
|
||||||
length: {
|
length: {
|
||||||
singular: "Must have at least {n} character",
|
singular: "Must have at least {n} character",
|
||||||
|
@ -62,6 +62,19 @@ export class DOMInvite implements Invite {
|
|||||||
this._infoArea.querySelector("span.inv-expiry").textContent = expiry;
|
this._infoArea.querySelector("span.inv-expiry").textContent = expiry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _userDuration: string;
|
||||||
|
get userDurationTime(): string { return this._userDuration; }
|
||||||
|
set userDurationTime(d: string) {
|
||||||
|
const duration = this._middle.querySelector("span.user-duration") as HTMLSpanElement;
|
||||||
|
if (!d) {
|
||||||
|
duration.textContent = "";
|
||||||
|
} else {
|
||||||
|
duration.textContent = window.lang.strings("userDuration");
|
||||||
|
}
|
||||||
|
this._userDuration = d;
|
||||||
|
this._middle.querySelector("strong.user-duration-time").textContent = d;
|
||||||
|
}
|
||||||
|
|
||||||
private _remainingUses: string = "1";
|
private _remainingUses: string = "1";
|
||||||
get remainingUses(): string { return this._remainingUses; }
|
get remainingUses(): string { return this._remainingUses; }
|
||||||
set remainingUses(remaining: string) {
|
set remainingUses(remaining: string) {
|
||||||
@ -331,6 +344,7 @@ export class DOMInvite implements Invite {
|
|||||||
this._middle.innerHTML = `
|
this._middle.innerHTML = `
|
||||||
<p class="supra mb-1 top">${window.lang.strings("inviteDateCreated")} <strong class="inv-created"></strong></p>
|
<p class="supra mb-1 top">${window.lang.strings("inviteDateCreated")} <strong class="inv-created"></strong></p>
|
||||||
<p class="supra mb-1">${window.lang.strings("inviteRemainingUses")} <strong class="inv-remaining"></strong></p>
|
<p class="supra mb-1">${window.lang.strings("inviteRemainingUses")} <strong class="inv-remaining"></strong></p>
|
||||||
|
<p class="supra mb-1"><span class="user-duration"></span> <strong class="user-duration-time"></strong></p>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
this._right = document.createElement('div') as HTMLDivElement;
|
this._right = document.createElement('div') as HTMLDivElement;
|
||||||
@ -362,6 +376,7 @@ export class DOMInvite implements Invite {
|
|||||||
if (invite.label) {
|
if (invite.label) {
|
||||||
this.label = invite.label;
|
this.label = invite.label;
|
||||||
}
|
}
|
||||||
|
this.userDurationTime = invite.userDurationTime || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
asElement = (): HTMLDivElement => { return this._container; }
|
asElement = (): HTMLDivElement => { return this._container; }
|
||||||
@ -462,13 +477,25 @@ function parseInvite(invite: { [f: string]: string | number | string[][] | boole
|
|||||||
parsed.email = invite["email"] as string || "";
|
parsed.email = invite["email"] as string || "";
|
||||||
parsed.label = invite["label"] as string || "";
|
parsed.label = invite["label"] as string || "";
|
||||||
let time = "";
|
let time = "";
|
||||||
|
let userDurationTime = "";
|
||||||
const fields = ["days", "hours", "minutes"];
|
const fields = ["days", "hours", "minutes"];
|
||||||
|
let prefixes = [""];
|
||||||
|
if (invite["user-duration"] as boolean) { prefixes.push("user-"); }
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
if (invite[fields[i]] != 0) {
|
for (let j = 0; j < prefixes.length; j++) {
|
||||||
time += `${invite[fields[i]]}${fields[i][0]} `;
|
if (invite[prefixes[j]+fields[i]]) {
|
||||||
|
let text = `${invite[prefixes[j]+fields[i]]}${fields[i][0]} `;
|
||||||
|
if (prefixes[j] == "user-") {
|
||||||
|
userDurationTime += text;
|
||||||
|
} else {
|
||||||
|
time += text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parsed.expiresIn = window.lang.var("strings", "inviteExpiresInTime", time.slice(0, -1));
|
parsed.expiresIn = window.lang.var("strings", "inviteExpiresInTime", time.slice(0, -1));
|
||||||
|
parsed.userDuration = invite["user-duration"] as boolean;
|
||||||
|
parsed.userDurationTime = userDurationTime.slice(0, -1);
|
||||||
parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"])
|
parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"])
|
||||||
parsed.usedBy = invite["used-by"] as string[][] || [];
|
parsed.usedBy = invite["used-by"] as string[][] || [];
|
||||||
parsed.created = invite["created"] as string || window.lang.strings("unknown");
|
parsed.created = invite["created"] as string || window.lang.strings("unknown");
|
||||||
@ -481,6 +508,7 @@ function parseInvite(invite: { [f: string]: string | number | string[][] | boole
|
|||||||
export class createInvite {
|
export class createInvite {
|
||||||
private _sendToEnabled = document.getElementById("create-send-to-enabled") as HTMLInputElement;
|
private _sendToEnabled = document.getElementById("create-send-to-enabled") as HTMLInputElement;
|
||||||
private _sendTo = document.getElementById("create-send-to") as HTMLInputElement;
|
private _sendTo = document.getElementById("create-send-to") as HTMLInputElement;
|
||||||
|
private _userDurationToggle = document.getElementById("create-user-duration-enabled") as HTMLInputElement;
|
||||||
private _uses = document.getElementById('create-uses') as HTMLInputElement;
|
private _uses = document.getElementById('create-uses') as HTMLInputElement;
|
||||||
private _infUses = document.getElementById("create-inf-uses") as HTMLInputElement;
|
private _infUses = document.getElementById("create-inf-uses") as HTMLInputElement;
|
||||||
private _infUsesWarning = document.getElementById('create-inf-uses-warning') as HTMLParagraphElement;
|
private _infUsesWarning = document.getElementById('create-inf-uses-warning') as HTMLParagraphElement;
|
||||||
@ -491,6 +519,14 @@ export class createInvite {
|
|||||||
private _days = document.getElementById("create-days") as HTMLSelectElement;
|
private _days = document.getElementById("create-days") as HTMLSelectElement;
|
||||||
private _hours = document.getElementById("create-hours") as HTMLSelectElement;
|
private _hours = document.getElementById("create-hours") as HTMLSelectElement;
|
||||||
private _minutes = document.getElementById("create-minutes") as HTMLSelectElement;
|
private _minutes = document.getElementById("create-minutes") as HTMLSelectElement;
|
||||||
|
private _userDays = document.getElementById("user-days") as HTMLSelectElement;
|
||||||
|
private _userHours = document.getElementById("user-hours") as HTMLSelectElement;
|
||||||
|
private _userMinutes = document.getElementById("user-minutes") as HTMLSelectElement;
|
||||||
|
|
||||||
|
private _invDurationButton = document.getElementById('radio-inv-duration') as HTMLInputElement;
|
||||||
|
private _userDurationButton = document.getElementById('radio-user-duration') as HTMLInputElement;
|
||||||
|
private _invDuration = document.getElementById('inv-duration');
|
||||||
|
private _userDuration = document.getElementById('user-duration');
|
||||||
|
|
||||||
// Broadcast when new invite created
|
// Broadcast when new invite created
|
||||||
private _newInviteEvent = new CustomEvent("newInviteEvent");
|
private _newInviteEvent = new CustomEvent("newInviteEvent");
|
||||||
@ -498,15 +534,18 @@ export class createInvite {
|
|||||||
|
|
||||||
private _count: Number = 30;
|
private _count: Number = 30;
|
||||||
private _populateNumbers = () => {
|
private _populateNumbers = () => {
|
||||||
const fieldIDs = ["create-days", "create-hours", "create-minutes"];
|
const fieldIDs = ["days", "hours", "minutes"];
|
||||||
|
const prefixes = ["create-", "user-"];
|
||||||
for (let i = 0; i < fieldIDs.length; i++) {
|
for (let i = 0; i < fieldIDs.length; i++) {
|
||||||
const field = document.getElementById(fieldIDs[i]);
|
for (let j = 0; j < prefixes.length; j++) {
|
||||||
field.textContent = '';
|
const field = document.getElementById(prefixes[j] + fieldIDs[i]);
|
||||||
for (let n = 0; n <= this._count; n++) {
|
field.textContent = '';
|
||||||
const opt = document.createElement("option") as HTMLOptionElement;
|
for (let n = 0; n <= this._count; n++) {
|
||||||
opt.textContent = ""+n;
|
const opt = document.createElement("option") as HTMLOptionElement;
|
||||||
opt.value = ""+n;
|
opt.textContent = ""+n;
|
||||||
field.appendChild(opt);
|
opt.value = ""+n;
|
||||||
|
field.appendChild(opt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,6 +619,33 @@ export class createInvite {
|
|||||||
this._minutes.value = ""+n;
|
this._minutes.value = ""+n;
|
||||||
this._checkDurationValidity();
|
this._checkDurationValidity();
|
||||||
}
|
}
|
||||||
|
get userDuration(): boolean {
|
||||||
|
return this._userDurationToggle.checked;
|
||||||
|
}
|
||||||
|
set userDuration(enabled: boolean) {
|
||||||
|
this._userDurationToggle.checked = enabled;
|
||||||
|
this._userDays.disabled = !enabled;
|
||||||
|
this._userHours.disabled = !enabled;
|
||||||
|
this._userMinutes.disabled = !enabled;
|
||||||
|
}
|
||||||
|
get userDays(): number {
|
||||||
|
return +this._userDays.value;
|
||||||
|
}
|
||||||
|
set userDays(n: number) {
|
||||||
|
this._userDays.value = ""+n;
|
||||||
|
}
|
||||||
|
get userHours(): number {
|
||||||
|
return +this._userHours.value;
|
||||||
|
}
|
||||||
|
set userHours(n: number) {
|
||||||
|
this._userHours.value = ""+n;
|
||||||
|
}
|
||||||
|
get userMinutes(): number {
|
||||||
|
return +this._userMinutes.value;
|
||||||
|
}
|
||||||
|
set userMinutes(n: number) {
|
||||||
|
this._userMinutes.value = ""+n;
|
||||||
|
}
|
||||||
|
|
||||||
get sendTo(): string { return this._sendTo.value; }
|
get sendTo(): string { return this._sendTo.value; }
|
||||||
set sendTo(address: string) { this._sendTo.value = address; }
|
set sendTo(address: string) { this._sendTo.value = address; }
|
||||||
@ -613,10 +679,18 @@ export class createInvite {
|
|||||||
|
|
||||||
create = () => {
|
create = () => {
|
||||||
toggleLoader(this._createButton);
|
toggleLoader(this._createButton);
|
||||||
|
let userDuration = this.userDuration;
|
||||||
|
if (this.userDays == 0 && this.userHours == 0 && this.userMinutes == 0) {
|
||||||
|
userDuration = false;
|
||||||
|
}
|
||||||
let send = {
|
let send = {
|
||||||
"days": this.days,
|
"days": this.days,
|
||||||
"hours": this.hours,
|
"hours": this.hours,
|
||||||
"minutes": this.minutes,
|
"minutes": this.minutes,
|
||||||
|
"user-duration": userDuration,
|
||||||
|
"user-days": this.userDays,
|
||||||
|
"user-hours": this.userHours,
|
||||||
|
"user-minutes": this.userMinutes,
|
||||||
"multiple-uses": (this.uses > 1 || this.infiniteUses),
|
"multiple-uses": (this.uses > 1 || this.infiniteUses),
|
||||||
"no-limit": this.infiniteUses,
|
"no-limit": this.infiniteUses,
|
||||||
"remaining-uses": this.uses,
|
"remaining-uses": this.uses,
|
||||||
@ -642,12 +716,43 @@ export class createInvite {
|
|||||||
this._infUses.onchange = () => { this.infiniteUses = this.infiniteUses; };
|
this._infUses.onchange = () => { this.infiniteUses = this.infiniteUses; };
|
||||||
this.infiniteUses = false;
|
this.infiniteUses = false;
|
||||||
this._sendToEnabled.onchange = () => { this.sendToEnabled = this.sendToEnabled; };
|
this._sendToEnabled.onchange = () => { this.sendToEnabled = this.sendToEnabled; };
|
||||||
|
this.userDuration = false;
|
||||||
|
this._userDurationToggle.onchange = () => { this.userDuration = this._userDurationToggle.checked; }
|
||||||
|
this._userDays.disabled = true;
|
||||||
|
this._userHours.disabled = true;
|
||||||
|
this._userMinutes.disabled = true;
|
||||||
this.sendToEnabled = false;
|
this.sendToEnabled = false;
|
||||||
this._createButton.onclick = this.create;
|
this._createButton.onclick = this.create;
|
||||||
this.sendTo = "";
|
this.sendTo = "";
|
||||||
this.uses = 1;
|
this.uses = 1;
|
||||||
this.label = "";
|
this.label = "";
|
||||||
|
|
||||||
|
const checkDuration = () => {
|
||||||
|
console.log("bbbb")
|
||||||
|
const invSpan = this._invDurationButton.nextElementSibling as HTMLSpanElement;
|
||||||
|
const userSpan = this._userDurationButton.nextElementSibling as HTMLSpanElement;
|
||||||
|
if (this._invDurationButton.checked) {
|
||||||
|
this._invDuration.classList.remove("unfocused");
|
||||||
|
this._userDuration.classList.add("unfocused");
|
||||||
|
invSpan.classList.add("!high");
|
||||||
|
invSpan.classList.remove("!normal");
|
||||||
|
userSpan.classList.add("!normal");
|
||||||
|
userSpan.classList.remove("!high");
|
||||||
|
} else if (this._userDurationButton.checked) {
|
||||||
|
this._userDuration.classList.remove("unfocused");
|
||||||
|
this._invDuration.classList.add("unfocused");
|
||||||
|
invSpan.classList.add("!normal");
|
||||||
|
invSpan.classList.remove("!high");
|
||||||
|
userSpan.classList.add("!high");
|
||||||
|
userSpan.classList.remove("!normal");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._userDurationButton.checked = false;
|
||||||
|
this._invDurationButton.checked = true;
|
||||||
|
this._userDurationButton.onchange = checkDuration;
|
||||||
|
this._invDurationButton.onchange = checkDuration;
|
||||||
|
|
||||||
this._days.onchange = this._checkDurationValidity;
|
this._days.onchange = this._checkDurationValidity;
|
||||||
this._hours.onchange = this._checkDurationValidity;
|
this._hours.onchange = this._checkDurationValidity;
|
||||||
this._minutes.onchange = this._checkDurationValidity;
|
this._minutes.onchange = this._checkDurationValidity;
|
||||||
|
@ -90,6 +90,8 @@ interface Invite {
|
|||||||
notifyCreation?: boolean;
|
notifyCreation?: boolean;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
userDuration?: boolean;
|
||||||
|
userDurationTime?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface inviteList {
|
interface inviteList {
|
||||||
|
35
views.go
35
views.go
@ -167,21 +167,26 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
|||||||
email = ""
|
email = ""
|
||||||
}
|
}
|
||||||
gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{
|
gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{
|
||||||
"urlBase": app.getURLBase(gc),
|
"urlBase": app.getURLBase(gc),
|
||||||
"cssClass": app.cssClass,
|
"cssClass": app.cssClass,
|
||||||
"contactMessage": app.config.Section("ui").Key("contact_message").String(),
|
"contactMessage": app.config.Section("ui").Key("contact_message").String(),
|
||||||
"helpMessage": app.config.Section("ui").Key("help_message").String(),
|
"helpMessage": app.config.Section("ui").Key("help_message").String(),
|
||||||
"successMessage": app.config.Section("ui").Key("success_message").String(),
|
"successMessage": app.config.Section("ui").Key("success_message").String(),
|
||||||
"jfLink": app.config.Section("jellyfin").Key("public_server").String(),
|
"jfLink": app.config.Section("jellyfin").Key("public_server").String(),
|
||||||
"validate": app.config.Section("password_validation").Key("enabled").MustBool(false),
|
"validate": app.config.Section("password_validation").Key("enabled").MustBool(false),
|
||||||
"requirements": app.validator.getCriteria(),
|
"requirements": app.validator.getCriteria(),
|
||||||
"email": email,
|
"email": email,
|
||||||
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
||||||
"strings": app.storage.lang.Form[lang].Strings,
|
"strings": app.storage.lang.Form[lang].Strings,
|
||||||
"validationStrings": app.storage.lang.Form[lang].validationStringsJSON,
|
"validationStrings": app.storage.lang.Form[lang].validationStringsJSON,
|
||||||
"notifications": app.storage.lang.Form[lang].notificationsJSON,
|
"notifications": app.storage.lang.Form[lang].notificationsJSON,
|
||||||
"code": code,
|
"code": code,
|
||||||
"confirmation": app.config.Section("email_confirmation").Key("enabled").MustBool(false),
|
"confirmation": app.config.Section("email_confirmation").Key("enabled").MustBool(false),
|
||||||
|
"userDuration": inv.UserDuration,
|
||||||
|
"userDurationDays": inv.UserDays,
|
||||||
|
"userDurationHours": inv.UserHours,
|
||||||
|
"userDurationMinutes": inv.UserMinutes,
|
||||||
|
"userDurationMessage": app.storage.lang.Form[lang].Strings.get("yourAccountIsValidUntil"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user