Skip to content

Commit

Permalink
refactor: update API controller to work with new tunnel service inter…
Browse files Browse the repository at this point in the history
…face
  • Loading branch information
fredriklindberg committed Sep 30, 2023
1 parent 9b1da5d commit 7030bac
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 210 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@types/koa-joi-router": "^8.0.5",
"@types/mocha": "^10.0.1",
"@types/node": "^20.5.0",
"@types/sinon": "^10.0.17",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ import {
} from '../utils/errors.js';
import KoaController from './koa-controller.js';
import ClusterService from '../cluster/index.js';
import Account from '../account/account.js';
import Tunnel from '../tunnel/tunnel.js';

class AdminApiController extends KoaController {
_name = 'Admin API'
public readonly _name: string = 'Admin API'

constructor(opts) {
const logger = Logger("admin-api");
private apiKey!: string;
private unauthAccess: boolean = false;
private accountService!: AccountService;
private _tunnelService!: TunnelService;
private _transportService!: TransportService;
private _clusterService!: ClusterService;

constructor(opts: any) {
const logger: any = Logger("admin-api");

super({...opts, logger: logger});
if (!opts.enable) {
logger.info({
message: `HTTP Admin API disabled`,
});
typeof opts.callback === 'function' && process.nextTick(opts.callback);
return super();
return;
}

super({...opts, logger: logger});
this.logger = logger;

this.apiKey = typeof opts.apiKey === 'string' &&
opts.apiKey?.length > 0 ? opts.apiKey : undefined;
this.unauthAccess = this.apiKey === undefined && opts.unauthAccess === true;
Expand All @@ -45,13 +51,11 @@ class AdminApiController extends KoaController {
logger.warn("Admin API resource disabled - no API key given");
return;
}

this.setRoutes((router) => this._initializeRoutes(router));
}

_initializeRoutes(router) {
protected _initializeRoutes(router: Router.Router) {

const handleError = async (ctx, next) => {
const handleError: Router.FullHandler = async (ctx, next) => {
if (!ctx.invalid) {
return next();
}
Expand Down Expand Up @@ -89,7 +93,7 @@ class AdminApiController extends KoaController {
}
};

const handleAdminAuth = (ctx, next) => {
const handleAdminAuth: Router.FullHandler = (ctx, next) => {
if (this.unauthAccess === true) {
return next();
} else if (this.apiKey != undefined) {
Expand All @@ -106,7 +110,7 @@ class AdminApiController extends KoaController {
}
};

const accountProps = (account) => {
const accountProps = (account: Account) => {
const {accountId, formatted} = account.getId();
return {
account_id: accountId,
Expand All @@ -118,29 +122,32 @@ class AdminApiController extends KoaController {
}
};

const tunnelProps = (tunnel, baseUrl) => {
const tunnelProps = (tunnel: Tunnel, baseUrl: String) => {
return {
tunnel_id: tunnel.id,
accound_id: tunnel.account,
transport: {
...tunnel.transport,
...tunnel.config.transport,
...this._transportService.getTransports(tunnel, baseUrl),
},
ingress: tunnel.ingress,
target: tunnel.target,
ingress: tunnel.config.ingress,
target: tunnel.config.target,
connection: {
connected: tunnel.state().connected,
peer: tunnel.state().peer,
connected_at: tunnel.state().connected_at,
disconnected_at: tunnel.state().disconnected_at,
alive_at: tunnel.state().alive_at,
connected: tunnel.state.connected,
connected_at: tunnel.state.connected_at,
disconnected_at: tunnel.state.disconnected_at,
alive_at: tunnel.state.alive_at,
},
connections: tunnel.state().connections,
created_at: tunnel.created_at,
updated_at: tunnel.updated_at,
connections: tunnel.state.connections,
created_at: tunnel.config.created_at,
updated_at: tunnel.config.updated_at,
}
};

const getBaseUrl = (req: any) => {
return req._exposrBaseUrl;
}

router.route({
method: 'post',
path: '/v1/admin/account',
Expand All @@ -166,7 +173,7 @@ class AdminApiController extends KoaController {
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const account = await this.accountService.get(ctx.params.account_id);
const account: Account = await this.accountService.get(ctx.params.account_id);
if (!account) {
ctx.status = 404;
ctx.body = {};
Expand All @@ -190,12 +197,12 @@ class AdminApiController extends KoaController {
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const res = await this.accountService.list(ctx.query.cursor, ctx.query.count, ctx.query.verbose);
const res = await this.accountService.list(<any>ctx.query.cursor, <any>ctx.query.count, <any>ctx.query.verbose);

ctx.status = 200;
ctx.body = {
cursor: res.cursor,
accounts: res.accounts.map((a) => { return accountProps(a); }),
accounts: res.accounts.map((a: Account) => { return accountProps(a); }),
};
}]
});
Expand Down Expand Up @@ -256,14 +263,23 @@ class AdminApiController extends KoaController {
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const tunnel = await this._tunnelService._get(ctx.params.tunnel_id);
if (!tunnel) {
ctx.body = {
error: ERROR_TUNNEL_NOT_FOUND,
};
} else {
try {
const tunnel = await this._tunnelService.lookup(ctx.params.tunnel_id);
ctx.status = 200;
ctx.body = tunnelProps(tunnel, ctx.req._exposrBaseUrl);
ctx.body = tunnelProps(tunnel, getBaseUrl(ctx.req));
} catch (e: any) {
if (e.message == 'no_such_tunnel') {
ctx.status = 404;
ctx.body = {
error: ERROR_TUNNEL_NOT_FOUND,
};
} else {
ctx.status = 500;
ctx.body = {
error: ERROR_TUNNEL_NOT_FOUND,
details: e.message,
};
}
}
}]
});
Expand All @@ -279,47 +295,48 @@ class AdminApiController extends KoaController {
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const tunnel = await this._tunnelService._get(ctx.params.tunnel_id);
if (!tunnel) {
ctx.body = {
error: ERROR_TUNNEL_NOT_FOUND,
};
}
const result = await this._tunnelService.delete(tunnel.id, tunnel.account);
if (result === false) {
ctx.status = 500;
try {
const tunnel = await this._tunnelService.lookup(ctx.params.tunnel_id);
const result = await this._tunnelService.delete(tunnel.id, tunnel.account);
if (result) {
ctx.status = 204;
} else {
ctx.status = 403;
}
} catch (e: any) {
ctx.status = 403;
ctx.body = {
error: ERROR_UNKNOWN_ERROR,
};
} else {
ctx.status = 204;
details: e.message,
}
}
}]
});

router.route({
method: 'post',
path: '/v1/admin/tunnel/:tunnel_id/disconnect/:connection_id?',
path: '/v1/admin/tunnel/:tunnel_id/disconnect',
validate: {
failure: 400,
continueOnError: true,
params: {
tunnel_id: Router.Joi.string().regex(TunnelService.TUNNEL_ID_REGEX).required(),
connection_id: Router.Joi.string().optional()
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const tunnel = await this._tunnelService._get(ctx.params.tunnel_id);
if (!tunnel) {
ctx.body = {
error: ERROR_TUNNEL_NOT_FOUND,
};
} else {
const res = await this._tunnelService._disconnect(tunnel, ctx.params.connection_id);
try {
const tunnel = await this._tunnelService.lookup(ctx.params.tunnel_id);
const res = await this._tunnelService.disconnect(tunnel.id, tunnel.account);
ctx.status = 200;
ctx.body = {
result: res
}

} catch (e:any) {
ctx.status = 403,
ctx.body = {
details: e.message
}
}
}]
});
Expand All @@ -337,12 +354,14 @@ class AdminApiController extends KoaController {
}
},
handler: [handleAdminAuth, handleError, async (ctx, next) => {
const res = await this._tunnelService.list(ctx.query.cursor, ctx.query.count, ctx.query.verbose);
const res = await this._tunnelService.list(<any>ctx.query.cursor, <any>ctx.query.count, <any>ctx.query.verbose);

ctx.status = 200;
ctx.body = {
cursor: res.cursor,
tunnels: res.tunnels.map((t) => { return ctx.query.verbose ? tunnelProps(t, ctx.req._exposrBaseUrl) : t; }),
tunnels: res.tunnels.map((t) => {
return ctx.query.verbose ? tunnelProps(t, getBaseUrl(ctx.req)) : t.id;
}),
};
}]
});
Expand Down Expand Up @@ -375,8 +394,8 @@ class AdminApiController extends KoaController {
});
}

async _destroy() {
return Promise.allSettled([
protected async _destroy(): Promise<void> {
Promise.allSettled([
this.accountService.destroy(),
this._tunnelService.destroy(),
this._transportService.destroy(),
Expand Down
51 changes: 0 additions & 51 deletions src/controller/admin-controller.js

This file was deleted.

53 changes: 53 additions & 0 deletions src/controller/admin-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Router } from 'koa-joi-router';
import { Logger } from '../logger.js';
import KoaController from "./koa-controller.js";

class AdminController extends KoaController {

public readonly _name: string = 'Admin'

public appReady: boolean | undefined;

constructor(opts: any) {
const logger: any = Logger("admin");

super({...opts, logger});
if (!opts.enable) {
logger.info({
message: `HTTP Admin disabled`,
});
return;
}

this.appReady = undefined;
}

public setReady(ready: boolean) {
ready ??= true;
this.appReady = ready;
}

protected _initializeRoutes(router: Router): void {
router.route({
method: 'get',
path: '/ping',
handler: async (ctx, next) => {
ctx.status = this.appReady != undefined ? 200 : 404;
},
});

router.route({
method: 'get',
path: '/health',
handler: async (ctx, next) => {
ctx.status = this.appReady ? 200 : 404;
},
});
}

protected async _destroy() {
this.appReady = undefined;
}
}

export default AdminController;
Loading

0 comments on commit 7030bac

Please sign in to comment.