2023-01-14 09:51:46 +01:00
|
|
|
import {USER_GROUP_ADMIN} from "./constants";
|
2023-01-19 18:12:16 +01:00
|
|
|
import {createDownload, isInt} from "./util";
|
|
|
|
|
|
|
|
Date.prototype.toJSON = function() {
|
|
|
|
return Math.round(this.getTime() / 1000);
|
|
|
|
};
|
2023-01-14 09:51:46 +01:00
|
|
|
|
2020-06-14 12:38:35 +02:00
|
|
|
export default class API {
|
|
|
|
constructor() {
|
2020-06-14 22:35:01 +02:00
|
|
|
this.loggedIn = false;
|
2023-01-07 15:34:05 +01:00
|
|
|
this.user = null;
|
|
|
|
this.session = null;
|
2023-01-25 14:15:34 +01:00
|
|
|
this.language = { id: 1, code: "en_US", shortCode: "en", name: "English (US)" };
|
2023-01-14 09:51:46 +01:00
|
|
|
this.permissions = [];
|
2020-06-14 22:35:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
csrfToken() {
|
2023-01-07 15:34:05 +01:00
|
|
|
return this.loggedIn ? this.session.csrfToken : null;
|
2020-06-14 22:35:01 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 18:12:16 +01:00
|
|
|
async apiCall(method, params, expectBinary=false) {
|
2020-06-14 22:35:01 +02:00
|
|
|
params = params || { };
|
2023-01-15 00:32:17 +01:00
|
|
|
const csrfToken = this.csrfToken();
|
|
|
|
const config = {method: 'post'};
|
|
|
|
if (params instanceof FormData) {
|
|
|
|
if (csrfToken) {
|
|
|
|
params.append("csrfToken", csrfToken);
|
|
|
|
}
|
|
|
|
config.body = params;
|
|
|
|
} else {
|
|
|
|
if (csrfToken) {
|
|
|
|
params.csrfToken = csrfToken;
|
|
|
|
}
|
|
|
|
config.headers = {'Content-Type': 'application/json'};
|
|
|
|
config.body = JSON.stringify(params);
|
|
|
|
}
|
2020-06-14 22:35:01 +02:00
|
|
|
|
2023-01-15 00:32:17 +01:00
|
|
|
let response = await fetch("/api/" + method, config);
|
2023-01-19 18:12:16 +01:00
|
|
|
if (response.headers.has("content-disposition")) {
|
|
|
|
let contentDisposition = response.headers.get("content-disposition");
|
|
|
|
if (contentDisposition.toLowerCase().startsWith("attachment;")) {
|
|
|
|
let fileName = /filename="?([^"]*)"?/;
|
|
|
|
let blob = await response.blob();
|
|
|
|
createDownload(fileName.exec(contentDisposition)[1], blob);
|
|
|
|
return { success: true, msg: "" };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 01:03:35 +02:00
|
|
|
let res = await response.json();
|
|
|
|
if (!res.success && res.msg === "You are not logged in.") {
|
2023-01-05 22:47:17 +01:00
|
|
|
this.loggedIn = false;
|
2023-02-09 23:55:30 +01:00
|
|
|
this.user = null;
|
2020-06-23 01:03:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2020-06-14 12:38:35 +02:00
|
|
|
}
|
|
|
|
|
2023-01-14 09:51:46 +01:00
|
|
|
hasPermission(method) {
|
|
|
|
if (!this.permissions) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const permission of this.permissions) {
|
2023-01-15 00:32:17 +01:00
|
|
|
if (method.endsWith("*") && permission.toLowerCase().startsWith(method.toLowerCase().substring(0, method.length - 1))) {
|
2023-01-14 09:51:46 +01:00
|
|
|
return true;
|
|
|
|
} else if (method.toLowerCase() === permission.toLowerCase()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hasGroup(groupIdOrName) {
|
|
|
|
if (this.loggedIn && this.user?.groups) {
|
2023-01-15 00:32:17 +01:00
|
|
|
if (isInt(groupIdOrName)) {
|
2023-01-14 09:51:46 +01:00
|
|
|
return this.user.groups.hasOwnProperty(groupIdOrName);
|
|
|
|
} else {
|
|
|
|
let userGroups = Object.values(this.user.groups);
|
|
|
|
return userGroups.includes(groupIdOrName);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isAdmin() {
|
|
|
|
return this.hasGroup(USER_GROUP_ADMIN);
|
|
|
|
}
|
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** Info **/
|
|
|
|
async info() {
|
|
|
|
return this.apiCall("info");
|
|
|
|
}
|
|
|
|
|
|
|
|
/** UserAPI **/
|
|
|
|
async login(username, password, rememberMe=false) {
|
2023-01-16 21:47:23 +01:00
|
|
|
let res = await this.apiCall("user/login", { username: username, password: password, stayLoggedIn: rememberMe });
|
|
|
|
if (res.success) {
|
|
|
|
this.loggedIn = true;
|
|
|
|
this.session = res.session;
|
|
|
|
this.user = res.user;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2022-11-29 14:17:11 +01:00
|
|
|
}
|
|
|
|
|
2020-06-14 12:38:35 +02:00
|
|
|
async fetchUser() {
|
2023-01-16 21:47:23 +01:00
|
|
|
let res = await this.apiCall("user/info");
|
|
|
|
if (res.success) {
|
|
|
|
this.loggedIn = res.loggedIn;
|
|
|
|
this.language = res.language;
|
|
|
|
this.permissions = (res.permissions || []).map(s => s.toLowerCase());
|
2023-01-07 15:34:05 +01:00
|
|
|
if (this.loggedIn) {
|
2023-01-16 21:47:23 +01:00
|
|
|
this.session = res.session;
|
|
|
|
this.user = res.user;
|
2023-01-07 15:34:05 +01:00
|
|
|
} else {
|
|
|
|
this.session = null;
|
|
|
|
this.user = null;
|
|
|
|
}
|
2022-11-29 14:17:11 +01:00
|
|
|
}
|
2023-01-16 21:47:23 +01:00
|
|
|
return res;
|
2020-06-14 12:38:35 +02:00
|
|
|
}
|
|
|
|
|
2020-07-02 00:47:45 +02:00
|
|
|
async editUser(id, username, email, password, groups, confirmed) {
|
2022-11-29 14:17:11 +01:00
|
|
|
return this.apiCall("user/edit", {
|
|
|
|
id: id, username: username, email: email,
|
|
|
|
password: password, groups: groups, confirmed: confirmed
|
|
|
|
});
|
2020-06-23 20:57:54 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 12:38:35 +02:00
|
|
|
async logout() {
|
2022-12-01 01:28:38 +01:00
|
|
|
const res = await this.apiCall("user/logout");
|
|
|
|
if (res.success) {
|
|
|
|
this.loggedIn = false;
|
2023-01-14 09:51:46 +01:00
|
|
|
this.permissions = [];
|
|
|
|
this.session = null;
|
|
|
|
this.user = null;
|
2022-12-01 01:28:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2020-06-14 12:38:35 +02:00
|
|
|
}
|
2020-06-15 00:00:15 +02:00
|
|
|
|
2020-06-23 16:26:04 +02:00
|
|
|
async getUser(id) {
|
|
|
|
return this.apiCall("user/get", { id: id });
|
|
|
|
}
|
|
|
|
|
2020-06-23 22:24:56 +02:00
|
|
|
async deleteUser(id) {
|
|
|
|
return this.apiCall("user/delete", { id: id });
|
|
|
|
}
|
|
|
|
|
2023-01-05 22:47:17 +01:00
|
|
|
async fetchUsers(pageNum = 1, count = 20, orderBy = 'id', sortOrder = 'asc') {
|
|
|
|
return this.apiCall("user/fetch", { page: pageNum, count: count, orderBy: orderBy, sortOrder: sortOrder });
|
|
|
|
}
|
|
|
|
|
|
|
|
async fetchGroups(pageNum = 1, count = 20, orderBy = 'id', sortOrder = 'asc') {
|
|
|
|
return this.apiCall("groups/fetch", { page: pageNum, count: count, orderBy: orderBy, sortOrder: sortOrder });
|
2020-06-15 20:07:43 +02:00
|
|
|
}
|
2020-06-15 21:14:59 +02:00
|
|
|
|
2023-01-05 22:47:17 +01:00
|
|
|
async getGroup(id) {
|
|
|
|
return this.apiCall("groups/get", { id: id });
|
2020-06-15 21:14:59 +02:00
|
|
|
}
|
2020-06-17 14:30:37 +02:00
|
|
|
|
|
|
|
async inviteUser(username, email) {
|
|
|
|
return this.apiCall("user/invite", { username: username, email: email });
|
|
|
|
}
|
|
|
|
|
|
|
|
async createUser(username, email, password, confirmPassword) {
|
|
|
|
return this.apiCall("user/create", { username: username, email: email, password: password, confirmPassword: confirmPassword });
|
|
|
|
}
|
2020-06-17 23:50:08 +02:00
|
|
|
|
2023-01-16 21:47:23 +01:00
|
|
|
async updateProfile(username=null, fullName=null, password=null, confirmPassword = null, oldPassword = null) {
|
2023-01-18 14:37:34 +01:00
|
|
|
let res = await this.apiCall("user/updateProfile", { username: username, fullName: fullName,
|
2023-01-16 21:47:23 +01:00
|
|
|
password: password, confirmPassword: confirmPassword, oldPassword: oldPassword });
|
2023-01-18 14:37:34 +01:00
|
|
|
|
|
|
|
if (res.success) {
|
|
|
|
if (username !== null) {
|
|
|
|
this.user.name = username;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fullName !== null) {
|
|
|
|
this.user.fullName = fullName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
async uploadPicture(file, scale=1.0) {
|
|
|
|
const formData = new FormData();
|
|
|
|
formData.append("scale", scale);
|
|
|
|
formData.append("picture", file, file.name);
|
|
|
|
let res = await this.apiCall("user/uploadPicture", formData);
|
|
|
|
if (res.success) {
|
|
|
|
this.user.profilePicture = res.profilePicture;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
async removePicture() {
|
|
|
|
let res = await this.apiCall("user/removePicture");
|
|
|
|
if (res.success) {
|
|
|
|
this.user.profilePicture = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** Stats **/
|
2020-06-17 23:50:08 +02:00
|
|
|
async getStats() {
|
|
|
|
return this.apiCall("stats");
|
|
|
|
}
|
2020-06-19 13:13:13 +02:00
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** RoutesAPI **/
|
2020-06-19 13:13:13 +02:00
|
|
|
async getRoutes() {
|
|
|
|
return this.apiCall("routes/fetch");
|
|
|
|
}
|
2020-06-19 16:37:44 +02:00
|
|
|
|
|
|
|
async saveRoutes(routes) {
|
2020-06-24 01:09:08 +02:00
|
|
|
return this.apiCall("routes/save", { routes: routes });
|
|
|
|
}
|
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** GroupAPI **/
|
2020-06-24 01:09:08 +02:00
|
|
|
async createGroup(name, color) {
|
|
|
|
return this.apiCall("groups/create", { name: name, color: color });
|
2020-06-19 16:37:44 +02:00
|
|
|
}
|
2020-06-24 01:23:37 +02:00
|
|
|
|
|
|
|
async deleteGroup(id) {
|
2022-06-20 19:52:31 +02:00
|
|
|
return this.apiCall("groups/delete", { id: id });
|
2020-06-24 01:23:37 +02:00
|
|
|
}
|
2020-06-26 01:47:43 +02:00
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** SettingsAPI **/
|
2020-06-26 01:47:43 +02:00
|
|
|
async getSettings(key = "") {
|
|
|
|
return this.apiCall("settings/get", { key: key });
|
|
|
|
}
|
2020-06-26 14:58:17 +02:00
|
|
|
|
|
|
|
async saveSettings(settings) {
|
|
|
|
return this.apiCall("settings/set", { settings: settings });
|
|
|
|
}
|
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** MailAPI **/
|
2020-06-26 14:58:17 +02:00
|
|
|
async sendTestMail(receiver) {
|
2020-06-27 22:47:12 +02:00
|
|
|
return this.apiCall("mail/test", { receiver: receiver });
|
|
|
|
}
|
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** PermissionAPI **/
|
2020-06-27 22:47:12 +02:00
|
|
|
async fetchPermissions() {
|
|
|
|
return this.apiCall("permission/fetch");
|
|
|
|
}
|
|
|
|
|
|
|
|
async savePermissions(permissions) {
|
|
|
|
return this.apiCall("permission/save", { permissions: permissions });
|
2020-06-26 14:58:17 +02:00
|
|
|
}
|
2020-07-01 21:10:25 +02:00
|
|
|
|
2022-11-29 14:17:11 +01:00
|
|
|
/** VisitorsAPI **/
|
2020-07-01 21:10:25 +02:00
|
|
|
async getVisitors(type, date) {
|
|
|
|
return this.apiCall("visitors/stats", { type: type, date: date });
|
|
|
|
}
|
2022-11-29 14:17:11 +01:00
|
|
|
|
|
|
|
/** LanguageAPI **/
|
|
|
|
async getLanguages() {
|
|
|
|
return this.apiCall("language/get");
|
|
|
|
}
|
|
|
|
|
2022-11-30 16:42:24 +01:00
|
|
|
async setLanguage(params) {
|
2023-01-16 21:47:23 +01:00
|
|
|
let res = await this.apiCall("language/set", params);
|
|
|
|
if (res.success) {
|
|
|
|
this.language = res.language;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2022-11-30 16:42:24 +01:00
|
|
|
}
|
|
|
|
|
2022-11-30 23:15:52 +01:00
|
|
|
async getLanguageEntries(modules, code=null, useCache=false) {
|
2022-11-30 16:42:24 +01:00
|
|
|
if (!Array.isArray(modules)) {
|
|
|
|
modules = [modules];
|
|
|
|
}
|
|
|
|
|
2022-11-30 23:15:52 +01:00
|
|
|
return this.apiCall("language/getEntries", {code: code, modules: modules});
|
|
|
|
}
|
2022-11-30 16:42:24 +01:00
|
|
|
|
2023-01-14 09:51:46 +01:00
|
|
|
/** ApiKeyAPI **/
|
2023-01-18 14:37:34 +01:00
|
|
|
async getApiKeys(showActiveOnly = false, page = 1, count = 25, orderBy = "validUntil", sortOrder = "desc") {
|
2023-01-20 12:16:18 +01:00
|
|
|
return this.apiCall("apiKey/fetch", { showActiveOnly: showActiveOnly, page: page, count: count, orderBy: orderBy, sortOrder: sortOrder });
|
2023-01-14 09:51:46 +01:00
|
|
|
}
|
2022-11-30 16:42:24 +01:00
|
|
|
|
2023-01-14 09:51:46 +01:00
|
|
|
async createApiKey() {
|
|
|
|
return this.apiCall("apiKey/create");
|
|
|
|
}
|
|
|
|
|
|
|
|
async revokeKey(id) {
|
|
|
|
return this.apiCall("apiKey/revoke", { id: id });
|
|
|
|
}
|
2023-01-16 21:47:23 +01:00
|
|
|
|
|
|
|
/** 2FA API **/
|
|
|
|
async confirmTOTP(code) {
|
2023-01-18 14:37:34 +01:00
|
|
|
let res = await this.apiCall("tfa/confirmTotp", { code: code });
|
|
|
|
if (res.success) {
|
|
|
|
this.user.twoFactorToken = { type: "totp", confirmed: true };
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async remove2FA(password) {
|
2023-01-18 14:37:34 +01:00
|
|
|
let res = await this.apiCall("tfa/remove", { password: password });
|
|
|
|
if (res.success) {
|
|
|
|
this.user.twoFactorToken = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async verifyTotp2FA(code) {
|
|
|
|
return this.apiCall("tfa/verifyTotp", { code: code });
|
|
|
|
}
|
|
|
|
|
|
|
|
async verifyKey2FA(credentialID, clientDataJSON, authData, signature) {
|
|
|
|
return this.apiCall("tfa/verifyKey", { credentialID: credentialID, clientDataJSON: clientDataJSON, authData: authData, signature: signature })
|
|
|
|
}
|
|
|
|
|
|
|
|
async register2FA(clientDataJSON = null, attestationObject = null) {
|
2023-01-18 14:37:34 +01:00
|
|
|
let res = await this.apiCall("tfa/registerKey", { clientDataJSON: clientDataJSON, attestationObject: attestationObject });
|
|
|
|
if (res.success && res.twoFactorToken) {
|
|
|
|
this.user.twoFactorToken = res.twoFactorToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** GPG API **/
|
|
|
|
async uploadGPG(pubkey) {
|
2023-01-18 14:37:34 +01:00
|
|
|
let res = await this.apiCall("user/importGPG", { pubkey: pubkey });
|
|
|
|
if (res.success) {
|
|
|
|
this.user.gpgKey = res.gpgKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async confirmGpgToken(token) {
|
2023-01-18 16:36:29 +01:00
|
|
|
let res = await this.apiCall("user/confirmGPG", { token: token });
|
|
|
|
if (res.success) {
|
|
|
|
this.user.gpgKey.confirmed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async removeGPG(password) {
|
2023-01-18 16:36:29 +01:00
|
|
|
let res = await this.apiCall("user/removeGPG", { password: password });
|
|
|
|
if (res.success) {
|
|
|
|
this.user.gpgKey = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async downloadGPG(userId) {
|
|
|
|
return this.apiCall("user/downloadGPG", { id: userId }, true);
|
|
|
|
}
|
2024-03-25 18:37:08 +01:00
|
|
|
|
|
|
|
/** Log API **/
|
|
|
|
async fetchLogEntries(pageNum = 1, count = 20, orderBy = 'id', sortOrder = 'asc',
|
|
|
|
severity = "debug", since = null, query = "") {
|
|
|
|
return this.apiCall("logs/get", {
|
|
|
|
page: pageNum, count: count, orderBy: orderBy, sortOrder: sortOrder,
|
|
|
|
since: since, severity: severity, query: query
|
|
|
|
});
|
|
|
|
}
|
2020-06-14 12:38:35 +02:00
|
|
|
};
|