Merge branch 'dev' into reimpl-passyfire

This commit is contained in:
valerie 2024-05-05 17:01:53 -04:00 committed by GitHub
commit a18a770d5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 1232 additions and 1029 deletions

2
.gitconfig Normal file
View file

@ -0,0 +1,2 @@
[core]
hooksPath = .githooks/

6
.githooks/pre-commit Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
shopt -s globstar
"$(git rev-parse --show-toplevel)"/api/node_modules/.bin/prettier --ignore-unknown --write $(git rev-parse --show-toplevel)/api/src/**/*.ts
rustfmt $(git rev-parse --show-toplevel)/gui/src/**/*.rs
git update-index --again
exit 0

16
.prettierrc Normal file
View file

@ -0,0 +1,16 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "always",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}

16
api/package-lock.json generated
View file

@ -21,6 +21,7 @@
"@types/ssh2": "^1.15.0",
"@types/ws": "^8.5.10",
"nodemon": "^3.0.3",
"prettier": "^3.2.5",
"prisma": "^5.13.0",
"typescript": "^5.3.3"
}
@ -1288,6 +1289,21 @@
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz",
"integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="
},
"node_modules/prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prisma": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz",

View file

@ -19,6 +19,7 @@
"@types/ssh2": "^1.15.0",
"@types/ws": "^8.5.10",
"nodemon": "^3.0.3",
"prettier": "^3.2.5",
"prisma": "^5.13.0",
"typescript": "^5.3.3"
},

View file

@ -1,19 +1,19 @@
export type ParameterReturnedValue = {
success: boolean,
message?: string
}
success: boolean;
message?: string;
};
export type ForwardRule = {
sourceIP: string,
sourcePort: number,
destPort: number
sourceIP: string;
sourcePort: number;
destPort: number;
};
export type ConnectedClient = {
ip: string,
port: number,
ip: string;
port: number;
connectionDetails: ForwardRule
connectionDetails: ForwardRule;
};
export class BackendBaseClass {
@ -29,8 +29,18 @@ export class BackendBaseClass {
this.state = "stopped";
}
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
addConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): void {}
removeConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): void {}
async start(): Promise<boolean> {
return true;
@ -38,22 +48,27 @@ export class BackendBaseClass {
async stop(): Promise<boolean> {
return true;
};
}
getAllConnections(): ConnectedClient[] {
if (this.clients == null) return [];
return this.clients;
};
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
return {
success: true
}
static checkParametersConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): ParameterReturnedValue {
return {
success: true,
};
}
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
return {
success: true
}
success: true,
};
}
}

View file

@ -1,43 +1,51 @@
import { NodeSSH } from "node-ssh";
import { Socket } from "node:net";
import type { BackendBaseClass, ForwardRule, ConnectedClient, ParameterReturnedValue } from "./base.js";
import type {
BackendBaseClass,
ForwardRule,
ConnectedClient,
ParameterReturnedValue,
} from "./base.js";
type ForwardRuleExt = ForwardRule & {
enabled: boolean
}
enabled: boolean;
};
// Fight me (for better naming)
type BackendParsedProviderString = {
ip: string,
port: number,
ip: string;
port: number;
username: string,
privateKey: string
}
username: string;
privateKey: string;
};
function parseBackendProviderString(data: string): BackendParsedProviderString {
try {
JSON.parse(data);
} catch (e) {
throw new Error("Payload body is not JSON")
throw new Error("Payload body is not JSON");
}
const jsonData = JSON.parse(data);
if (typeof jsonData.ip != "string") throw new Error("IP field is not a string");
if (typeof jsonData.ip != "string")
throw new Error("IP field is not a string");
if (typeof jsonData.port != "number") throw new Error("Port is not a number");
if (typeof jsonData.username != "string") throw new Error("Username is not a string");
if (typeof jsonData.privateKey != "string") throw new Error("Private key is not a string");
if (typeof jsonData.username != "string")
throw new Error("Username is not a string");
if (typeof jsonData.privateKey != "string")
throw new Error("Private key is not a string");
return {
ip: jsonData.ip,
port: jsonData.port,
username: jsonData.username,
privateKey: jsonData.privateKey
}
privateKey: jsonData.privateKey,
};
}
export class SSHBackendProvider implements BackendBaseClass {
@ -76,7 +84,7 @@ export class SSHBackendProvider implements BackendBaseClass {
port: this.options.port,
username: this.options.username,
privateKey: this.options.privateKey
privateKey: this.options.privateKey,
});
} catch (e) {
this.logs.push(`Failed to start SSHBackendProvider! Error: '${e}'`);
@ -86,7 +94,7 @@ export class SSHBackendProvider implements BackendBaseClass {
this.sshInstance = null;
return false;
};
}
this.state = "started";
this.logs.push("Successfully started SSHBackendProvider.");
@ -109,25 +117,48 @@ export class SSHBackendProvider implements BackendBaseClass {
this.state = "stopped";
return true;
};
}
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
addConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): void {
const connectionCheck = SSHBackendProvider.checkParametersConnection(
sourceIP,
sourcePort,
destPort,
protocol,
);
if (!connectionCheck.success) throw new Error(connectionCheck.message);
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
const foundProxyEntry = this.proxies.find(
i =>
i.sourceIP == sourceIP &&
i.sourcePort == sourcePort &&
i.destPort == destPort,
);
if (foundProxyEntry) return;
(async () => {
await this.sshInstance.forwardIn("0.0.0.0", destPort, (info, accept, reject) => {
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
await this.sshInstance.forwardIn(
"0.0.0.0",
destPort,
(info, accept, reject) => {
const foundProxyEntry = this.proxies.find(
i =>
i.sourceIP == sourceIP &&
i.sourcePort == sourcePort &&
i.destPort == destPort,
);
if (!foundProxyEntry || !foundProxyEntry.enabled) return reject();
const client: ConnectedClient = {
ip: info.srcIP,
port: info.srcPort,
connectionDetails: foundProxyEntry
connectionDetails: foundProxyEntry,
};
this.clients.push(client);
@ -136,7 +167,7 @@ export class SSHBackendProvider implements BackendBaseClass {
srcConn.connect({
host: sourceIP,
port: sourcePort
port: sourcePort,
});
// Why is this so confusing
@ -151,7 +182,7 @@ export class SSHBackendProvider implements BackendBaseClass {
srcConn.end();
});
srcConn.on("data", (data) => {
srcConn.on("data", data => {
destConn.write(data);
});
@ -159,7 +190,8 @@ export class SSHBackendProvider implements BackendBaseClass {
this.clients.splice(this.clients.indexOf(client), 1);
destConn.close();
});
});
},
);
})();
this.proxies.push({
@ -167,34 +199,56 @@ export class SSHBackendProvider implements BackendBaseClass {
sourcePort,
destPort,
enabled: true
enabled: true,
});
};
}
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
removeConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): void {
const connectionCheck = SSHBackendProvider.checkParametersConnection(
sourceIP,
sourcePort,
destPort,
protocol,
);
if (!connectionCheck.success) throw new Error(connectionCheck.message);
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
const foundProxyEntry = this.proxies.find(
i =>
i.sourceIP == sourceIP &&
i.sourcePort == sourcePort &&
i.destPort == destPort,
);
if (!foundProxyEntry) return;
foundProxyEntry.enabled = false;
};
}
getAllConnections(): ConnectedClient[] {
return this.clients;
};
}
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
if (protocol == "udp") return {
static checkParametersConnection(
sourceIP: string,
sourcePort: number,
destPort: number,
protocol: "tcp" | "udp",
): ParameterReturnedValue {
if (protocol == "udp")
return {
success: false,
message: "SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)"
message:
"SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)",
};
return {
success: true
}
success: true,
};
}
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
try {
@ -203,12 +257,12 @@ export class SSHBackendProvider implements BackendBaseClass {
} catch (e: Error) {
return {
success: false,
message: e.toString()
}
message: e.toString(),
};
}
return {
success: true
}
success: true,
};
}
}

View file

@ -1,10 +1,14 @@
import process from "node:process";
import { PrismaClient } from '@prisma/client';
import { PrismaClient } from "@prisma/client";
import Fastify from "fastify";
import type { ServerOptions, SessionToken, RouteOptions } from "./libs/types.js";
import type { BackendBaseClass } from "./backendimpl/base.js";``
import type {
ServerOptions,
SessionToken,
RouteOptions,
} from "./libs/types.js";
import type { BackendBaseClass } from "./backendimpl/base.js";
import { route as getPermissions } from "./routes/getPermissions.js";
@ -31,17 +35,19 @@ const prisma = new PrismaClient();
const isSignupEnabled: boolean = Boolean(process.env.IS_SIGNUP_ENABLED);
const unsafeAdminSignup: boolean = Boolean(process.env.UNSAFE_ADMIN_SIGNUP);
const noUsersCheck: boolean = await prisma.user.count() == 0;
const noUsersCheck: boolean = (await prisma.user.count()) == 0;
if (unsafeAdminSignup) {
console.error("WARNING: You have admin sign up on! This means that anyone that signs up will have admin rights!");
console.error(
"WARNING: You have admin sign up on! This means that anyone that signs up will have admin rights!",
);
}
const serverOptions: ServerOptions = {
isSignupEnabled: isSignupEnabled ? true : noUsersCheck,
isSignupAsAdminEnabled: unsafeAdminSignup ? true : noUsersCheck,
allowUnsafeGlobalTokens: process.env.NODE_ENV != "production"
allowUnsafeGlobalTokens: process.env.NODE_ENV != "production",
};
const sessionTokens: Record<number, SessionToken[]> = {};
@ -58,7 +64,7 @@ const routeOptions: RouteOptions = {
tokens: sessionTokens,
options: serverOptions,
backends: backends
backends: backends,
};
console.log("Initializing forwarding rules...");
@ -96,7 +102,7 @@ userLogin(routeOptions);
try {
await fastify.listen({
port: 3000,
host: process.env.NODE_ENV == "production" ? "0.0.0.0" : "127.0.0.1"
host: process.env.NODE_ENV == "production" ? "0.0.0.0" : "127.0.0.1",
});
} catch (err) {
fastify.log.error(err);

View file

@ -11,7 +11,11 @@ type Backend = {
connectionDetails: string;
};
export async function backendInit(backend: Backend, backends: Record<number, BackendBaseClass>, prisma: PrismaClient): Promise<boolean> {
export async function backendInit(
backend: Backend,
backends: Record<number, BackendBaseClass>,
prisma: PrismaClient,
): Promise<boolean> {
const ourProvider = backendProviders[backend.backend];
if (!ourProvider) {
@ -24,7 +28,7 @@ export async function backendInit(backend: Backend, backends: Record<number, Bac
backends[backend.id] = new ourProvider(backend.connectionDetails);
const ourBackend = backends[backend.id];
if (!await ourBackend.start()) {
if (!(await ourBackend.start())) {
console.log(" - Error initializing backend!");
console.log(" - " + ourBackend.logs.join("\n - "));
@ -36,17 +40,24 @@ export async function backendInit(backend: Backend, backends: Record<number, Bac
const clients = await prisma.forwardRule.findMany({
where: {
destProviderID: backend.id,
enabled: true
}
enabled: true,
},
});
for (const client of clients) {
if (client.protocol != "tcp" && client.protocol != "udp") {
console.error(` - Error: Client with ID of '${client.id}' has an invalid protocol! (must be either TCP or UDP)`);
console.error(
` - Error: Client with ID of '${client.id}' has an invalid protocol! (must be either TCP or UDP)`,
);
continue;
}
ourBackend.addConnection(client.sourceIP, client.sourcePort, client.destPort, client.protocol);
ourBackend.addConnection(
client.sourceIP,
client.sourcePort,
client.destPort,
client.protocol,
);
}
return true;

View file

@ -27,19 +27,25 @@ export const permissionListDisabled: Record<string, boolean> = {
};
// FIXME: This solution fucking sucks.
export let permissionListEnabled: Record<string, boolean> = JSON.parse(JSON.stringify(permissionListDisabled));
export let permissionListEnabled: Record<string, boolean> = JSON.parse(
JSON.stringify(permissionListDisabled),
);
for (const index of Object.keys(permissionListEnabled)) {
permissionListEnabled[index] = true;
}
export async function hasPermission(permissionList: string[], uid: number, prisma: PrismaClient): Promise<boolean> {
export async function hasPermission(
permissionList: string[],
uid: number,
prisma: PrismaClient,
): Promise<boolean> {
for (const permission of permissionList) {
const permissionNode = await prisma.permission.findFirst({
where: {
userID: uid,
permission
}
permission,
},
});
if (!permissionNode || !permissionNode.has) return false;
@ -48,7 +54,11 @@ export async function hasPermission(permissionList: string[], uid: number, prism
return true;
}
export async function getUID(token: string, tokens: Record<number, SessionToken[]>, prisma: PrismaClient): Promise<number> {
export async function getUID(
token: string,
tokens: Record<number, SessionToken[]>,
prisma: PrismaClient,
): Promise<number> {
let userID = -1;
// Look up in our currently authenticated users
@ -59,7 +69,10 @@ export async function getUID(token: string, tokens: Record<number, SessionToken[
const otherToken = otherTokenList[otherTokenIndex];
if (otherToken.token == token) {
if (otherToken.expiresAt < otherToken.createdAt + (otherToken.createdAt - Date.now())) {
if (
otherToken.expiresAt <
otherToken.createdAt + (otherToken.createdAt - Date.now())
) {
otherTokenList.splice(parseInt(otherTokenIndex), 1);
continue;
} else {
@ -74,19 +87,24 @@ export async function getUID(token: string, tokens: Record<number, SessionToken[
if (userID == -1) {
const allUsers = await prisma.user.findMany({
where: {
isRootServiceAccount: true
}
isRootServiceAccount: true,
},
});
for (const user of allUsers) {
if (user.rootToken == token) userID = user.id;
};
}
}
return userID;
}
export async function hasPermissionByToken(permissionList: string[], token: string, tokens: Record<number, SessionToken[]>, prisma: PrismaClient): Promise<boolean> {
export async function hasPermissionByToken(
permissionList: string[],
token: string,
tokens: Record<number, SessionToken[]>,
prisma: PrismaClient,
): Promise<boolean> {
const userID = await getUID(token, tokens, prisma);
return await hasPermission(permissionList, userID, prisma);
}

View file

@ -8,21 +8,21 @@ export type ServerOptions = {
isSignupAsAdminEnabled: boolean;
allowUnsafeGlobalTokens: boolean;
}
};
// NOTE: Someone should probably use Redis for this, but this is fine...
export type SessionToken = {
createdAt: number,
expiresAt: number, // Should be (createdAt + (30 minutes))
createdAt: number;
expiresAt: number; // Should be (createdAt + (30 minutes))
token: string
token: string;
};
export type RouteOptions = {
fastify: FastifyInstance,
prisma: PrismaClient,
tokens: Record<number, SessionToken[]>,
fastify: FastifyInstance;
prisma: PrismaClient;
tokens: Record<number, SessionToken[]>;
options: ServerOptions,
backends: Record<number, BackendBaseClass>
options: ServerOptions;
backends: Record<number, BackendBaseClass>;
};

View file

@ -5,21 +5,21 @@ import { backendProviders } from "../../backendimpl/index.js";
import { backendInit } from "../../libs/backendInit.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new backend to use
*/
fastify.post("/api/v1/backends/create", {
fastify.post(
"/api/v1/backends/create",
{
schema: {
body: {
type: "object",
@ -29,42 +29,46 @@ export function route(routeOptions: RouteOptions) {
token: { type: "string" },
name: { type: "string" },
description: { type: "string" },
backend: { type: "string" }
}
}
}
}, async(req, res) => {
backend: { type: "string" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
name: string,
description?: string,
connectionDetails: any,
backend: string
token: string;
name: string;
description?: string;
connectionDetails: any;
backend: string;
} = req.body;
if (!await hasPermission(body.token, [
"backends.add"
])) {
if (!(await hasPermission(body.token, ["backends.add"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
if (!backendProviders[body.backend]) {
return res.status(400).send({
error: "Unknown/unsupported/deprecated backend!"
error: "Unknown/unsupported/deprecated backend!",
});
};
}
const connectionDetails = JSON.stringify(body.connectionDetails);
const connectionDetailsValidityCheck = backendProviders[body.backend].checkParametersBackendInstance(connectionDetails);
const connectionDetailsValidityCheck =
backendProviders[body.backend].checkParametersBackendInstance(
connectionDetails,
);
if (!connectionDetailsValidityCheck.success) {
return res.status(400).send({
error: connectionDetailsValidityCheck.message ?? "Unknown error while attempting to parse connectionDetails (it's on your side)"
error:
connectionDetailsValidityCheck.message ??
"Unknown error while attempting to parse connectionDetails (it's on your side)",
});
};
}
const backend = await prisma.desinationProvider.create({
data: {
@ -72,8 +76,8 @@ export function route(routeOptions: RouteOptions) {
description: body.description,
backend: body.backend,
connectionDetails: JSON.stringify(body.connectionDetails)
}
connectionDetails: JSON.stringify(body.connectionDetails),
},
});
const init = await backendInit(backend, backends, prisma);
@ -82,13 +86,14 @@ export function route(routeOptions: RouteOptions) {
// TODO: better error code
return res.status(504).send({
error: "Backend is created, but failed to initalize correctly",
id: backend.id
id: backend.id,
});
}
return {
success: true,
id: backend.id
id: backend.id,
};
});
},
);
}

View file

@ -2,21 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/backends/lookup", {
fastify.post(
"/api/v1/backends/lookup",
{
schema: {
body: {
type: "object",
@ -27,30 +27,33 @@ export function route(routeOptions: RouteOptions) {
id: { type: "number" },
name: { type: "string" },
description: { type: "string" },
backend: { type: "string" }
}
}
}
}, async(req, res) => {
backend: { type: "string" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id?: number,
name?: string,
description?: string,
backend?: string
token: string;
id?: number;
name?: string;
description?: string;
backend?: string;
} = req.body;
if (!await hasPermission(body.token, [
"backends.visible" // wtf?
])) {
if (
!(await hasPermission(body.token, [
"backends.visible", // wtf?
]))
) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const canSeeSecrets = await hasPermission(body.token, [
"backends.secretVis"
"backends.secretVis",
]);
const prismaBackends = await prisma.desinationProvider.findMany({
@ -58,21 +61,22 @@ export function route(routeOptions: RouteOptions) {
id: body.id,
name: body.name,
description: body.description,
backend: body.backend
}
backend: body.backend,
},
});
return {
success: true,
data: prismaBackends.map((i) => ({
data: prismaBackends.map(i => ({
name: i.name,
description: i.description,
backend: i.backend,
connectionDetails: canSeeSecrets ? i.connectionDetails : "",
logs: backends[i.id].logs
}))
}
});
logs: backends[i.id].logs,
})),
};
},
);
}

View file

@ -2,21 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/backends/remove", {
fastify.post(
"/api/v1/backends/remove",
{
schema: {
body: {
type: "object",
@ -24,48 +24,48 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
id: { type: "number" }
}
}
}
}, async(req, res) => {
id: { type: "number" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id: number
token: string;
id: number;
} = req.body;
if (!await hasPermission(body.token, [
"backends.remove"
])) {
if (!(await hasPermission(body.token, ["backends.remove"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
if (!backends[body.id]) {
return res.status(400).send({
error: "Backend not found"
error: "Backend not found",
});
};
}
// Unload the backend
if (!await backends[body.id].stop()) {
if (!(await backends[body.id].stop())) {
return res.status(400).send({
error: "Failed to stop backend! Please report this issue."
})
error: "Failed to stop backend! Please report this issue.",
});
}
delete backends[body.id];
await prisma.desinationProvider.delete({
where: {
id: body.id
}
id: body.id,
},
});
return {
success: true
}
});
success: true,
};
},
);
}

View file

@ -2,18 +2,18 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
fastify.post("/api/v1/forward/connections", {
fastify.post(
"/api/v1/forward/connections",
{
schema: {
body: {
type: "object",
@ -21,42 +21,44 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
id: { type: "number" }
}
}
}
}, async(req, res) => {
id: { type: "number" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id: number
token: string;
id: number;
} = req.body;
if (!await hasPermission(body.token, [
"routes.visibleConn"
])) {
if (!(await hasPermission(body.token, ["routes.visibleConn"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const forward = await prisma.forwardRule.findUnique({
where: {
id: body.id
}
id: body.id,
},
});
if (!forward) return res.status(400).send({
error: "Could not find forward entry"
if (!forward)
return res.status(400).send({
error: "Could not find forward entry",
});
if (!backends[forward.destProviderID]) return res.status(400).send({
error: "Backend not found"
if (!backends[forward.destProviderID])
return res.status(400).send({
error: "Backend not found",
});
return {
success: true,
data: backends[forward.destProviderID].getAllConnections()
}
})
data: backends[forward.destProviderID].getAllConnections(),
};
},
);
}

View file

@ -2,24 +2,33 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/forward/create", {
fastify.post(
"/api/v1/forward/create",
{
schema: {
body: {
type: "object",
required: ["token", "name", "protocol", "sourceIP", "sourcePort", "destinationPort", "providerID"],
required: [
"token",
"name",
"protocol",
"sourceIP",
"sourcePort",
"destinationPort",
"providerID",
],
properties: {
token: { type: "string" },
@ -35,52 +44,53 @@ export function route(routeOptions: RouteOptions) {
destinationPort: { type: "number" },
providerID: { type: "number" },
autoStart: { type: "boolean" }
}
}
}
}, async(req, res) => {
autoStart: { type: "boolean" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
token: string;
name: string,
description?: string,
name: string;
description?: string;
protocol: "tcp" | "udp",
protocol: "tcp" | "udp";
sourceIP: string,
sourcePort: number,
sourceIP: string;
sourcePort: number;
destinationPort: number,
destinationPort: number;
providerID: number,
providerID: number;
autoStart?: boolean
autoStart?: boolean;
} = req.body;
if (body.protocol != "tcp" && body.protocol != "udp") {
return res.status(400).send({
error: "Body protocol field must be either tcp or udp"
error: "Body protocol field must be either tcp or udp",
});
};
if (!await hasPermission(body.token, [
"routes.add"
])) {
return res.status(403).send({
error: "Unauthorized"
});
};
const lookupIDForDestProvider = await prisma.desinationProvider.findUnique({
where: {
id: body.providerID
}
if (!(await hasPermission(body.token, ["routes.add"]))) {
return res.status(403).send({
error: "Unauthorized",
});
}
const lookupIDForDestProvider =
await prisma.desinationProvider.findUnique({
where: {
id: body.providerID,
},
});
if (!lookupIDForDestProvider) return res.status(400).send({
error: "Could not find provider"
if (!lookupIDForDestProvider)
return res.status(400).send({
error: "Could not find provider",
});
const forwardRule = await prisma.forwardRule.create({
@ -96,13 +106,14 @@ export function route(routeOptions: RouteOptions) {
destPort: body.destinationPort,
destProviderID: body.providerID,
enabled: Boolean(body.autoStart)
}
enabled: Boolean(body.autoStart),
},
});
return {
success: true,
id: forwardRule.id
}
});
id: forwardRule.id,
};
},
);
}

View file

@ -2,20 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/forward/lookup", {
fastify.post(
"/api/v1/forward/lookup",
{
schema: {
body: {
type: "object",
@ -34,43 +35,46 @@ export function route(routeOptions: RouteOptions) {
destPort: { type: "number" },
providerID: { type: "number" },
autoStart: { type: "boolean" }
}
}
}
}, async(req, res) => {
autoStart: { type: "boolean" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
token: string;
id?: number,
name?: string,
description?: string,
id?: number;
name?: string;
description?: string;
protocol?: "tcp" | "udp",
protocol?: "tcp" | "udp";
sourceIP?: string,
sourcePort?: number,
sourceIP?: string;
sourcePort?: number;
destinationPort?: number,
destinationPort?: number;
providerID?: number,
autoStart?: boolean
providerID?: number;
autoStart?: boolean;
} = req.body;
if (body.protocol && body.protocol != "tcp" && body.protocol != "udp") {
return res.status(400).send({
error: "Protocol specified in body must be either 'tcp' or 'udp'"
})
error: "Protocol specified in body must be either 'tcp' or 'udp'",
});
}
if (!await hasPermission(body.token, [
"routes.visible" // wtf?
])) {
if (
!(await hasPermission(body.token, [
"routes.visible", // wtf?
]))
) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const forwardRules = await prisma.forwardRule.findMany({
where: {
@ -84,13 +88,13 @@ export function route(routeOptions: RouteOptions) {
destPort: body.destinationPort,
destProviderID: body.providerID,
enabled: body.autoStart
}
enabled: body.autoStart,
},
});
return {
success: true,
data: forwardRules.map((i) => ({
data: forwardRules.map(i => ({
id: i.id,
name: i.name,
description: i.description,
@ -101,8 +105,9 @@ export function route(routeOptions: RouteOptions) {
destPort: i.destPort,
providerID: i.destProviderID,
autoStart: i.enabled // TODO: Add enabled flag in here to see if we're running or not
}))
autoStart: i.enabled, // TODO: Add enabled flag in here to see if we're running or not
})),
};
});
},
);
}

View file

@ -2,20 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/forward/remove", {
fastify.post(
"/api/v1/forward/remove",
{
schema: {
body: {
type: "object",
@ -23,33 +24,33 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
id: { type: "number" }
}
}
}
}, async(req, res) => {
id: { type: "number" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id: number
token: string;
id: number;
} = req.body;
if (!await hasPermission(body.token, [
"routes.remove"
])) {
if (!(await hasPermission(body.token, ["routes.remove"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
await prisma.forwardRule.delete({
where: {
id: body.id
}
id: body.id,
},
});
return {
success: true
}
});
success: true,
};
},
);
}

View file

@ -2,21 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/forward/start", {
fastify.post(
"/api/v1/forward/start",
{
schema: {
body: {
type: "object",
@ -24,47 +24,54 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
id: { type: "number" }
}
id: { type: "number" },
},
}
}, async(req, res) => {
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id: number
token: string;
id: number;
} = req.body;
if (!await hasPermission(body.token, [
"routes.start"
])) {
if (!(await hasPermission(body.token, ["routes.start"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const forward = await prisma.forwardRule.findUnique({
where: {
id: body.id
}
id: body.id,
},
});
if (!forward) return res.status(400).send({
error: "Could not find forward entry"
if (!forward)
return res.status(400).send({
error: "Could not find forward entry",
});
if (!backends[forward.destProviderID]) return res.status(400).send({
error: "Backend not found"
if (!backends[forward.destProviderID])
return res.status(400).send({
error: "Backend not found",
});
// Other restrictions in place make it so that it MUST be either TCP or UDP
// @ts-ignore
const protocol: "tcp" | "udp" = forward.protocol;
backends[forward.destProviderID].addConnection(forward.sourceIP, forward.sourcePort, forward.destPort, protocol);
backends[forward.destProviderID].addConnection(
forward.sourceIP,
forward.sourcePort,
forward.destPort,
protocol,
);
return {
success: true
}
});
success: true,
};
},
);
}

View file

@ -2,21 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
backends
} = routeOptions;
const { fastify, prisma, tokens, backends } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new route to use
*/
fastify.post("/api/v1/forward/stop", {
fastify.post(
"/api/v1/forward/stop",
{
schema: {
body: {
type: "object",
@ -24,47 +24,54 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
id: { type: "number" }
}
id: { type: "number" },
},
}
}, async(req, res) => {
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id: number
token: string;
id: number;
} = req.body;
if (!await hasPermission(body.token, [
"routes.stop"
])) {
if (!(await hasPermission(body.token, ["routes.stop"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const forward = await prisma.forwardRule.findUnique({
where: {
id: body.id
}
id: body.id,
},
});
if (!forward) return res.status(400).send({
error: "Could not find forward entry"
if (!forward)
return res.status(400).send({
error: "Could not find forward entry",
});
if (!backends[forward.destProviderID]) return res.status(400).send({
error: "Backend not found"
if (!backends[forward.destProviderID])
return res.status(400).send({
error: "Backend not found",
});
// Other restrictions in place make it so that it MUST be either TCP or UDP
// @ts-ignore
const protocol: "tcp" | "udp" = forward.protocol;
backends[forward.destProviderID].removeConnection(forward.sourceIP, forward.sourcePort, forward.destPort, protocol);
backends[forward.destProviderID].removeConnection(
forward.sourceIP,
forward.sourcePort,
forward.destPort,
protocol,
);
return {
success: true
}
});
success: true,
};
},
);
}

View file

@ -2,52 +2,50 @@ import { hasPermission, getUID } from "../libs/permissions.js";
import type { RouteOptions } from "../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
/**
* Logs in to a user account.
*/
fastify.post("/api/v1/getPermissions", {
fastify.post(
"/api/v1/getPermissions",
{
schema: {
body: {
type: "object",
required: ["token"],
properties: {
token: { type: "string" }
}
}
}
}, async(req, res) => {
token: { type: "string" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string
token: string;
} = req.body;
const uid = await getUID(body.token, tokens, prisma);
if (!await hasPermission([
"permissions.see"
], uid, prisma)) {
if (!(await hasPermission(["permissions.see"], uid, prisma))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const permissionsRaw = await prisma.permission.findMany({
where: {
userID: uid
}
userID: uid,
},
});
return {
success: true,
// Get the ones that we have, and transform them into just their name
data: permissionsRaw.filter((i) => i.has).map((i) => i.permission)
}
});
data: permissionsRaw.filter(i => i.has).map(i => i.permission),
};
},
);
}

View file

@ -6,17 +6,14 @@ import { generateRandomData } from "../../libs/generateRandom.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens,
options
} = routeOptions;
const { fastify, prisma, tokens, options } = routeOptions;
/**
* Creates a new user account to use, only if it is enabled.
*/
fastify.post("/api/v1/users/create", {
fastify.post(
"/api/v1/users/create",
{
schema: {
body: {
type: "object",
@ -25,35 +22,36 @@ export function route(routeOptions: RouteOptions) {
properties: {
name: { type: "string" },
email: { type: "string" },
password: { type: "string" }
}
}
}
}, async(req, res) => {
password: { type: "string" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
name: string,
email: string,
password: string
name: string;
email: string;
password: string;
} = req.body;
if (!options.isSignupEnabled) {
return res.status(403).send({
error: "Signing up is not enabled at this time."
error: "Signing up is not enabled at this time.",
});
};
}
const userSearch = await prisma.user.findFirst({
where: {
email: body.email
}
email: body.email,
},
});
if (userSearch) {
return res.status(400).send({
error: "User already exists"
})
};
error: "User already exists",
});
}
const saltedPassword: string = await hash(body.password, 15);
@ -64,21 +62,25 @@ export function route(routeOptions: RouteOptions) {
permissions: {
create: [] as {
permission: string,
has: boolean
}[]
}
permission: string;
has: boolean;
}[],
},
};
// TODO: There's probably a faster way to pull this off, but I'm lazy
for (const permissionKey of Object.keys(permissionListEnabled)) {
if (options.isSignupAsAdminEnabled || (permissionKey.startsWith("routes") || permissionKey == "permissions.see")) {
if (
options.isSignupAsAdminEnabled ||
permissionKey.startsWith("routes") ||
permissionKey == "permissions.see"
) {
userData.permissions.create.push({
permission: permissionKey,
has: permissionListEnabled[permissionKey]
has: permissionListEnabled[permissionKey],
});
}
};
}
if (options.allowUnsafeGlobalTokens) {
// @ts-ignore
@ -88,14 +90,14 @@ export function route(routeOptions: RouteOptions) {
}
const userCreateResults = await prisma.user.create({
data: userData
data: userData,
});
// FIXME(?): Redundant checks
if (options.allowUnsafeGlobalTokens) {
return {
success: true,
token: userCreateResults.rootToken
token: userCreateResults.rootToken,
};
} else {
const generatedToken = generateRandomData();
@ -104,15 +106,16 @@ export function route(routeOptions: RouteOptions) {
tokens[userCreateResults.id].push({
createdAt: Date.now(),
expiresAt: Date.now() + (30 * 60_000),
expiresAt: Date.now() + 30 * 60_000,
token: generatedToken
token: generatedToken,
});
return {
success: true,
token: generatedToken
token: generatedToken,
};
};
});
}
},
);
}

View file

@ -4,16 +4,14 @@ import { generateRandomData } from "../../libs/generateRandom.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
/**
* Logs in to a user account.
*/
fastify.post("/api/v1/users/login", {
fastify.post(
"/api/v1/users/login",
{
schema: {
body: {
type: "object",
@ -21,31 +19,34 @@ export function route(routeOptions: RouteOptions) {
properties: {
email: { type: "string" },
password: { type: "string" }
}
}
}
}, async(req, res) => {
password: { type: "string" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
email: string,
password: string
email: string;
password: string;
} = req.body;
const userSearch = await prisma.user.findFirst({
where: {
email: body.email
}
email: body.email,
},
});
if (!userSearch) return res.status(403).send({
error: "Email or password is incorrect"
if (!userSearch)
return res.status(403).send({
error: "Email or password is incorrect",
});
const passwordIsValid = await compare(body.password, userSearch.password);
if (!passwordIsValid) return res.status(403).send({
error: "Email or password is incorrect"
if (!passwordIsValid)
return res.status(403).send({
error: "Email or password is incorrect",
});
const token = generateRandomData();
@ -53,14 +54,15 @@ export function route(routeOptions: RouteOptions) {
tokens[userSearch.id].push({
createdAt: Date.now(),
expiresAt: Date.now() + (30 * 60_000),
expiresAt: Date.now() + 30 * 60_000,
token
token,
});
return {
success: true,
token
}
});
token,
};
},
);
}

View file

@ -2,17 +2,18 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
fastify.post("/api/v1/users/lookup", {
fastify.post(
"/api/v1/users/lookup",
{
schema: {
body: {
type: "object",
@ -23,44 +24,44 @@ export function route(routeOptions: RouteOptions) {
id: { type: "number" },
name: { type: "string" },
email: { type: "string" },
isServiceAccount: { type: "boolean" }
}
}
}
}, async(req, res) => {
isServiceAccount: { type: "boolean" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
id?: number,
name?: string,
email?: string,
isServiceAccount?: boolean
token: string;
id?: number;
name?: string;
email?: string;
isServiceAccount?: boolean;
} = req.body;
if (!await hasPermission(body.token, [
"users.lookup"
])) {
if (!(await hasPermission(body.token, ["users.lookup"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
const users = await prisma.user.findMany({
where: {
id: body.id,
name: body.name,
email: body.email,
isRootServiceAccount: body.isServiceAccount
}
isRootServiceAccount: body.isServiceAccount,
},
});
return {
success: true,
data: users.map((i) => ({
data: users.map(i => ({
name: i.name,
email: i.email,
isServiceAccount: i.isRootServiceAccount
}))
}
});
isServiceAccount: i.isRootServiceAccount,
})),
};
},
);
}

View file

@ -2,20 +2,21 @@ import { hasPermissionByToken } from "../../libs/permissions.js";
import type { RouteOptions } from "../../libs/types.js";
export function route(routeOptions: RouteOptions) {
const {
fastify,
prisma,
tokens
} = routeOptions;
const { fastify, prisma, tokens } = routeOptions;
function hasPermission(token: string, permissionList: string[]): Promise<boolean> {
function hasPermission(
token: string,
permissionList: string[],
): Promise<boolean> {
return hasPermissionByToken(permissionList, token, tokens, prisma);
};
}
/**
* Creates a new backend to use
*/
fastify.post("/api/v1/users/remove", {
fastify.post(
"/api/v1/users/remove",
{
schema: {
body: {
type: "object",
@ -23,39 +24,39 @@ export function route(routeOptions: RouteOptions) {
properties: {
token: { type: "string" },
uid: { type: "number" }
}
}
}
}, async(req, res) => {
uid: { type: "number" },
},
},
},
},
async (req, res) => {
// @ts-ignore
const body: {
token: string,
uid: number
token: string;
uid: number;
} = req.body;
if (!await hasPermission(body.token, [
"users.remove"
])) {
if (!(await hasPermission(body.token, ["users.remove"]))) {
return res.status(403).send({
error: "Unauthorized"
error: "Unauthorized",
});
};
}
await prisma.permission.deleteMany({
where: {
userID: body.uid
}
userID: body.uid,
},
});
await prisma.user.delete({
where: {
id: body.uid
}
id: body.uid,
},
});
return {
success: true
}
});
success: true,
};
},
);
}

View file

@ -3,17 +3,22 @@ use serde::Deserialize;
use serde_json::json;
pub struct NextAPIClient {
pub url: String
pub url: String,
}
#[derive(Deserialize, Debug, Default)]
pub struct LoginResponse {
pub error: Option<String>,
pub token: Option<String>
pub token: Option<String>,
}
impl NextAPIClient {
pub fn login(&self, email: &str, password: &str, mut callback: impl 'static + Send + FnMut(LoginResponse)) {
pub fn login(
&self,
email: &str,
password: &str,
mut callback: impl 'static + Send + FnMut(LoginResponse),
) {
let json_data = json!({
"email": email,
"password": password
@ -24,7 +29,10 @@ impl NextAPIClient {
println!("{}", json_str);
let mut request = Request::post(self.url.clone() + "/api/v1/users/login", json_str.as_bytes().to_vec());
let mut request = Request::post(
self.url.clone() + "/api/v1/users/login",
json_str.as_bytes().to_vec(),
);
request.headers.insert("Content-Type", "application/json");
fetch(request, move |result: ehttp::Result<ehttp::Response>| {
@ -36,9 +44,7 @@ impl NextAPIClient {
}
pub fn new(url: String) -> NextAPIClient {
let api_client: NextAPIClient = NextAPIClient {
url
};
let api_client: NextAPIClient = NextAPIClient { url };
return api_client;
}

View file

@ -1,5 +1,5 @@
use std::sync::Arc;
use eframe::egui;
use std::sync::Arc;
use crate::api;
use crate::ApplicationState;
@ -21,18 +21,20 @@ pub fn main(state: &mut ApplicationState, api: &api::NextAPIClient, ctx: &eframe
if ui.button("Login").clicked() {
let token_clone = Arc::clone(&state.token);
api.login(state.username.as_str(), state.password.as_str(), Box::new(move |res: api::LoginResponse| {
match res.token {
api.login(
state.username.as_str(),
state.password.as_str(),
Box::new(move |res: api::LoginResponse| match res.token {
Some(x) => {
let mut token = token_clone.lock().unwrap();
*token = x;
},
}
None => {
let mut token = token_clone.lock().unwrap();
*token = "".to_string();
}
}
}));
}),
);
}
});
}

View file

@ -1,8 +1,8 @@
use std::sync::{Arc, Mutex};
use eframe::egui;
use std::sync::{Arc, Mutex};
mod components;
mod api;
mod components;
pub struct ApplicationState {
token: Arc<Mutex<String>>,
@ -27,7 +27,7 @@ fn main() -> Result<(), eframe::Error> {
// components/log_in.rs
username: "replace@gmail.com".to_owned(),
password: "replace123".to_owned()
password: "replace123".to_owned(),
};
eframe::run_simple_native("NextNet GUI", options, move |ctx, _frame| {

View file

@ -1,3 +1,5 @@
pushd api > /dev/null 2> /dev/null
source init.sh
git config --local include.path .gitconfig
popd > /dev/null 2> /dev/null

View file

@ -5,6 +5,7 @@
# gui/
cargo
rustc
rustfmt
# api/
nodejs