1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-09-20 03:10:11 +00:00

Compare commits

..

No commits in common. "76bb95098c81de0c57f11ab740ac6b501bc7a3bc" and "8ac3bb97111514e848fcb9c23d0d891962742359" have entirely different histories.

6 changed files with 102 additions and 329 deletions

View File

@ -579,19 +579,11 @@
</div> </div>
</div> </div>
<div id="tab-accounts" class="unfocused"> <div id="tab-accounts" class="unfocused">
<div class="card @low dark:~d_neutral accounts mb-4 overflow-visible"> <div class="card @low dark:~d_neutral accounts mb-4">
<div class="flex-expand row"> <div class="flex-expand row">
<div class="row w-6/12"> <div class="row">
<span class="text-3xl font-bold mr-2 col">{{ .strings.accounts }}</span> <span class="text-3xl font-bold mr-2 col">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral @low input search ml-2 mr-2" id="accounts-search" placeholder="{{ .strings.search }}"> <input type="search" class="col sm field ~neutral @low input search ml-2 mr-2" id="accounts-search" placeholder="{{ .strings.search }}">
<div id="accounts-filter-dropdown" class="col sm dropdown pb-0i" tabindex="0">
<span class="h-100 sm button ~neutral @low center mb-2" id="accounts-filter-button">{{ .strings.filters }}</span>
<div class="dropdown-display">
<div class="card ~neutral @low mt-2" id="accounts-filter-list">
<p class="supra pb-2">{{ .strings.filters }}</p>
</div>
</div>
</div>
</div> </div>
<div class="row"> <div class="row">
<span class="col sm button ~neutral @low center mb-2" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span> <span class="col sm button ~neutral @low center mb-2" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>

View File

@ -118,7 +118,6 @@
"accessJFA": "Access jfa-go", "accessJFA": "Access jfa-go",
"accessJFASettings": "Cannot be changed as either \"Admin Only\" or \"Allow All\" has been set in Settings > General.", "accessJFASettings": "Cannot be changed as either \"Admin Only\" or \"Allow All\" has been set in Settings > General.",
"sortingBy": "Sorting By", "sortingBy": "Sorting By",
"filters": "Filters",
"clickToRemoveFilter": "Click to remove this filter." "clickToRemoveFilter": "Click to remove this filter."
}, },
"notifications": { "notifications": {

View File

@ -1,5 +0,0 @@
module github.com/hrfee/jfa-go/scripts/account-gen
go 1.20
require github.com/hrfee/mediabrowser v0.3.8 // indirect

View File

@ -1,2 +0,0 @@
github.com/hrfee/mediabrowser v0.3.8 h1:y0iBCb6jE3QKcsiCJSYva2fFPHRn4UA+sGRzoPuJ/Dk=
github.com/hrfee/mediabrowser v0.3.8/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=

View File

@ -1,122 +0,0 @@
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"strings"
"time"
"github.com/hrfee/mediabrowser"
)
var (
names = []string{"Aaron", "Agnes", "Bridget", "Brandon", "Dolly", "Drake", "Elizabeth", "Erika", "Geoff", "Graham", "Haley", "Halsey", "Josie", "John", "Kayleigh", "Luka", "Melissa", "Nasreen", "Paul", "Ross", "Sam", "Talib", "Veronika", "Zaynab"}
)
const (
PASSWORD = "test"
COUNT = 10
)
func main() {
fmt.Println("Usage: account-gen <server> <username> <password, or file://path to file containing password>")
var server, username, password string
reader := bufio.NewReader(os.Stdin)
if len(os.Args) > 1 {
server = os.Args[1]
} else {
fmt.Print("Server Address: ")
server, _ = reader.ReadString('\n')
server = strings.TrimSuffix(server, "\n")
}
if len(os.Args) > 2 {
username = os.Args[2]
} else {
fmt.Print("Username: ")
username, _ = reader.ReadString('\n')
username = strings.TrimSuffix(username, "\n")
}
if len(os.Args) > 3 {
password = os.Args[3]
if strings.HasPrefix(password, "file://") {
p, err := os.ReadFile(strings.TrimPrefix(password, "file://"))
if err != nil {
log.Fatalf("Failed to read password file \"%s\": %+v\n", password, err)
}
password = strings.TrimSuffix(string(p), "\n")
}
} else {
fmt.Print("Password: ")
password, _ = reader.ReadString('\n')
password = strings.TrimSuffix(password, "\n")
}
jf, err := mediabrowser.NewServer(
mediabrowser.JellyfinServer,
server,
"jfa-go-account-gen-script",
"0.0.1",
"testing",
"my_left_foot",
mediabrowser.NewNamedTimeoutHandler("Jellyfin Account Gen", "\""+server+"\"", true),
30,
)
if err != nil {
log.Fatalf("Failed to connect to Jellyin @ \"%s\": %+v\n", server, err)
}
_, status, err := jf.Authenticate(username, password)
if status != 200 || err != nil {
log.Fatalf("Failed to authenticate: %+v\n", err)
}
jfTemp, err := mediabrowser.NewServer(
mediabrowser.JellyfinServer,
server,
"jfa-go-account-gen-script",
"0.0.1",
"fake-activity",
"my_left_foot",
mediabrowser.NewNamedTimeoutHandler("Jellyfin Account Gen", "\""+server+"\"", true),
30,
)
if err != nil {
log.Fatalf("Failed to connect to Jellyin @ \"%s\": %+v\n", server, err)
}
rand.Seed(time.Now().Unix())
for i := 0; i < COUNT; i++ {
name := names[rand.Intn(len(names))] + strconv.Itoa(rand.Intn(100))
user, status, err := jf.NewUser(name, PASSWORD)
if (status != 200 && status != 201 && status != 204) || err != nil {
log.Fatalf("Failed to create user \"%s\" (%d): %+v\n", name, status, err)
}
if rand.Intn(100) > 65 {
user.Policy.IsAdministrator = true
}
if rand.Intn(100) > 80 {
user.Policy.IsDisabled = true
}
status, err = jf.SetPolicy(user.ID, user.Policy)
if (status != 200 && status != 201 && status != 204) || err != nil {
log.Fatalf("Failed to set policy for user \"%s\" (%d): %+v\n", name, status, err)
}
if rand.Intn(100) > 20 {
jfTemp.Authenticate(name, PASSWORD)
}
}
}

View File

@ -790,108 +790,101 @@ export class accountsList {
} }
} }
private _queries: { [field: string]: { name: string, getter: string, bool: boolean, string: boolean, date: boolean, dependsOnTableHeader?: string, show?: boolean }} = {
"id": {
name: "Jellyfin ID",
getter: "id",
bool: false,
string: true,
date: false
},
"label": {
name: "Label",
getter: "label",
bool: true,
string: true,
date: false
},
"username": {
name: "Username",
getter: "name",
bool: false,
string: true,
date: false
},
"name": {
name: "Username",
getter: "name",
bool: false,
string: true,
date: false,
show: false
},
"admin": {
name: "Admin",
getter: "admin",
bool: true,
string: false,
date: false
},
"disabled": {
name: "Disabled",
getter: "disabled",
bool: true,
string: false,
date: false
},
"access-jfa": {
name: "Access jfa-go",
getter: "accounts_admin",
bool: true,
string: false,
date: false,
dependsOnTableHeader: "accounts-header-access-jfa"
},
"email": {
name: "Email",
getter: "email",
bool: true,
string: true,
date: false,
dependsOnTableHeader: "accounts-header-email"
},
"telegram": {
name: "Telegram",
getter: "telegram",
bool: true,
string: true,
date: false,
dependsOnTableHeader: "accounts-header-telegram"
},
"matrix": {
name: "Matrix",
getter: "matrix",
bool: true,
string: true,
date: false,
dependsOnTableHeader: "accounts-header-matrix"
},
"discord": {
name: "Discord",
getter: "discord",
bool: true,
string: true,
date: false,
dependsOnTableHeader: "accounts-header-discord"
},
"expiry": {
name: "Expiry",
getter: "expiry",
bool: true,
string: false,
date: true,
dependsOnTableHeader: "accounts-header-expiry"
},
"last-active": {
name: "Last Active",
getter: "last_active",
bool: true,
string: false,
date: true
}
}
search = (query: String): string[] => { search = (query: String): string[] => {
const queries: { [field: string]: { name: string, getter: string, bool: boolean, string: boolean, date: boolean }} = {
"id": {
name: "Jellyfin ID",
getter: "id",
bool: false,
string: true,
date: false
},
"label": {
name: "Label",
getter: "label",
bool: true,
string: true,
date: false
},
"username": {
name: "Username",
getter: "name",
bool: false,
string: true,
date: false
},
"name": {
name: "Username",
getter: "name",
bool: false,
string: true,
date: false
},
"admin": {
name: "Admin",
getter: "admin",
bool: true,
string: false,
date: false
},
"disabled": {
name: "Disabled",
getter: "disabled",
bool: true,
string: false,
date: false
},
"access-jfa": {
name: "Access jfa-go",
getter: "accounts_admin",
bool: true,
string: false,
date: false
},
"email": {
name: "Email",
getter: "email",
bool: true,
string: true,
date: false
},
"telegram": {
name: "Telegram",
getter: "telegram",
bool: true,
string: true,
date: false
},
"matrix": {
name: "Matrix",
getter: "matrix",
bool: true,
string: true,
date: false
},
"discord": {
name: "Discord",
getter: "discord",
bool: true,
string: true,
date: false
},
"expiry": {
name: "Expiry",
getter: "expiry",
bool: true,
string: false,
date: true
},
"last-active": {
name: "Last Active",
getter: "last_active",
bool: true,
string: false,
date: true
}
}
const filterArea = document.getElementById("accounts-filter-area"); const filterArea = document.getElementById("accounts-filter-area");
filterArea.textContent = ""; filterArea.textContent = "";
@ -952,9 +945,9 @@ export class accountsList {
} }
const split = [word.substring(0, word.indexOf(":")), word.substring(word.indexOf(":")+1)]; const split = [word.substring(0, word.indexOf(":")), word.substring(word.indexOf(":")+1)];
if (!(split[0] in this._queries)) continue; if (!(split[0] in queries)) continue;
const queryFormat = this._queries[split[0]]; const queryFormat = queries[split[0]];
if (queryFormat.bool) { if (queryFormat.bool) {
let isBool = false; let isBool = false;
@ -1011,8 +1004,7 @@ export class accountsList {
filterCard.addEventListener("click", () => { filterCard.addEventListener("click", () => {
for (let quote of [`"`, `'`, ``]) { for (let quote of [`"`, `'`, ``]) {
let regex = new RegExp(split[0] + ":" + quote + split[1] + quote, "ig"); this._search.value = this._search.value.replace(split[0] + ":" + quote + split[1] + quote, "");
this._search.value = this._search.value.replace(regex, "");
} }
this._search.oninput((null as Event)); this._search.oninput((null as Event));
}) })
@ -1032,7 +1024,6 @@ export class accountsList {
if (queryFormat.date) { if (queryFormat.date) {
// -1 = Before, 0 = On, 1 = After, 2 = No symbol, assume 0 // -1 = Before, 0 = On, 1 = After, 2 = No symbol, assume 0
let compareType = (split[1][0] == ">") ? 1 : ((split[1][0] == "<") ? -1 : ((split[1][0] == "=") ? 0 : 2)); let compareType = (split[1][0] == ">") ? 1 : ((split[1][0] == "<") ? -1 : ((split[1][0] == "=") ? 0 : 2));
let unmodifiedValue = split[1];
if (compareType != 2) { if (compareType != 2) {
split[1] = split[1].substring(1); split[1] = split[1].substring(1);
} }
@ -1055,8 +1046,7 @@ export class accountsList {
filterCard.addEventListener("click", () => { filterCard.addEventListener("click", () => {
for (let quote of [`"`, `'`, ``]) { for (let quote of [`"`, `'`, ``]) {
let regex = new RegExp(split[0] + ":" + quote + unmodifiedValue + quote, "ig"); this._search.value = this._search.value.replace(split[0] + ":" + quote + split[1] + quote, "");
this._search.value = this._search.value.replace(regex, "");
} }
this._search.oninput((null as Event)); this._search.oninput((null as Event));
@ -1833,7 +1823,7 @@ export class accountsList {
const headerNames: string[] = ["username", "access-jfa", "email", "telegram", "matrix", "discord", "expiry", "last-active"]; const headerNames: string[] = ["username", "access-jfa", "email", "telegram", "matrix", "discord", "expiry", "last-active"];
const headerGetters: string[] = ["name", "accounts_admin", "email", "telegram", "matrix", "discord", "expiry", "last_active"]; const headerGetters: string[] = ["name", "accounts_admin", "email", "telegram", "matrix", "discord", "expiry", "last_active"];
for (let i = 0; i < headerNames.length; i++) { for (let i = 0; i < headerNames.length; i++) {
const header: HTMLTableHeaderCellElement = document.querySelector(".accounts-header-" + headerNames[i]) as HTMLTableHeaderCellElement; const header: HTMLTableHeaderCellElement = document.getElementsByClassName("accounts-header-" + headerNames[i])[0] as HTMLTableHeaderCellElement;
if (header !== null) { if (header !== null) {
this._columns[header.className] = new Column(header, Object.getOwnPropertyDescriptor(user.prototype, headerGetters[i]).get); this._columns[header.className] = new Column(header, Object.getOwnPropertyDescriptor(user.prototype, headerGetters[i]).get);
} }
@ -1864,85 +1854,6 @@ export class accountsList {
}); });
defaultSort(); defaultSort();
const filterList = document.getElementById("accounts-filter-list");
const fillInFilter = (name: string, value: string, offset?: number) => {
this._search.value = name + ":" + value + " " + this._search.value;
this._search.focus();
let newPos = name.length + 1 + value.length;
if (typeof offset !== 'undefined')
newPos += offset;
this._search.setSelectionRange(newPos, newPos);
this._search.oninput(null as any);
};
// Generate filter buttons
for (let queryName of Object.keys(this._queries)) {
const query = this._queries[queryName];
if ("show" in query && !query.show) continue;
if ("dependsOnTableHeader" in query && query.dependsOnTableHeader) {
const el = document.querySelector("."+query.dependsOnTableHeader);
if (el === null) continue;
}
const container = document.createElement("span") as HTMLSpanElement;
container.classList.add("button", "button-xl", "~neutral", "@low", "mb-1", "mr-2");
container.innerHTML = `<span class="mr-2">${query.name}</span>`;
if (query.bool) {
const pos = document.createElement("button") as HTMLButtonElement;
pos.type = "button";
pos.ariaLabel = `Filter by "${query.name}": True`;
pos.classList.add("button", "~positive", "ml-2");
pos.innerHTML = `<i class="ri-checkbox-circle-fill"></i>`;
pos.addEventListener("click", () => fillInFilter(queryName, "true"));
const neg = document.createElement("button") as HTMLButtonElement;
neg.type = "button";
neg.ariaLabel = `Filter by "${query.name}": False`;
neg.classList.add("button", "~critical", "ml-2");
neg.innerHTML = `<i class="ri-close-circle-fill"></i>`;
neg.addEventListener("click", () => fillInFilter(queryName, "false"));
container.appendChild(pos);
container.appendChild(neg);
}
if (query.string) {
const button = document.createElement("button") as HTMLButtonElement;
button.type = "button";
button.classList.add("button", "~urge", "ml-2");
button.innerHTML = `<i class="ri-equal-line mr-2"></i>Match Text`;
// Position cursor between quotes
button.addEventListener("click", () => fillInFilter(queryName, `""`, -1));
container.appendChild(button);
}
if (query.date) {
const onDate = document.createElement("button") as HTMLButtonElement;
onDate.type = "button";
onDate.classList.add("button", "~urge", "ml-2");
onDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>On Date`;
onDate.addEventListener("click", () => fillInFilter(queryName, `"="`, -1));
const beforeDate = document.createElement("button") as HTMLButtonElement;
beforeDate.type = "button";
beforeDate.classList.add("button", "~urge", "ml-2");
beforeDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>Before Date`;
beforeDate.addEventListener("click", () => fillInFilter(queryName, `"<"`, -1));
const afterDate = document.createElement("button") as HTMLButtonElement;
afterDate.type = "button";
afterDate.classList.add("button", "~urge", "ml-2");
afterDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>After Date`;
afterDate.addEventListener("click", () => fillInFilter(queryName, `">"`, -1));
container.appendChild(onDate);
container.appendChild(beforeDate);
container.appendChild(afterDate);
}
filterList.appendChild(container);
}
} }
reload = () => { reload = () => {