mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
Initial features for move to profiles
user templates will become profiles. You will be able to make multiple, and assign them to invites individually. This commit migrates the separate template files into one profile entry called "Default", and lets you select them on invites. No way to create profiles has been added yet.
This commit is contained in:
parent
49b056f1d6
commit
c4acb43cb8
84
api.go
84
api.go
@ -293,18 +293,26 @@ func (app *appContext) NewUser(gc *gin.Context) {
|
|||||||
if user["Id"] != nil {
|
if user["Id"] != nil {
|
||||||
id = user["Id"].(string)
|
id = user["Id"].(string)
|
||||||
}
|
}
|
||||||
if len(app.storage.policy) != 0 {
|
if invite.Profile != "" {
|
||||||
status, err = app.jf.setPolicy(id, app.storage.policy)
|
profile, ok := app.storage.profiles[invite.Profile]
|
||||||
if !(status == 200 || status == 204) {
|
if !ok {
|
||||||
app.err.Printf("%s: Failed to set user policy: Code %d", req.Code, status)
|
profile = app.storage.profiles["Default"]
|
||||||
}
|
}
|
||||||
}
|
if len(profile.Policy) != 0 {
|
||||||
if len(app.storage.configuration) != 0 && len(app.storage.displayprefs) != 0 {
|
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
|
||||||
status, err = app.jf.setConfiguration(id, app.storage.configuration)
|
status, err = app.jf.setPolicy(id, profile.Policy)
|
||||||
if (status == 200 || status == 204) && err == nil {
|
if !(status == 200 || status == 204) {
|
||||||
status, err = app.jf.setDisplayPreferences(id, app.storage.displayprefs)
|
app.err.Printf("%s: Failed to set user policy: Code %d", req.Code, status)
|
||||||
} else {
|
}
|
||||||
app.err.Printf("%s: Failed to set configuration template: Code %d", req.Code, status)
|
}
|
||||||
|
if len(profile.Configuration) != 0 && len(profile.Displayprefs) != 0 {
|
||||||
|
app.debug.Printf("Applying homescreen from profile \"%s\"", invite.Profile)
|
||||||
|
status, err = app.jf.setConfiguration(id, profile.Configuration)
|
||||||
|
if (status == 200 || status == 204) && err == nil {
|
||||||
|
status, err = app.jf.setDisplayPreferences(id, profile.Displayprefs)
|
||||||
|
} else {
|
||||||
|
app.err.Printf("%s: Failed to set configuration template: Code %d", req.Code, status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
||||||
@ -379,6 +387,7 @@ type generateInviteReq struct {
|
|||||||
MultipleUses bool `json:"multiple-uses"`
|
MultipleUses bool `json:"multiple-uses"`
|
||||||
NoLimit bool `json:"no-limit"`
|
NoLimit bool `json:"no-limit"`
|
||||||
RemainingUses int `json:"remaining-uses"`
|
RemainingUses int `json:"remaining-uses"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) GenerateInvite(gc *gin.Context) {
|
func (app *appContext) GenerateInvite(gc *gin.Context) {
|
||||||
@ -418,11 +427,39 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
app.info.Printf("%s: Sent invite email to %s", invite_code, req.Email)
|
app.info.Printf("%s: Sent invite email to %s", invite_code, req.Email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if req.Profile != "" {
|
||||||
|
if _, ok := app.storage.profiles[req.Profile]; ok {
|
||||||
|
invite.Profile = req.Profile
|
||||||
|
} else {
|
||||||
|
invite.Profile = "Default"
|
||||||
|
}
|
||||||
|
}
|
||||||
app.storage.invites[invite_code] = invite
|
app.storage.invites[invite_code] = invite
|
||||||
app.storage.storeInvites()
|
app.storage.storeInvites()
|
||||||
gc.JSON(200, map[string]bool{"success": true})
|
gc.JSON(200, map[string]bool{"success": true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type profileReq struct {
|
||||||
|
Invite string `json:"invite"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appContext) SetProfile(gc *gin.Context) {
|
||||||
|
var req profileReq
|
||||||
|
gc.BindJSON(&req)
|
||||||
|
app.debug.Printf("%s: Setting profile to \"%s\"", req.Invite, req.Profile)
|
||||||
|
if _, ok := app.storage.profiles[req.Profile]; !ok {
|
||||||
|
app.err.Printf("%s: Profile \"%s\" not found", req.Invite, req.Profile)
|
||||||
|
respond(500, "Profile not found", gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inv := app.storage.invites[req.Invite]
|
||||||
|
inv.Profile = req.Profile
|
||||||
|
app.storage.invites[req.Invite] = inv
|
||||||
|
app.storage.storeInvites()
|
||||||
|
gc.JSON(200, map[string]bool{"success": true})
|
||||||
|
}
|
||||||
|
|
||||||
func (app *appContext) GetInvites(gc *gin.Context) {
|
func (app *appContext) GetInvites(gc *gin.Context) {
|
||||||
app.debug.Println("Invites requested")
|
app.debug.Println("Invites requested")
|
||||||
current_time := time.Now()
|
current_time := time.Now()
|
||||||
@ -431,12 +468,14 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
var invites []map[string]interface{}
|
var invites []map[string]interface{}
|
||||||
for code, inv := range app.storage.invites {
|
for code, inv := range app.storage.invites {
|
||||||
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, current_time)
|
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, current_time)
|
||||||
invite := make(map[string]interface{})
|
invite := map[string]interface{}{
|
||||||
invite["code"] = code
|
"code": code,
|
||||||
invite["days"] = days
|
"days": days,
|
||||||
invite["hours"] = hours
|
"hours": hours,
|
||||||
invite["minutes"] = minutes
|
"minutes": minutes,
|
||||||
invite["created"] = app.formatDatetime(inv.Created)
|
"created": app.formatDatetime(inv.Created),
|
||||||
|
"profile": inv.Profile,
|
||||||
|
}
|
||||||
if len(inv.UsedBy) != 0 {
|
if len(inv.UsedBy) != 0 {
|
||||||
invite["used-by"] = inv.UsedBy
|
invite["used-by"] = inv.UsedBy
|
||||||
}
|
}
|
||||||
@ -470,8 +509,15 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
invites = append(invites, invite)
|
invites = append(invites, invite)
|
||||||
}
|
}
|
||||||
resp := map[string][]map[string]interface{}{
|
profiles := make([]string, len(app.storage.profiles))
|
||||||
"invites": invites,
|
i := 0
|
||||||
|
for p := range app.storage.profiles {
|
||||||
|
profiles[i] = p
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
resp := map[string]interface{}{
|
||||||
|
"profiles": profiles,
|
||||||
|
"invites": invites,
|
||||||
}
|
}
|
||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func (app *appContext) loadConfig() error {
|
|||||||
// }
|
// }
|
||||||
key.SetValue(key.MustString(filepath.Join(app.data_path, (key.Name() + ".json"))))
|
key.SetValue(key.MustString(filepath.Join(app.data_path, (key.Name() + ".json"))))
|
||||||
}
|
}
|
||||||
for _, key := range []string{"user_configuration", "user_displayprefs", "ombi_template"} {
|
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template"} {
|
||||||
// if app.config.Section("files").Key(key).MustString("") == "" {
|
// if app.config.Section("files").Key(key).MustString("") == "" {
|
||||||
// key.SetValue(filepath.Join(app.data_path, (key.Name() + ".json")))
|
// key.SetValue(filepath.Join(app.data_path, (key.Name() + ".json")))
|
||||||
// }
|
// }
|
||||||
|
@ -626,6 +626,14 @@
|
|||||||
"value": "",
|
"value": "",
|
||||||
"description": "Location of stored displayPreferences template (also used for homescreen layout) (json)"
|
"description": "Location of stored displayPreferences template (also used for homescreen layout) (json)"
|
||||||
},
|
},
|
||||||
|
"user_profiles": {
|
||||||
|
"name": "User Profiles",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": true,
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"description": "Location of stored user profiles (encompasses template and homescreen) (json)"
|
||||||
|
},
|
||||||
"custom_css": {
|
"custom_css": {
|
||||||
"name": "Custom CSS",
|
"name": "Custom CSS",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -137,7 +137,9 @@ var aboutModal = createModal('aboutModal');
|
|||||||
var deleteModal = createModal('deleteModal');
|
var deleteModal = createModal('deleteModal');
|
||||||
var newUserModal = createModal('newUserModal');
|
var newUserModal = createModal('newUserModal');
|
||||||
|
|
||||||
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>]
|
var availableProfiles = [];
|
||||||
|
// god this is an ugly way to do it
|
||||||
|
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>, <selectedProfile>]
|
||||||
function parseInvite(invite, empty = false) {
|
function parseInvite(invite, empty = false) {
|
||||||
if (empty) {
|
if (empty) {
|
||||||
return ["None", "", 1];
|
return ["None", "", 1];
|
||||||
@ -170,6 +172,9 @@ function parseInvite(invite, empty = false) {
|
|||||||
if ('notify-creation' in invite) {
|
if ('notify-creation' in invite) {
|
||||||
i[8] = invite['notify-creation'];
|
i[8] = invite['notify-creation'];
|
||||||
}
|
}
|
||||||
|
if ('profile' in invite) {
|
||||||
|
i[9] = invite['profile'];
|
||||||
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,6 +272,29 @@ function addItem(parsedInvite) {
|
|||||||
|
|
||||||
let leftList = document.createElement('ul');
|
let leftList = document.createElement('ul');
|
||||||
leftList.classList.add('list-group', 'list-group-flush');
|
leftList.classList.add('list-group', 'list-group-flush');
|
||||||
|
|
||||||
|
let profileBox = document.createElement('li');
|
||||||
|
profileBox.classList.add('input-group', 'py-1');
|
||||||
|
let prof = `
|
||||||
|
<label class="input-group-text" for="profile_${parsedInvite[0]}">Profile: </label>
|
||||||
|
<select class="form-select" id="profile_${parsedInvite[0]}" onchange="setProfile(this)">
|
||||||
|
`;
|
||||||
|
let match = false;
|
||||||
|
for (profile of availableProfiles) {
|
||||||
|
let selected = "";
|
||||||
|
if (profile == parsedInvite[9]) {
|
||||||
|
selected = "selected";
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
prof += `<option value="${profile}" ${selected}>${profile}</option>`;
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
prof += `<option value="" selected></option>`;
|
||||||
|
}
|
||||||
|
prof += `</select>`;
|
||||||
|
profileBox.innerHTML = prof;
|
||||||
|
leftList.appendChild(profileBox);
|
||||||
|
// 9 is profileName! availableProfiles
|
||||||
|
|
||||||
if (typeof(parsedInvite[6]) != 'undefined') {
|
if (typeof(parsedInvite[6]) != 'undefined') {
|
||||||
let createdDate = document.createElement('li');
|
let createdDate = document.createElement('li');
|
||||||
@ -438,6 +466,7 @@ function generateInvites(empty = false) {
|
|||||||
req.onreadystatechange = function() {
|
req.onreadystatechange = function() {
|
||||||
if (this.readyState == 4) {
|
if (this.readyState == 4) {
|
||||||
var data = this.response;
|
var data = this.response;
|
||||||
|
availableProfiles = data['profiles'];
|
||||||
if (data['invites'] == null || data['invites'].length == 0) {
|
if (data['invites'] == null || data['invites'].length == 0) {
|
||||||
document.getElementById('invites').textContent = '';
|
document.getElementById('invites').textContent = '';
|
||||||
addItem(parseInvite([], true));
|
addItem(parseInvite([], true));
|
||||||
@ -1117,7 +1146,29 @@ document.getElementById('settingsSave').onclick = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diable 'Generate' button if days, hours, minutes are all zero
|
function setProfile(select) {
|
||||||
|
if (select.value == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let invite = select.id.replace("profile_", "");
|
||||||
|
let req = new XMLHttpRequest();
|
||||||
|
let send = {
|
||||||
|
"invite": invite,
|
||||||
|
"profile": select.value
|
||||||
|
};
|
||||||
|
req.open("POST", "/setProfile", true);
|
||||||
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
||||||
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 && this.status != 200) {
|
||||||
|
generateInvites(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
req.send(JSON.stringify(send));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Disable 'Generate' button if days, hours, minutes are all zero
|
||||||
function checkDuration() {
|
function checkDuration() {
|
||||||
let boxVals = [document.getElementById("days").value, document.getElementById("hours").value, document.getElementById("minutes").value];
|
let boxVals = [document.getElementById("days").value, document.getElementById("hours").value, document.getElementById("minutes").value];
|
||||||
let submit = document.getElementById("generateSubmit");
|
let submit = document.getElementById("generateSubmit");
|
||||||
|
10
main.go
10
main.go
@ -328,6 +328,15 @@ func start(asDaemon, firstCall bool) {
|
|||||||
app.storage.displayprefs_path = app.config.Section("files").Key("user_displayprefs").String()
|
app.storage.displayprefs_path = app.config.Section("files").Key("user_displayprefs").String()
|
||||||
app.storage.loadDisplayprefs()
|
app.storage.loadDisplayprefs()
|
||||||
|
|
||||||
|
app.storage.profiles_path = app.config.Section("files").Key("user_profiles").String()
|
||||||
|
app.storage.loadProfiles()
|
||||||
|
|
||||||
|
if !(len(app.storage.policy) == 0 && len(app.storage.configuration) == 0 && len(app.storage.displayprefs) == 0) {
|
||||||
|
app.info.Println("Migrating user template files to new profile format")
|
||||||
|
app.storage.migrateToProfile()
|
||||||
|
app.storage.storeProfiles()
|
||||||
|
}
|
||||||
|
|
||||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||||
app.storage.ombi_path = app.config.Section("files").Key("ombi_template").String()
|
app.storage.ombi_path = app.config.Section("files").Key("ombi_template").String()
|
||||||
app.storage.loadOmbiTemplate()
|
app.storage.loadOmbiTemplate()
|
||||||
@ -438,6 +447,7 @@ func start(asDaemon, firstCall bool) {
|
|||||||
api.POST("/newUserAdmin", app.NewUserAdmin)
|
api.POST("/newUserAdmin", app.NewUserAdmin)
|
||||||
api.POST("/generateInvite", app.GenerateInvite)
|
api.POST("/generateInvite", app.GenerateInvite)
|
||||||
api.GET("/getInvites", app.GetInvites)
|
api.GET("/getInvites", app.GetInvites)
|
||||||
|
api.POST("/setProfile", app.SetProfile)
|
||||||
api.POST("/setNotify", app.SetNotify)
|
api.POST("/setNotify", app.SetNotify)
|
||||||
api.POST("/deleteInvite", app.DeleteInvite)
|
api.POST("/deleteInvite", app.DeleteInvite)
|
||||||
api.POST("/deleteUser", app.DeleteUser)
|
api.POST("/deleteUser", app.DeleteUser)
|
||||||
|
37
storage.go
37
storage.go
@ -7,14 +7,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
timePattern string
|
timePattern string
|
||||||
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path string
|
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path string
|
||||||
invites Invites
|
invites Invites
|
||||||
emails, policy, configuration, displayprefs, ombi_template map[string]interface{}
|
profiles map[string]Profile
|
||||||
|
emails, policy, configuration, displayprefs, ombi_template map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
Policy map[string]interface{} `json:"policy"`
|
||||||
|
Configuration map[string]interface{} `json:"configuration"`
|
||||||
|
Displayprefs map[string]interface{} `json:"displayprefs"`
|
||||||
|
}
|
||||||
|
|
||||||
type Invite struct {
|
type Invite struct {
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
NoLimit bool `json:"no-limit"`
|
NoLimit bool `json:"no-limit"`
|
||||||
@ -23,6 +30,7 @@ type Invite struct {
|
|||||||
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"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Invites map[string]Invite
|
type Invites map[string]Invite
|
||||||
@ -75,6 +83,27 @@ func (st *Storage) storeOmbiTemplate() error {
|
|||||||
return storeJSON(st.ombi_path, st.ombi_template)
|
return storeJSON(st.ombi_path, st.ombi_template)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *Storage) loadProfiles() error {
|
||||||
|
return loadJSON(st.profiles_path, &st.profiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Storage) storeProfiles() error {
|
||||||
|
return storeJSON(st.profiles_path, st.profiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Storage) migrateToProfile() error {
|
||||||
|
st.loadPolicy()
|
||||||
|
st.loadConfiguration()
|
||||||
|
st.loadDisplayprefs()
|
||||||
|
st.loadProfiles()
|
||||||
|
st.profiles["Default"] = Profile{
|
||||||
|
Policy: st.policy,
|
||||||
|
Configuration: st.configuration,
|
||||||
|
Displayprefs: st.displayprefs,
|
||||||
|
}
|
||||||
|
return st.storeProfiles()
|
||||||
|
}
|
||||||
|
|
||||||
func loadJSON(path string, obj interface{}) error {
|
func loadJSON(path string, obj interface{}) error {
|
||||||
var file []byte
|
var file []byte
|
||||||
var err error
|
var err error
|
||||||
|
Loading…
Reference in New Issue
Block a user