Created policy module.
This commit is contained in:
12
src/app/policies/policies.module.ts
Normal file
12
src/app/policies/policies.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule
|
||||
]
|
||||
})
|
||||
export class PoliciesModule { }
|
@ -0,0 +1,25 @@
|
||||
<div>
|
||||
<form
|
||||
standalone>
|
||||
<mat-form-field class="example-full-width">
|
||||
<input
|
||||
name="path"
|
||||
type="text"
|
||||
placeholder="Pick one"
|
||||
[(ngModel)]="newPolicyName"
|
||||
matInput
|
||||
[formControl]="myControl"
|
||||
[matAutocomplete]="auto" />
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
@for (option of filteredPolicies | async; track option) {
|
||||
<mat-option [value]="option">{{option}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<button
|
||||
mat-flat-button
|
||||
(click)="addNewPolicy()">
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PolicyAddFormComponent } from './policy-add-form.component';
|
||||
|
||||
describe('PolicyAddFormComponent', () => {
|
||||
let component: PolicyAddFormComponent;
|
||||
let fixture: ComponentFixture<PolicyAddFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PolicyAddFormComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PolicyAddFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,76 @@
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
|
||||
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { Policy } from '../../shared/models/policy';
|
||||
import EventService from '../../shared/services/EventService';
|
||||
import { map, Observable, startWith } from 'rxjs';
|
||||
import { HermesClientService } from '../../hermes-client.service';
|
||||
|
||||
const Policies = [
|
||||
{ path: "tts", description: "Anything to do with TTS" },
|
||||
{ path: "tts.chat", description: "Anything to do with chat" },
|
||||
{ path: "tts.chat.bits.read", description: "To read chat messages with bits via TTS" },
|
||||
{ path: "tts.chat.messages.read", description: "To read chat messages via TTS" },
|
||||
{ path: "tts.chat.redemptions.read", description: "To read channel point redemption messages via TTS" },
|
||||
//{ path: "tts.chat.subscriptions.read", description: "To read chat messages from subscriptions via TTS" },
|
||||
{ path: "tts.commands", description: "To execute commands for TTS" },
|
||||
{ path: "tts.commands.nightbot", description: "To use !nightbot command" },
|
||||
{ path: "tts.commands.obs", description: "To use !obs command" },
|
||||
{ path: "tts.commands.refresh", description: "To use !refresh command" },
|
||||
{ path: "tts.commands.skip", description: "To use !skip command" },
|
||||
{ path: "tts.commands.skipall", description: "To use !skipall command" },
|
||||
{ path: "tts.commands.tts", description: "To use !tts command" },
|
||||
{ path: "tts.commands.tts.join", description: "To use !tts join command" },
|
||||
{ path: "tts.commands.tts.leave", description: "To use !tts leave command" },
|
||||
{ path: "tts.commands.version", description: "To use !version command" },
|
||||
{ path: "tts.commands.voice", description: "To use !voice command" },
|
||||
{ path: "tts.commands.voice.admin", description: "To use !voice command on others" },
|
||||
]
|
||||
|
||||
@Component({
|
||||
selector: 'policy-add-form',
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
FormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
templateUrl: './policy-add-form.component.html',
|
||||
styleUrl: './policy-add-form.component.scss'
|
||||
})
|
||||
export class PolicyAddFormComponent {
|
||||
myControl = new FormControl('');
|
||||
newPolicyName: string = '';
|
||||
filteredPolicies: Observable<string[]>;
|
||||
|
||||
constructor(private events: EventService, private hermes: HermesClientService) {
|
||||
this.filteredPolicies = this.myControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => this._filter(value || '')),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.filteredPolicies = this.myControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => this._filter(value || '')),
|
||||
);
|
||||
}
|
||||
|
||||
private _filter(value: string): string[] {
|
||||
const filterValue = value.toLowerCase();
|
||||
|
||||
return Policies.map(p => p.path).filter(option => option.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
addNewPolicy() {
|
||||
this.events.emit('addPolicy', this.newPolicyName);
|
||||
this.newPolicyName = "";
|
||||
}
|
||||
}
|
61
src/app/policies/policy-table/policy-table.component.html
Normal file
61
src/app/policies/policy-table/policy-table.component.html
Normal file
@ -0,0 +1,61 @@
|
||||
<table mat-table [dataSource]="policies" class="mat-elevation-z8">
|
||||
<ng-container matColumnDef="path">
|
||||
<th mat-header-cell *matHeaderCellDef>Path</th>
|
||||
<td mat-cell *matCellDef="let policy">
|
||||
{{policy.path}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="group">
|
||||
<th mat-header-cell *matHeaderCellDef>Group</th>
|
||||
<td mat-cell *matCellDef="let policy">
|
||||
@if (policy.editing) {
|
||||
<input type="text" [(ngModel)]="policy.temp_group_name" />
|
||||
}
|
||||
@if (!policy.editing && groups[policy.group_id]) {
|
||||
{{groups[policy.group_id].name}}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="usage">
|
||||
<th mat-header-cell *matHeaderCellDef>Usage per span</th>
|
||||
<td mat-cell *matCellDef="let policy">
|
||||
@if (policy.editing) {
|
||||
<input type="number" [(ngModel)]="policy.usage" (keypress)="($event.charCode >= 48 && $event.charCode < 58)" />
|
||||
}
|
||||
@if (!policy.editing) {
|
||||
{{policy.usage}}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="span">
|
||||
<th mat-header-cell *matHeaderCellDef>Span (ms)</th>
|
||||
<td mat-cell *matCellDef="let policy">
|
||||
@if (policy.editing) {
|
||||
<input type="number" [(ngModel)]="policy.span" (keypress)="($event.charCode >= 48 && $event.charCode < 58)" />
|
||||
}
|
||||
@if (!policy.editing) {
|
||||
{{policy.span}}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let policy">
|
||||
@if (!policy.editing) {
|
||||
<button mat-mini-fab (click)="edit(policy)"><mat-icon>edit</mat-icon></button>
|
||||
<button mat-mini-fab (click)="delete(policy)"><mat-icon>delete</mat-icon></button>
|
||||
}
|
||||
@if (policy.editing) {
|
||||
<button mat-mini-fab (click)="save(policy)"><mat-icon>save</mat-icon></button>
|
||||
<button mat-mini-fab (click)="cancel(policy)"><mat-icon>cancel</mat-icon></button>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
23
src/app/policies/policy-table/policy-table.component.spec.ts
Normal file
23
src/app/policies/policy-table/policy-table.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PolicyTableComponent } from './policy-table.component';
|
||||
|
||||
describe('PolicyTableComponent', () => {
|
||||
let component: PolicyTableComponent;
|
||||
let fixture: ComponentFixture<PolicyTableComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PolicyTableComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PolicyTableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
184
src/app/policies/policy-table/policy-table.component.ts
Normal file
184
src/app/policies/policy-table/policy-table.component.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import { Component, ElementRef, Input, isDevMode, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatTable, MatTableModule } from '@angular/material/table';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import EventService from '../../shared/services/EventService';
|
||||
import { Policy } from '../../shared/models/policy';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HermesClientService } from '../../hermes-client.service';
|
||||
|
||||
@Component({
|
||||
selector: 'policy-table',
|
||||
standalone: true,
|
||||
imports: [FormsModule, MatTableModule, MatIconModule],
|
||||
templateUrl: './policy-table.component.html',
|
||||
styleUrl: './policy-table.component.scss'
|
||||
})
|
||||
export class PolicyTableComponent implements OnInit, OnDestroy {
|
||||
@Input() policies: Policy[] = []
|
||||
displayedColumns = ['path', 'group', 'usage', 'span', 'actions']
|
||||
groups: { [id: string]: { id: string, name: string, priority: number } }
|
||||
|
||||
@ViewChild(MatTable) table: MatTable<Policy>;
|
||||
private subscription: Subscription | undefined;
|
||||
|
||||
constructor(private events: EventService, private hermes: HermesClientService) {
|
||||
this.table = {} as MatTable<Policy>;
|
||||
this.groups = {};
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscription = this.events.listen('addPolicy', (payload) => {
|
||||
if (!payload)
|
||||
return;
|
||||
if (this.policies.map(p => p.path).includes(payload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.policies.push(new Policy("", "", payload, 1, 5000, "", true, true));
|
||||
this.table.renderRows();
|
||||
});
|
||||
this.hermes.subscribe(4, (response: any) => {
|
||||
console.log('request received: ', response);
|
||||
if (response.request.type == "get_policies") {
|
||||
for (let policy of response.data) {
|
||||
this.policies.push(new Policy(policy.id, policy.group_id, policy.path, policy.usage, policy.span, "", false, false));
|
||||
}
|
||||
if (isDevMode())
|
||||
console.log('policies', this.policies);
|
||||
this.table.renderRows();
|
||||
} else if (response.request.type == "create_policy") {
|
||||
console.log("create policy", response);
|
||||
const policy = this.policies.find(p => this.groups[response.data.group_id].name == p.temp_group_name && p.path == response.data.path);
|
||||
if (policy == null) {
|
||||
this.policies.push(new Policy(response.data.id, response.data.group_id, response.data.path, response.data.usage, response.data.span));
|
||||
} else {
|
||||
policy.id = response.data.id;
|
||||
policy.group_id = response.data.group_id;
|
||||
policy.editing = false;
|
||||
policy.isNew = false;
|
||||
}
|
||||
this.table.renderRows();
|
||||
} else if (response.request.type == "update_policy") {
|
||||
console.log("update policy", response);
|
||||
const policy = this.policies.find(p => p.id == response.data.id);
|
||||
if (policy == null) {
|
||||
this.policies.push(new Policy(response.data.id, response.data.group_id, response.data.path, response.data.usage, response.data.span));
|
||||
} else {
|
||||
policy.id = response.data.id;
|
||||
policy.group_id = response.data.group_id;
|
||||
policy.editing = false;
|
||||
policy.isNew = false;
|
||||
}
|
||||
this.table.renderRows();
|
||||
} else if (response.request.type == "delete_policy") {
|
||||
console.log('delete policy', response.request.data.id);
|
||||
const policy = this.policies.find(p => p.id == response.request.data.id);
|
||||
if (!policy) {
|
||||
console.log('Could not find the policy by id. Already deleted.');
|
||||
return;
|
||||
}
|
||||
const index = this.policies.indexOf(policy);
|
||||
if (index >= 0) {
|
||||
this.policies.splice(index, 1);
|
||||
this.table.renderRows();
|
||||
}
|
||||
} else if (response.request.type == "get_permissions") {
|
||||
this.groups = Object.assign({}, ...response.data.groups.map((g: any) => ({ [g.id]: g })));
|
||||
console.log('groups', response.data)
|
||||
}
|
||||
});
|
||||
this.hermes.fetchPolicies();
|
||||
this.hermes.fetchPermissionsAndGroups();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription)
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
cancel(policy: Policy) {
|
||||
if (!policy.editing)
|
||||
return;
|
||||
|
||||
if (policy.isNew) {
|
||||
const index = this.policies.indexOf(policy);
|
||||
if (index >= 0) {
|
||||
this.policies.splice(index, 1);
|
||||
this.table.renderRows();
|
||||
}
|
||||
} else {
|
||||
policy.path = policy.old_path ?? '';
|
||||
policy.usage = policy.old_usage ?? 1;
|
||||
policy.span = policy.old_span ?? 5000;
|
||||
policy.old_path = undefined;
|
||||
policy.old_span = undefined;
|
||||
policy.old_usage = undefined;
|
||||
policy.editing = false;
|
||||
}
|
||||
}
|
||||
|
||||
delete(policy: Policy) {
|
||||
this.hermes.deletePolicy(policy.id);
|
||||
}
|
||||
|
||||
edit(policy: Policy) {
|
||||
policy.old_path = policy.path;
|
||||
policy.old_span = policy.span;
|
||||
policy.old_usage = policy.usage;
|
||||
policy.temp_group_name = this.groups[policy.group_id].name
|
||||
policy.editing = true;
|
||||
}
|
||||
|
||||
save(policy: Policy) {
|
||||
if (!policy.temp_group_name) {
|
||||
console.log('group must be valid.');
|
||||
return;
|
||||
}
|
||||
const group = Object.values(this.groups).find(g => g.name == policy.temp_group_name);
|
||||
if (group == null) {
|
||||
console.log('group does not exist.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy.isNew) {
|
||||
const match = this.policies.find(p => p.group_id == group.id && p.path == policy.path);
|
||||
if (match) {
|
||||
console.log('policy already exists');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNaN(policy.usage)) {
|
||||
console.log('usage must be a whole number.');
|
||||
return;
|
||||
}
|
||||
if (policy.usage < 1 || policy.usage > 99) {
|
||||
console.error('usage must be between 1 and 99.');
|
||||
return;
|
||||
}
|
||||
if (policy.usage % 1.0 != 0) {
|
||||
console.error('usage must be a whole number.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNaN(policy.span)) {
|
||||
console.log('span must be a whole number.');
|
||||
return;
|
||||
}
|
||||
if (policy.span < 1000 || policy.span > 1800000) {
|
||||
console.error('span must be between 1 and 1800000.');
|
||||
return;
|
||||
}
|
||||
if (policy.span % 1.0 != 0) {
|
||||
console.error('span must be a whole number.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy.isNew) {
|
||||
this.hermes.createPolicy(group.id, policy.path, policy.usage, policy.span);
|
||||
} else {
|
||||
this.hermes.updatePolicy(policy.id, group.id, policy.path, policy.usage, policy.span);
|
||||
}
|
||||
}
|
||||
}
|
8
src/app/policies/policy/policy.component.html
Normal file
8
src/app/policies/policy/policy.component.html
Normal file
@ -0,0 +1,8 @@
|
||||
<h4>Policies</h4>
|
||||
<div>
|
||||
<policy-add-form />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<policy-table [policies]="policies" />
|
||||
</div>
|
7
src/app/policies/policy/policy.component.scss
Normal file
7
src/app/policies/policy/policy.component.scss
Normal file
@ -0,0 +1,7 @@
|
||||
div {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-align: center;
|
||||
}
|
23
src/app/policies/policy/policy.component.spec.ts
Normal file
23
src/app/policies/policy/policy.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PolicyComponent } from './policy.component';
|
||||
|
||||
describe('PolicyComponent', () => {
|
||||
let component: PolicyComponent;
|
||||
let fixture: ComponentFixture<PolicyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PolicyComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
53
src/app/policies/policy/policy.component.ts
Normal file
53
src/app/policies/policy/policy.component.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Component, Inject, NgZone, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||
import { PolicyAddFormComponent } from "../policy-add-form/policy-add-form.component";
|
||||
import { PolicyTableComponent } from "../policy-table/policy-table.component";
|
||||
import { Policy, PolicyScope } from '../../shared/models/policy';
|
||||
import { DatePipe, isPlatformBrowser } from '@angular/common';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { HermesClientService } from '../../hermes-client.service';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'policy',
|
||||
standalone: true,
|
||||
imports: [RouterModule, PolicyAddFormComponent, PolicyTableComponent],
|
||||
templateUrl: './policy.component.html',
|
||||
styleUrl: './policy.component.scss'
|
||||
})
|
||||
export class PolicyComponent implements OnInit, OnDestroy {
|
||||
private isBrowser: boolean;
|
||||
private ngZone: NgZone;
|
||||
private subscription: Subscription | undefined;
|
||||
items: Policy[];
|
||||
pipe = new DatePipe('en-US')
|
||||
|
||||
|
||||
constructor(private client: HermesClientService, private oauthService: OAuthService, private router: Router, ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) {
|
||||
this.ngZone = ngZone;
|
||||
this.isBrowser = isPlatformBrowser(this.platformId)
|
||||
|
||||
this.items = []
|
||||
}
|
||||
|
||||
get policies() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.isBrowser)
|
||||
return;
|
||||
|
||||
if (!this.client.logged_in) {
|
||||
this.router.navigate(["/tts-login"]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.subscription = this.client.connect();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription)
|
||||
this.subscription.unsubscribe()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user