add rights and a few menus to go along with that
This commit is contained in:
parent
b944be96a4
commit
11d2a8cb32
7 changed files with 339 additions and 9 deletions
|
@ -29,6 +29,7 @@ import {Emoji} from "./emoji.js";
|
|||
import {Play} from "./audio/play.js";
|
||||
import {Message} from "./message.js";
|
||||
import {badgeArr} from "./Dbadges.js";
|
||||
import {Rights} from "./rights.js";
|
||||
|
||||
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
||||
|
||||
|
@ -79,11 +80,13 @@ class Localuser {
|
|||
this.play = _;
|
||||
});
|
||||
if (userinfo === -1) {
|
||||
this.rights = new Rights("");
|
||||
return;
|
||||
}
|
||||
this.token = userinfo.token;
|
||||
this.userinfo = userinfo;
|
||||
this.perminfo.guilds ??= {};
|
||||
this.perminfo.user ??= {};
|
||||
this.serverurls = this.userinfo.serverurls;
|
||||
this.initialized = false;
|
||||
this.info = this.serverurls;
|
||||
|
@ -91,6 +94,8 @@ class Localuser {
|
|||
"Content-type": "application/json; charset=UTF-8",
|
||||
Authorization: this.userinfo.token,
|
||||
};
|
||||
const rights = this.perminfo.user.rights || "875069521787904";
|
||||
this.rights = new Rights(rights);
|
||||
}
|
||||
async gottenReady(ready: readyjson): Promise<void> {
|
||||
await I18n.done;
|
||||
|
@ -406,6 +411,11 @@ class Localuser {
|
|||
await promise;
|
||||
}
|
||||
relationshipsUpdate = () => {};
|
||||
rights: Rights;
|
||||
updateRights(rights: string | number) {
|
||||
this.rights.update(rights);
|
||||
this.perminfo.user.rights = rights;
|
||||
}
|
||||
async handleEvent(temp: wsjson) {
|
||||
console.debug(temp);
|
||||
if (temp.s) this.lastSequence = temp.s;
|
||||
|
@ -1776,6 +1786,135 @@ class Localuser {
|
|||
}
|
||||
});
|
||||
}
|
||||
if (
|
||||
this.rights.hasPermission("OPERATOR") ||
|
||||
this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")
|
||||
) {
|
||||
const manageInstance = settings.addButton(I18n.localuser.manageInstance());
|
||||
if (this.rights.hasPermission("OPERATOR")) {
|
||||
manageInstance.addButtonInput("", I18n.manageInstance.stop(), () => {
|
||||
const menu = new Dialog("");
|
||||
const options = menu.float.options;
|
||||
options.addTitle(I18n.manageInstance.AreYouSureStop());
|
||||
const yesno = options.addOptions("", {ltr: true});
|
||||
yesno.addButtonInput("", I18n.yes(), () => {
|
||||
fetch(this.info.api + "/stop", {headers: this.headers, method: "POST"});
|
||||
menu.hide();
|
||||
});
|
||||
yesno.addButtonInput("", I18n.no(), () => {
|
||||
menu.hide();
|
||||
});
|
||||
menu.show();
|
||||
});
|
||||
}
|
||||
if (this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")) {
|
||||
manageInstance.addButtonInput("", I18n.manageInstance.createTokens(), () => {
|
||||
const tokens = manageInstance.addSubOptions(I18n.manageInstance.createTokens(), {
|
||||
noSubmit: true,
|
||||
});
|
||||
const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, {
|
||||
initText: "1",
|
||||
});
|
||||
const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, {
|
||||
initText: "32",
|
||||
});
|
||||
const format = tokens.addSelect(
|
||||
I18n.manageInstance.format(),
|
||||
() => {},
|
||||
[
|
||||
I18n.manageInstance.TokenFormats.JSON(),
|
||||
I18n.manageInstance.TokenFormats.plain(),
|
||||
I18n.manageInstance.TokenFormats.URLs(),
|
||||
],
|
||||
{
|
||||
defaultIndex: 2,
|
||||
},
|
||||
);
|
||||
format.watchForChange((e) => {
|
||||
if (e !== 2) {
|
||||
urlOption.removeAll();
|
||||
} else {
|
||||
makeURLMenu();
|
||||
}
|
||||
});
|
||||
const urlOption = tokens.addOptions("");
|
||||
const urlOptionsJSON = {
|
||||
url: window.location.origin,
|
||||
type: "Jank",
|
||||
};
|
||||
function makeURLMenu() {
|
||||
urlOption
|
||||
.addTextInput(I18n.manageInstance.clientURL(), () => {}, {
|
||||
initText: urlOptionsJSON.url,
|
||||
})
|
||||
.watchForChange((str) => {
|
||||
urlOptionsJSON.url = str;
|
||||
});
|
||||
urlOption
|
||||
.addSelect(
|
||||
I18n.manageInstance.regType(),
|
||||
() => {},
|
||||
["Jank", I18n.manageInstance.genericType()],
|
||||
{
|
||||
defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type),
|
||||
},
|
||||
)
|
||||
.watchForChange((i) => {
|
||||
urlOptionsJSON.type = ["Jank", "generic"][i];
|
||||
});
|
||||
}
|
||||
makeURLMenu();
|
||||
tokens.addButtonInput("", I18n.manageInstance.create(), async () => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("count", count.value);
|
||||
params.set("length", length.value);
|
||||
const json = (await (
|
||||
await fetch(
|
||||
this.info.api + "/auth/generate-registration-tokens?" + params.toString(),
|
||||
{
|
||||
headers: this.headers,
|
||||
},
|
||||
)
|
||||
).json()) as {tokens: string[]};
|
||||
if (format.index === 0) {
|
||||
pre.textContent = JSON.stringify(json.tokens);
|
||||
} else if (format.index === 1) {
|
||||
pre.textContent = json.tokens.join("\n");
|
||||
} else if (format.index === 2) {
|
||||
if (urlOptionsJSON.type === "Jank") {
|
||||
const options = new URLSearchParams();
|
||||
options.set("instance", this.info.wellknown);
|
||||
pre.textContent = json.tokens
|
||||
.map((token) => {
|
||||
options.set("token", token);
|
||||
return `${urlOptionsJSON.url}/register?` + options.toString();
|
||||
})
|
||||
.join("\n");
|
||||
} else {
|
||||
const options = new URLSearchParams();
|
||||
pre.textContent = json.tokens
|
||||
.map((token) => {
|
||||
options.set("token", token);
|
||||
return `${urlOptionsJSON.url}/register?` + options.toString();
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
tokens.addButtonInput("", I18n.manageInstance.copy(), async () => {
|
||||
try {
|
||||
if (pre.textContent) {
|
||||
await navigator.clipboard.writeText(pre.textContent);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
const pre = document.createElement("pre");
|
||||
tokens.addHTMLArea(pre);
|
||||
});
|
||||
}
|
||||
}
|
||||
settings.show();
|
||||
}
|
||||
readonly botTokens: Map<string, string> = new Map();
|
||||
|
|
|
@ -88,7 +88,10 @@ if (instancein) {
|
|||
}
|
||||
timeout = setTimeout(() => checkInstance((instancein as HTMLInputElement).value), 1000);
|
||||
});
|
||||
if (localStorage.getItem("instanceinfo")) {
|
||||
if (
|
||||
localStorage.getItem("instanceinfo") &&
|
||||
!new URLSearchParams(window.location.search).get("instance")
|
||||
) {
|
||||
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||
if (json.value) {
|
||||
(instancein as HTMLInputElement).value = json.value;
|
||||
|
|
|
@ -42,9 +42,13 @@ async function registertry(e: Event) {
|
|||
|
||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||
const apiurl = new URL(instanceInfo.api);
|
||||
|
||||
let add = "";
|
||||
const token = new URLSearchParams(window.location.search).get("token");
|
||||
if (token) {
|
||||
add = "?" + new URLSearchParams([["token", token]]).toString();
|
||||
}
|
||||
try {
|
||||
const response = await fetch(apiurl + "/auth/register", {
|
||||
const response = await fetch(apiurl + "/auth/register" + add, {
|
||||
body: JSON.stringify({
|
||||
date_of_birth: dateofbirth,
|
||||
email,
|
||||
|
|
116
src/webpage/rights.ts
Normal file
116
src/webpage/rights.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
import {I18n} from "./i18n.js";
|
||||
|
||||
class Rights {
|
||||
allow!: bigint;
|
||||
constructor(allow: string | number) {
|
||||
this.update(allow);
|
||||
}
|
||||
update(allow: string | number) {
|
||||
try {
|
||||
this.allow = BigInt(allow);
|
||||
} catch {
|
||||
this.allow = 875069521787904n;
|
||||
console.error(
|
||||
`Something really stupid happened with a permission with allow being ${allow}, execution will still happen, but something really stupid happened, please report if you know what caused this.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
getPermissionbit(b: number, big: bigint): boolean {
|
||||
return Boolean((big >> BigInt(b)) & 1n);
|
||||
}
|
||||
setPermissionbit(b: number, state: boolean, big: bigint): bigint {
|
||||
const bit = 1n << BigInt(b);
|
||||
return (big & ~bit) | (BigInt(state) << BigInt(b)); //thanks to geotale for this code :3
|
||||
}
|
||||
static *info(): Generator<{name: string; readableName: string; description: string}> {
|
||||
throw new Error("Isn't implemented");
|
||||
for (const thing of this.permisions) {
|
||||
yield {
|
||||
name: thing,
|
||||
readableName: I18n.getTranslation("permissions.readableNames." + thing),
|
||||
description: I18n.getTranslation("permissions.descriptions." + thing),
|
||||
};
|
||||
}
|
||||
}
|
||||
static readonly permisions = [
|
||||
"OPERATOR",
|
||||
"MANAGE_APPLICATIONS",
|
||||
"MANAGE_GUILDS",
|
||||
"MANAGE_MESSAGES",
|
||||
"MANAGE_RATE_LIMITS",
|
||||
"MANAGE_ROUTING",
|
||||
"MANAGE_TICKETS",
|
||||
"MANAGE_USERS",
|
||||
"ADD_MEMBERS",
|
||||
"BYPASS_RATE_LIMITS",
|
||||
"CREATE_APPLICATIONS",
|
||||
"CREATE_CHANNELS",
|
||||
"CREATE_DMS",
|
||||
"CREATE_DM_GROUPS",
|
||||
"CREATE_GUILDS",
|
||||
"CREATE_INVITES",
|
||||
"CREATE_ROLES",
|
||||
"CREATE_TEMPLATES",
|
||||
"CREATE_WEBHOOKS",
|
||||
"JOIN_GUILDS",
|
||||
"PIN_MESSAGES",
|
||||
"SELF_ADD_REACTIONS",
|
||||
"SELF_DELETE_MESSAGES",
|
||||
"SELF_EDIT_MESSAGES",
|
||||
"SELF_EDIT_NAME",
|
||||
"SEND_MESSAGES",
|
||||
"USE_ACTIVITIES",
|
||||
"USE_VIDEO",
|
||||
"USE_VOICE",
|
||||
"INVITE_USERS",
|
||||
"SELF_DELETE_DISABLE",
|
||||
"DEBTABLE",
|
||||
"CREDITABLE",
|
||||
"KICK_BAN_MEMBERS",
|
||||
"SELF_LEAVE_GROUPS",
|
||||
"PRESENCE",
|
||||
"SELF_ADD_DISCOVERABLE",
|
||||
"MANAGE_GUILD_DIRECTORY",
|
||||
"POGGERS",
|
||||
"USE_ACHIEVEMENTS",
|
||||
"INITIATE_INTERACTIONS",
|
||||
"RESPOND_TO_INTERACTIONS",
|
||||
"SEND_BACKDATED_EVENTS",
|
||||
"USE_MASS_INVITES",
|
||||
"ACCEPT_INVITES",
|
||||
"SELF_EDIT_FLAGS",
|
||||
"EDIT_FLAGS",
|
||||
"MANAGE_GROUPS",
|
||||
"VIEW_SERVER_STATS",
|
||||
"RESEND_VERIFICATION_EMAIL",
|
||||
"CREATE_REGISTRATION_TOKENS",
|
||||
];
|
||||
getPermission(name: string): boolean {
|
||||
if (undefined === Rights.permisions.indexOf(name)) {
|
||||
console.error(name + " is not found in map", Rights.permisions);
|
||||
}
|
||||
return this.getPermissionbit(Rights.permisions.indexOf(name), this.allow);
|
||||
}
|
||||
hasPermission(name: string, adminOverride = true): boolean {
|
||||
if (this.getPermissionbit(Rights.permisions.indexOf(name), this.allow)) return true;
|
||||
if (name !== "OPERATOR" && adminOverride) return this.hasPermission("OPERATOR");
|
||||
return false;
|
||||
}
|
||||
setPermission(name: string, setto: number): void {
|
||||
const bit = Rights.permisions.indexOf(name);
|
||||
if (bit === undefined) {
|
||||
return console.error(
|
||||
"Tried to set permission to " + setto + " for " + name + " but it doesn't exist",
|
||||
);
|
||||
}
|
||||
|
||||
if (setto === 0) {
|
||||
this.allow = this.setPermissionbit(bit, false, this.allow);
|
||||
} else if (setto === 1) {
|
||||
this.allow = this.setPermissionbit(bit, true, this.allow);
|
||||
} else {
|
||||
console.error("invalid number entered:" + setto);
|
||||
}
|
||||
}
|
||||
}
|
||||
export {Rights};
|
|
@ -4,6 +4,11 @@ import {Guild} from "./guild.js";
|
|||
import {SnowFlake} from "./snowflake.js";
|
||||
import {rolesjson} from "./jsontypes.js";
|
||||
import {Search} from "./search.js";
|
||||
import {OptionsElement, Buttons} from "./settings.js";
|
||||
import {Contextmenu} from "./contextmenu.js";
|
||||
import {Channel} from "./channel.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
|
||||
class Role extends SnowFlake {
|
||||
permissions: Permissions;
|
||||
owner: Guild;
|
||||
|
@ -135,10 +140,7 @@ class PermissionToggle implements OptionsElement<number> {
|
|||
}
|
||||
submit() {}
|
||||
}
|
||||
import {OptionsElement, Buttons} from "./settings.js";
|
||||
import {Contextmenu} from "./contextmenu.js";
|
||||
import {Channel} from "./channel.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
|
||||
class RoleList extends Buttons {
|
||||
permissions: [Role, Permissions][];
|
||||
permission: Permissions;
|
||||
|
|
|
@ -10,6 +10,7 @@ import {Search} from "./search.js";
|
|||
import {I18n} from "./i18n.js";
|
||||
import {Direct} from "./direct.js";
|
||||
import {Hover} from "./hover.js";
|
||||
import {Dialog} from "./settings.js";
|
||||
|
||||
class User extends SnowFlake {
|
||||
owner: Localuser;
|
||||
|
@ -69,6 +70,10 @@ class User extends SnowFlake {
|
|||
"totp_secret",
|
||||
"webauthn_enabled",
|
||||
]);
|
||||
if (!this.localuser.rights.getPermission("OPERATOR")) {
|
||||
//Unless the user is an operator, we really shouldn't ever see this
|
||||
bad.add("rights");
|
||||
}
|
||||
for (const thing of bad) {
|
||||
if (json.hasOwnProperty(thing)) {
|
||||
console.error(thing + " should not be exposed to the client");
|
||||
|
@ -346,6 +351,36 @@ class User extends SnowFlake {
|
|||
navigator.clipboard.writeText(this.id);
|
||||
},
|
||||
);
|
||||
|
||||
this.contextmenu.addSeperator();
|
||||
|
||||
this.contextmenu.addButton(
|
||||
() => I18n.user.instanceBan(),
|
||||
function (this: User) {
|
||||
const menu = new Dialog("");
|
||||
const options = menu.float.options;
|
||||
options.addTitle(I18n.user.confirmInstBan(this.name));
|
||||
const opt = options.addOptions("", {ltr: true});
|
||||
opt.addButtonInput("", I18n.yes(), () => {
|
||||
fetch(this.info.api + "/users/" + this.id, {
|
||||
headers: this.localuser.headers,
|
||||
method: "POST",
|
||||
});
|
||||
menu.hide();
|
||||
});
|
||||
opt.addButtonInput("", I18n.no(), () => {
|
||||
menu.hide();
|
||||
});
|
||||
menu.show();
|
||||
},
|
||||
{
|
||||
visable: function () {
|
||||
return this.localuser.rights.hasPermission("MANAGE_USERS");
|
||||
},
|
||||
color: "red",
|
||||
},
|
||||
);
|
||||
console.warn("this ran");
|
||||
}
|
||||
|
||||
static checkuser(user: User | userjson, owner: Localuser): User {
|
||||
|
@ -461,6 +496,14 @@ class User extends SnowFlake {
|
|||
}
|
||||
(this as any)[key] = (json as any)[key];
|
||||
}
|
||||
if ("rights" in this) {
|
||||
if (
|
||||
this === this.localuser.user &&
|
||||
(typeof this.rights == "string" || typeof this.rights == "number")
|
||||
) {
|
||||
this.localuser.updateRights(this.rights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
|
||||
|
|
|
@ -321,7 +321,26 @@
|
|||
"areYouSureDelete": "Are you sure you want to delete your account? If so enter the phrase $1",
|
||||
"sillyDeleteConfirmPhrase": "Shrek is love, Shrek is life",
|
||||
"deleteAccountButton": "Delete account",
|
||||
"mustTypePhrase": "To delete your account you must type the phrase"
|
||||
"mustTypePhrase": "To delete your account you must type the phrase",
|
||||
"manageInstance": "Manage Instance"
|
||||
},
|
||||
"manageInstance": {
|
||||
"stop": "Stop instance",
|
||||
"AreYouSureStop": "Are you sure you want to stop this instance?",
|
||||
"createTokens": "Create Registration Tokens",
|
||||
"count": "Count:",
|
||||
"length": "Length:",
|
||||
"format": "Format:",
|
||||
"TokenFormats": {
|
||||
"plain": "Plain",
|
||||
"JSON": "JSON formatted",
|
||||
"URLs": "Invite URLs"
|
||||
},
|
||||
"create": "Create",
|
||||
"clientURL": "Client URL:",
|
||||
"regType": "Register token URL type",
|
||||
"genericType": "Generic",
|
||||
"copy": "Copy"
|
||||
},
|
||||
"message": {
|
||||
"reactionAdd": "Add reaction",
|
||||
|
@ -403,7 +422,9 @@
|
|||
"ban": "Ban member",
|
||||
"addRole": "Add roles",
|
||||
"removeRole": "Remove roles",
|
||||
"editServerProfile": "Edit server profile"
|
||||
"editServerProfile": "Edit server profile",
|
||||
"instanceBan": "Instance ban",
|
||||
"confirmInstBan": "Are you sure you want to instance ban $1?"
|
||||
},
|
||||
"login": {
|
||||
"checking": "Checking Instance",
|
||||
|
@ -453,6 +474,8 @@
|
|||
"name:": "Name:",
|
||||
"confirmDel": "Are you sure you want to delete this emoji?"
|
||||
},
|
||||
"incorrectURLS": "## This instance has likely sent the incorrect URLs.\n### If you're the instance owner please see [here](https://docs.spacebar.chat/setup/server/) under *Connecting from remote machines* to correct the issue.\n Would you like Jank Client to automatically try to fix this error to let you connect to the instance?",
|
||||
|
||||
"uploadFilesText": "Upload your files here!",
|
||||
"errorReconnect": "Unable to connect to the server, retrying in **$1** seconds...",
|
||||
"retrying": "Retrying...",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue