Show the appservice configuration after saving; Make the show config button work

Includes the ability to 'test' the appservice. This doesn't bother checking if the URL is set up correctly because we don't actually care about the transactions.
This commit is contained in:
Travis Ralston 2018-03-24 12:18:38 -06:00
parent 656d208059
commit 62d81ed842
10 changed files with 170 additions and 5 deletions

View File

@ -4,6 +4,9 @@ import AppService from "../../db/models/AppService";
import { AppserviceStore } from "../../db/AppserviceStore";
import { ApiError } from "../ApiError";
import AppServiceUser from "../../db/models/AppServiceUser";
import { MatrixAppserviceClient } from "../../matrix/MatrixAppserviceClient";
import config from "../../config";
import { LogService } from "matrix-js-snippets";
interface AppserviceResponse {
id: string;
@ -37,6 +40,20 @@ export class AdminAppserviceService {
return (await AppService.findAll()).map(a => this.mapAppservice(a));
}
@GET
@Path(":appserviceId")
public async getAppservice(@QueryParam("scalar_token") scalarToken: string, @PathParam("appserviceId") asId: string): Promise<AppserviceResponse> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
try {
const appservice = await AppserviceStore.getAppservice(asId);
return this.mapAppservice(appservice);
} catch (err) {
LogService.error("AdminAppserviceService", err);
throw new ApiError(404, "Appservice not found");
}
}
@POST
@Path("new")
public async createAppservice(@QueryParam("scalar_token") scalarToken: string, request: AppserviceCreateRequest): Promise<AppserviceResponse> {
@ -72,6 +89,19 @@ export class AdminAppserviceService {
return this.mapUser(user);
}
@POST
@Path(":appserviceId/test")
public async test(@QueryParam("scalar_token") scalarToken: string, @PathParam("appserviceId") asId: string): Promise<any> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
const appservice = await AppserviceStore.getAppservice(asId);
const client = new MatrixAppserviceClient(config.homeserver.name, appservice);
const userId = await client.whoAmI();
if (userId.startsWith("@" + appservice.userPrefix)) return {}; // 200 OK
throw new ApiError(500, "User ID does not match the application service");
}
private mapAppservice(as: AppService): AppserviceResponse {
return {
id: as.id,

View File

@ -62,6 +62,12 @@ export class AppserviceStore {
});
}
public static async getAppservice(id: string): Promise<AppService> {
const appservice = await AppService.findByPrimary(id);
if (!appservice) throw new Error("Appservice not found");
return appservice;
}
private constructor() {
}
}

View File

@ -22,4 +22,14 @@ export class MatrixAppserviceClient {
{type: "m.login.application_service", username: localpart},
);
}
public async whoAmI(): Promise<string> {
const response = await doFederatedApiCall(
"GET",
this.homeserverName,
"/_matrix/client/r0/account/whoami",
{access_token: this.appservice.asToken},
);
return response['user_id'];
}
}

View File

@ -3,6 +3,11 @@ import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-
import { AdminNebApiService } from "../../../shared/services/admin/admin-neb-api.service";
import { ToasterService } from "angular2-toaster";
import { ActivatedRoute, Router } from "@angular/router";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import {
AdminNebAppserviceConfigComponent,
AppserviceConfigDialogContext
} from "../appservice-config/appservice-config.component";
@Component({
@ -19,16 +24,23 @@ export class AdminAddSelfhostedNebComponent {
private nebApi: AdminNebApiService,
private toaster: ToasterService,
private router: Router,
private activatedRoute: ActivatedRoute) {
private activatedRoute: ActivatedRoute,
private modal: Modal) {
}
public save(): void {
this.isSaving = true;
this.asApi.createAppservice(this.userPrefix).then(appservice => {
return this.nebApi.newAppserviceConfiguration(this.adminUrl, appservice);
}).then(() => {
}).then(neb => {
this.toaster.pop("success", "New go-neb created");
this.router.navigate(["../.."], {relativeTo: this.activatedRoute});
this.modal.open(AdminNebAppserviceConfigComponent, overlayConfigFactory({
neb: neb,
isBlocking: true,
size: 'lg',
}, AppserviceConfigDialogContext)).result.then(() => this.router.navigate(["../.."], {relativeTo: this.activatedRoute}));
}).catch(err => {
console.error(err);
this.isSaving = false;

View File

@ -0,0 +1,22 @@
<div class="dialog">
<div class="dialog-header">
<h4>go-neb appservice configuration</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
Copy and paste this configuration to <code>appservice-{{appservice.id}}.yaml</code> on your homeserver and
register it as an application service.
<br/>
<pre>{{appserviceConfig}}</pre>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<button type="button" (click)="dialog.close()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> Close
</button>
<button type="button" (click)="test()" title="close" class="btn btn-secondary btn-sm">
<i class="fa fa-exchange-alt"></i> Test Configuration
</button>
</div>
</div>

View File

@ -0,0 +1,6 @@
pre {
margin-top: 10px;
padding: 5px;
border: 1px solid #ccc;
background-color: #eee;
}

View File

@ -0,0 +1,57 @@
import { Component } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { FE_Appservice, FE_NebConfiguration } from "../../../shared/models/admin_responses";
import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-appservice-api.service";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
export class AppserviceConfigDialogContext extends BSModalContext {
public neb: FE_NebConfiguration;
}
@Component({
templateUrl: "./appservice-config.component.html",
styleUrls: ["./appservice-config.component.scss"],
})
export class AdminNebAppserviceConfigComponent implements ModalComponent<AppserviceConfigDialogContext> {
public isLoading = true;
public neb: FE_NebConfiguration;
public appservice: FE_Appservice;
constructor(public dialog: DialogRef<AppserviceConfigDialogContext>, private adminAppserviceApi: AdminAppserviceApiService, private toaster: ToasterService) {
this.neb = dialog.context.neb;
this.adminAppserviceApi.getAppservice(this.neb.appserviceId).then(appservice => {
this.appservice = appservice;
this.isLoading = false;
}).catch(err => {
console.error(err);
this.toaster.pop("error", "Could not load appservice configuration");
});
}
public get appserviceConfig(): string {
return `` +
`id: ${this.appservice.id}\n` +
`url: ${window.location.origin}/_matrix/appservice/r0\n` +
`as_token: ${this.appservice.asToken}\n` +
`hs_token: ${this.appservice.hsToken}\n` +
`sender_localpart: ${this.appservice.userPrefix}\n` +
`namespaces:\n` +
` users:\n` +
` - exclusive: true\n` +
` regex: '@${this.appservice.userPrefix}.*'\n` +
` aliases: []\n` +
` rooms: []`;
}
public test() {
this.adminAppserviceApi.test(this.neb.appserviceId).then(() => {
this.toaster.pop("success", "The appservice appears to be correctly set up");
}).catch(err => {
console.error(err);
this.toaster.pop("error", "The appservice is not correctly set up");
});
}
}

View File

@ -5,6 +5,11 @@ import { AdminUpstreamApiService } from "../../shared/services/admin/admin-upstr
import { AdminAppserviceApiService } from "../../shared/services/admin/admin-appservice-api.service";
import { FE_Appservice, FE_NebConfiguration, FE_Upstream } from "../../shared/models/admin_responses";
import { ActivatedRoute, Router } from "@angular/router";
import {
AdminNebAppserviceConfigComponent,
AppserviceConfigDialogContext
} from "./appservice-config/appservice-config.component";
import { Modal, overlayConfigFactory } from "ngx-modialog";
@Component({
templateUrl: "./neb.component.html",
@ -24,7 +29,8 @@ export class AdminNebComponent {
private appserviceApi: AdminAppserviceApiService,
private toaster: ToasterService,
private router: Router,
private activatedRoute: ActivatedRoute) {
private activatedRoute: ActivatedRoute,
private modal: Modal) {
this.reload().then(() => this.isLoading = false).catch(error => {
console.error(error);
@ -68,7 +74,12 @@ export class AdminNebComponent {
}
public showAppserviceConfig(neb: FE_NebConfiguration) {
console.log(neb);
this.modal.open(AdminNebAppserviceConfigComponent, overlayConfigFactory({
neb: neb,
isBlocking: true,
size: 'lg',
}, AppserviceConfigDialogContext));
}
public getEnabledBotsString(neb: FE_NebConfiguration): string {

View File

@ -53,6 +53,7 @@ import { AdminUpstreamApiService } from "./shared/services/admin/admin-upstream-
import { AdminNebComponent } from "./admin/neb/neb.component";
import { AdminEditNebComponent } from "./admin/neb/edit/edit.component";
import { AdminAddSelfhostedNebComponent } from "./admin/neb/add_selfhosted/add_selfhosted.component";
import { AdminNebAppserviceConfigComponent } from "./admin/neb/appservice-config/appservice-config.component";
@NgModule({
imports: [
@ -100,6 +101,7 @@ import { AdminAddSelfhostedNebComponent } from "./admin/neb/add_selfhosted/add_s
AdminNebComponent,
AdminEditNebComponent,
AdminAddSelfhostedNebComponent,
AdminNebAppserviceConfigComponent,
// Vendor
],
@ -122,6 +124,7 @@ import { AdminAddSelfhostedNebComponent } from "./admin/neb/add_selfhosted/add_s
entryComponents: [
AdminWidgetEtherpadConfigComponent,
AdminWidgetJitsiConfigComponent,
AdminNebAppserviceConfigComponent,
]
})
export class AppModule {

View File

@ -13,7 +13,15 @@ export class AdminAppserviceApiService extends AuthedApi {
return this.authedGet("/api/v1/dimension/admin/appservices/all").map(r => r.json()).toPromise();
}
public getAppservice(appserviceId: string): Promise<FE_Appservice> {
return this.authedGet("/api/v1/dimension/admin/appservices/" + appserviceId).map(r => r.json()).toPromise();
}
public createAppservice(userPrefix: string): Promise<FE_Appservice> {
return this.authedPost("/api/v1/dimension/admin/appservices/new", {userPrefix: userPrefix}).map(r => r.json()).toPromise();
}
public test(appserviceId: string): Promise<any> {
return this.authedPost("/api/v1/dimension/admin/appservices/" + appserviceId + "/test").map(r => r.json()).toPromise();
}
}