Implement appeal form flow with dynamic pages, integrate punishment selection, and add username retrieval logic. Update API schema and enhance auth.service for username handling.
This commit is contained in:
parent
737b26a6c7
commit
ae1e972438
|
|
@ -40,6 +40,7 @@ public class SecurityConfig {
|
||||||
.authorizeHttpRequests(
|
.authorizeHttpRequests(
|
||||||
auth -> auth
|
auth -> auth
|
||||||
.requestMatchers("/api/form/**").hasAuthority(PermissionClaimDto.USER.getValue())
|
.requestMatchers("/api/form/**").hasAuthority(PermissionClaimDto.USER.getValue())
|
||||||
|
.requestMatchers("/api/login/userLogin").hasAuthority(PermissionClaimDto.USER.getValue())
|
||||||
.requestMatchers("/api/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
.requestMatchers("/api/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
.requestMatchers("/api/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
.requestMatchers("/api/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
.requestMatchers("/api/files/save/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
.requestMatchers("/api/files/save/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.alttd.altitudeweb.controllers.application;
|
package com.alttd.altitudeweb.controllers.forms;
|
||||||
|
|
||||||
import com.alttd.altitudeweb.api.AppealsApi;
|
import com.alttd.altitudeweb.api.AppealsApi;
|
||||||
import com.alttd.altitudeweb.services.limits.RateLimit;
|
import com.alttd.altitudeweb.services.limits.RateLimit;
|
||||||
|
|
@ -10,48 +10,140 @@
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div class="pages">
|
<div class="pages">
|
||||||
@if (currentPageIndex === 0) {
|
@if (currentPageIndex === 0) {
|
||||||
<section class="formPage">
|
@if (history()?.length === 0) {
|
||||||
<img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/>
|
<section class="formPage">
|
||||||
<h1>Punishment Appeal</h1>
|
<img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/>
|
||||||
<p>We aim to respond within 48 hours.</p>
|
<h1>Punishment Appeal</h1>
|
||||||
</section>
|
<p>You have no punishments to appeal.</p>
|
||||||
|
</section>
|
||||||
|
} @else {
|
||||||
|
<section class="formPage">
|
||||||
|
<img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/>
|
||||||
|
<h1>Punishment Appeal</h1>
|
||||||
|
<p>We aim to respond within 48 hours.</p>
|
||||||
|
<button mat-raised-button (click)="nextPage()" [disabled]="history() == null">
|
||||||
|
@if (history() == null) {
|
||||||
|
<mat-spinner></mat-spinner>
|
||||||
|
} @else {
|
||||||
|
Next
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Page 2 -->
|
|
||||||
@if (currentPageIndex === 1) {
|
@if (currentPageIndex === 1) {
|
||||||
<section class="formPage">
|
<section class="formPage">
|
||||||
<h1>Page 2</h1>
|
<div class="description">
|
||||||
<p>This is the second page of the form.</p>
|
<p>You are logged in as <strong>{{ authService.username() }}</strong>. If this is the correct account
|
||||||
|
please continue</p>
|
||||||
|
<br>
|
||||||
|
<p><strong>Notice: </strong> Submitting an appeal is <strong>not</strong> an instant process.
|
||||||
|
We will investigate the punishment you are appealing and respond within 48 hours.</p>
|
||||||
|
<p style="font-style: italic;">Appeals that seem to have been made with
|
||||||
|
little to no effort will be automatically denied.</p>
|
||||||
|
</div>
|
||||||
|
<button mat-raised-button (click)="nextPage()" [disabled]="authService.username() == null">
|
||||||
|
I, {{ authService.username() }}, understand and agree
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Page 3 -->
|
|
||||||
@if (currentPageIndex === 2) {
|
@if (currentPageIndex === 2) {
|
||||||
<section class="formPage">
|
<section class="formPage">
|
||||||
<h1>Page 3</h1>
|
<div class="description">
|
||||||
<p>This is the third page of the form.</p>
|
<h2>Please select the punishment you want to appeal</h2>
|
||||||
|
</div>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Punishment</mat-label>
|
||||||
|
<mat-select (valueChange)="onPunishmentSelected($event)">
|
||||||
|
@for (punishment of history(); track punishment) {
|
||||||
|
<mat-option [value]="punishment">{{ punishment.type }} - {{ punishment.reason }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
@if (selectedPunishment() != null) {
|
||||||
|
<button mat-raised-button (click)="nextPage()" [disabled]="selectedPunishment() == null">
|
||||||
|
Appeal {{ selectedPunishment()!.type }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<form [formGroup]="form">
|
||||||
|
@if (currentPageIndex === 3) {
|
||||||
|
<section class="formPage">
|
||||||
|
<div class="description">
|
||||||
|
<h2>Please enter your email.</h2>
|
||||||
|
<p style="font-style: italic">It does not have to be your minecraft email.</p>
|
||||||
|
<mat-form-field appearance="fill" style="width: 100%;">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput formControlName="email" placeholder="Email">
|
||||||
|
@if (form.controls.email.invalid && form.controls.email.touched) {
|
||||||
|
<mat-error>
|
||||||
|
@if (form.controls.email.errors?.['required']) {
|
||||||
|
Email is required
|
||||||
|
} @else if (form.controls.email.errors?.['email']) {
|
||||||
|
Please enter a valid email address
|
||||||
|
}
|
||||||
|
</mat-error>
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<button mat-raised-button (click)="nextPage()" [disabled]="form.controls.email.invalid">
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (currentPageIndex === 4) {
|
||||||
|
<section class="formPage">
|
||||||
|
<div class="description">
|
||||||
|
<h2>Why should your {{ selectedPunishment()?.type }} be reduced or removed?</h2>
|
||||||
|
<p style="font-style: italic">Please take your time writing this, we're more likely to accept an
|
||||||
|
appeal if effort was put into it.</p>
|
||||||
|
<mat-form-field appearance="fill" style="width: 100%;">
|
||||||
|
<mat-label>Reason</mat-label>
|
||||||
|
<textarea matInput formControlName="appeal" placeholder="Reason" rows="6"></textarea>
|
||||||
|
@if (form.controls.appeal.invalid && form.controls.appeal.touched) {
|
||||||
|
<mat-error>
|
||||||
|
@if (form.controls.appeal.errors?.['required']) {
|
||||||
|
Reason is required
|
||||||
|
} @else if (form.controls.appeal.errors?.['minlength']) {
|
||||||
|
Reason must be at least 10 characters
|
||||||
|
}
|
||||||
|
</mat-error>
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<button mat-raised-button (click)="onSubmit()" [disabled]="form.invalid">
|
||||||
|
Submit Appeal
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Navigation dots -->
|
<!-- Navigation dots -->
|
||||||
<div class="form-navigation">
|
@if (totalPages.length > 1) {
|
||||||
<button mat-icon-button class="nav-button" (click)="previousPage()" [disabled]="isFirstPage()">
|
<div class="form-navigation">
|
||||||
<mat-icon>navigate_before</mat-icon>
|
<button mat-icon-button class="nav-button" (click)="previousPage()" [disabled]="isFirstPage()">
|
||||||
</button>
|
<mat-icon>navigate_before</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
@for (i of totalPages; track i) {
|
@for (i of totalPages; track i) {
|
||||||
<div
|
<div
|
||||||
class="nav-dot"
|
class="nav-dot"
|
||||||
[class.active]="i === currentPageIndex"
|
[class.active]="i === currentPageIndex"
|
||||||
(click)="goToPage(i)">
|
(click)="goToPage(i)">
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<button mat-icon-button class="nav-button" (click)="nextPage()" [disabled]="isLastPage()">
|
<button mat-icon-button class="nav-button" (click)="nextPage()" [disabled]="isLastPage()">
|
||||||
<mat-icon>navigate_next</mat-icon>
|
<mat-icon>navigate_next</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,8 @@ main {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
max-width: 75ch;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import {AfterViewInit, Component, ElementRef, OnInit, Renderer2} from '@angular/core';
|
import {AfterViewInit, Component, ElementRef, OnInit, Renderer2, signal} from '@angular/core';
|
||||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {AppealsService, MinecraftAppeal} from '@api';
|
import {AppealsService, HistoryService, MinecraftAppeal, PunishmentHistory} from '@api';
|
||||||
import {HeaderComponent} from '@header/header.component';
|
import {HeaderComponent} from '@header/header.component';
|
||||||
import {NgOptimizedImage} from '@angular/common';
|
import {NgOptimizedImage} from '@angular/common';
|
||||||
import {MatButtonModule} from '@angular/material/button';
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
import {MatIconModule} from '@angular/material/icon';
|
import {MatIconModule} from '@angular/material/icon';
|
||||||
|
import {AuthService} from '@services/auth.service';
|
||||||
|
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||||
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||||
|
import {MatSelectModule} from '@angular/material/select';
|
||||||
|
import {MatInputModule} from '@angular/material/input';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-appeal',
|
selector: 'app-appeal',
|
||||||
|
|
@ -12,7 +17,12 @@ import {MatIconModule} from '@angular/material/icon';
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NgOptimizedImage,
|
NgOptimizedImage,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatIconModule
|
MatIconModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatInputModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
templateUrl: './appeal.component.html',
|
templateUrl: './appeal.component.html',
|
||||||
styleUrl: './appeal.component.scss'
|
styleUrl: './appeal.component.scss'
|
||||||
|
|
@ -22,21 +32,30 @@ export class AppealComponent implements OnInit, AfterViewInit {
|
||||||
public form: FormGroup<Appeal>;
|
public form: FormGroup<Appeal>;
|
||||||
private resizeObserver: ResizeObserver | null = null;
|
private resizeObserver: ResizeObserver | null = null;
|
||||||
private boundHandleResize: any;
|
private boundHandleResize: any;
|
||||||
|
protected history = signal<PunishmentHistory[] | null>(null);
|
||||||
|
protected selectedPunishment = signal<PunishmentHistory | null>(null);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appealApi: AppealsService,
|
private appealApi: AppealsService,
|
||||||
|
private historyApi: HistoryService,
|
||||||
|
protected authService: AuthService,
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
private renderer: Renderer2
|
private renderer: Renderer2
|
||||||
) {
|
) {
|
||||||
this.form = new FormGroup({
|
this.form = new FormGroup({
|
||||||
username: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
|
|
||||||
punishmentId: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
|
|
||||||
email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
|
email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
|
||||||
appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]})
|
appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
const uuid = this.authService.getUuid();
|
||||||
|
if (uuid === null) {
|
||||||
|
throw new Error('JWT subject is null, are you logged in?');
|
||||||
|
}
|
||||||
|
this.historyApi.getAllHistoryForUUID(uuid).subscribe(history => {
|
||||||
|
this.history.set(history);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
|
@ -116,18 +135,22 @@ export class AppealComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
private sendForm() {
|
private sendForm() {
|
||||||
const rawValue = this.form.getRawValue();
|
const rawValue = this.form.getRawValue();
|
||||||
|
const uuid = this.authService.getUuid();
|
||||||
|
if (uuid === null) {
|
||||||
|
throw new Error('JWT subject is null, are you logged in?');
|
||||||
|
}
|
||||||
const appeal: MinecraftAppeal = {
|
const appeal: MinecraftAppeal = {
|
||||||
appeal: rawValue.appeal,
|
appeal: rawValue.appeal,
|
||||||
email: rawValue.email,
|
email: rawValue.email,
|
||||||
punishmentId: parseInt(rawValue.punishmentId),
|
punishmentId: this.selectedPunishment()!.id,
|
||||||
username: rawValue.username,
|
username: this.authService.username()!,
|
||||||
uuid: ''//TODO
|
uuid: uuid
|
||||||
}
|
}
|
||||||
this.appealApi.submitMinecraftAppeal(appeal).subscribe()
|
this.appealApi.submitMinecraftAppeal(appeal).subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
public currentPageIndex: number = 0;
|
public currentPageIndex: number = 0;
|
||||||
public totalPages: number[] = [0, 1, 2];
|
public totalPages: number[] = [0];
|
||||||
|
|
||||||
public goToPage(pageIndex: number): void {
|
public goToPage(pageIndex: number): void {
|
||||||
if (pageIndex >= 0 && pageIndex < this.totalPages.length) {
|
if (pageIndex >= 0 && pageIndex < this.totalPages.length) {
|
||||||
|
|
@ -140,6 +163,11 @@ export class AppealComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextPage() {
|
public nextPage() {
|
||||||
|
if (this.currentPageIndex === this.totalPages.length - 1) {
|
||||||
|
console.log('Adding page');
|
||||||
|
this.totalPages.push(this.currentPageIndex + 1);
|
||||||
|
console.log(this.totalPages);
|
||||||
|
}
|
||||||
this.goToPage(this.currentPageIndex + 1);
|
this.goToPage(this.currentPageIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,11 +178,15 @@ export class AppealComponent implements OnInit, AfterViewInit {
|
||||||
public isLastPage(): boolean {
|
public isLastPage(): boolean {
|
||||||
return this.currentPageIndex === this.totalPages.length - 1;
|
return this.currentPageIndex === this.totalPages.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly length = length;
|
||||||
|
|
||||||
|
onPunishmentSelected($event: PunishmentHistory) {
|
||||||
|
this.selectedPunishment.set($event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Appeal {
|
interface Appeal {
|
||||||
username: FormControl<string>;
|
|
||||||
punishmentId: FormControl<string>;
|
|
||||||
email: FormControl<string>;
|
email: FormControl<string>;
|
||||||
appeal: FormControl<string>;
|
appeal: FormControl<string>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable, signal} from '@angular/core';
|
||||||
import {LoginService} from '@api';
|
import {LoginService} from '@api';
|
||||||
import {BehaviorSubject, Observable, throwError} from 'rxjs';
|
import {BehaviorSubject, Observable, throwError} from 'rxjs';
|
||||||
import {catchError, tap} from 'rxjs/operators';
|
import {catchError, tap} from 'rxjs/operators';
|
||||||
|
|
@ -17,6 +17,8 @@ export class AuthService {
|
||||||
private userClaimsSubject = new BehaviorSubject<JwtClaims | null>(null);
|
private userClaimsSubject = new BehaviorSubject<JwtClaims | null>(null);
|
||||||
public userClaims$ = this.userClaimsSubject.asObservable();
|
public userClaims$ = this.userClaimsSubject.asObservable();
|
||||||
private jwtHelper = new JwtHelperService();
|
private jwtHelper = new JwtHelperService();
|
||||||
|
private _username = signal<string | null>(environment.defaultAuthStatus ? 'akastijn' : null);
|
||||||
|
public readonly username = this._username.asReadonly();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private loginService: LoginService,
|
private loginService: LoginService,
|
||||||
|
|
@ -34,6 +36,8 @@ export class AuthService {
|
||||||
tap(jwt => {
|
tap(jwt => {
|
||||||
this.saveJwt(jwt);
|
this.saveJwt(jwt);
|
||||||
this.isAuthenticatedSubject.next(true);
|
this.isAuthenticatedSubject.next(true);
|
||||||
|
|
||||||
|
this.reloadUsername();
|
||||||
}),
|
}),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
this.snackBar.open('Login failed', '', {duration: 2000});
|
this.snackBar.open('Login failed', '', {duration: 2000});
|
||||||
|
|
@ -42,6 +46,17 @@ export class AuthService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reloadUsername() {
|
||||||
|
this.loginService.getUsername().pipe(
|
||||||
|
tap(username => {
|
||||||
|
this._username.set(username.username);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log the user out by removing the JWT
|
* Log the user out by removing the JWT
|
||||||
*/
|
*/
|
||||||
|
|
@ -49,6 +64,7 @@ export class AuthService {
|
||||||
localStorage.removeItem('jwt');
|
localStorage.removeItem('jwt');
|
||||||
this.isAuthenticatedSubject.next(false);
|
this.isAuthenticatedSubject.next(false);
|
||||||
this.userClaimsSubject.next(null);
|
this.userClaimsSubject.next(null);
|
||||||
|
this._username.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,6 +84,7 @@ export class AuthService {
|
||||||
console.log("User claims: ", claims);
|
console.log("User claims: ", claims);
|
||||||
this.userClaimsSubject.next(claims);
|
this.userClaimsSubject.next(claims);
|
||||||
this.isAuthenticatedSubject.next(true);
|
this.isAuthenticatedSubject.next(true);
|
||||||
|
this.reloadUsername();
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logout();
|
this.logout();
|
||||||
|
|
@ -113,4 +130,15 @@ export class AuthService {
|
||||||
const userAuthorizations = this.getUserAuthorizations();
|
const userAuthorizations = this.getUserAuthorizations();
|
||||||
return requiredAuthorizations.some(auth => userAuthorizations.includes(auth));
|
return requiredAuthorizations.some(auth => userAuthorizations.includes(auth));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getUuid(): string | null {
|
||||||
|
if (environment.defaultAuthStatus) {
|
||||||
|
return '55e46bc3-2a29-4c53-850f-dbd944dc5c5f';
|
||||||
|
}
|
||||||
|
const jwtClaims = this.userClaimsSubject.getValue();
|
||||||
|
if (jwtClaims === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return jwtClaims.sub ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,10 @@ tags:
|
||||||
description: Retrieves information about the staff team
|
description: Retrieves information about the staff team
|
||||||
- name: particles
|
- name: particles
|
||||||
description: All actions related to particles
|
description: All actions related to particles
|
||||||
|
- name: forms
|
||||||
|
description: All actions shared between forms
|
||||||
|
- name: appeals
|
||||||
|
description: All action related to appeals
|
||||||
paths:
|
paths:
|
||||||
/api/team/{team}:
|
/api/team/{team}:
|
||||||
$ref: './schemas/team/team.yml#/getTeam'
|
$ref: './schemas/team/team.yml#/getTeam'
|
||||||
|
|
@ -53,6 +57,8 @@ paths:
|
||||||
$ref: './schemas/login/login.yml#/RequestNewUserLogin'
|
$ref: './schemas/login/login.yml#/RequestNewUserLogin'
|
||||||
/api/login/userLogin/{code}:
|
/api/login/userLogin/{code}:
|
||||||
$ref: './schemas/login/login.yml#/UserLogin'
|
$ref: './schemas/login/login.yml#/UserLogin'
|
||||||
|
/api/login/getUsername:
|
||||||
|
$ref: './schemas/login/login.yml#/GetUsername'
|
||||||
/api/files/save/{filename}:
|
/api/files/save/{filename}:
|
||||||
$ref: './schemas/particles/particles.yml#/SaveFile'
|
$ref: './schemas/particles/particles.yml#/SaveFile'
|
||||||
/api/files/save/{uuid}/{filename}:
|
/api/files/save/{uuid}/{filename}:
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,25 @@ RequestNewUserLogin:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../generic/errors.yml#/components/schemas/ApiError'
|
$ref: '../generic/errors.yml#/components/schemas/ApiError'
|
||||||
|
GetUsername:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- login
|
||||||
|
summary: Get the username for the logged in user
|
||||||
|
operationId: getUsername
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Username retrieved
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Username'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../generic/errors.yml#/components/schemas/ApiError'
|
||||||
components:
|
components:
|
||||||
parameters:
|
parameters:
|
||||||
Code:
|
Code:
|
||||||
|
|
@ -72,6 +91,12 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description: The code to log in with
|
description: The code to log in with
|
||||||
schemas:
|
schemas:
|
||||||
|
Username:
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- username
|
||||||
LoginData:
|
LoginData:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user