Archived
Private
Public Access
1
0

Fixed minor issues + added language support

This commit is contained in:
2023-03-25 18:36:19 +01:00
parent 4422d8818a
commit 6dfb5a4b48
23 changed files with 334 additions and 83 deletions

View File

@@ -83,6 +83,8 @@ public class ProjectApi : IProjectApi {
await _proxy.RemoveLocation(project.ProxyId, project.CertificateId);
Directory.Delete(_options.Root + projectId, true);
_context.Projects.Remove(project);
await _context.SaveChangesAsync();
}

View File

@@ -1,4 +1,5 @@
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.Extensions.Options;
using ProjectManager.Backend.Entities;

View File

@@ -88,6 +88,16 @@ public class ProjectController : ControllerBase {
return Redirect($"http://{_options.Host}:{project.Port}/_/");
}
[Authorized]
[HttpGet("{projectId}/url/string")]
public IActionResult GetProjectUrlHead(string projectId) {
var project = _projects.GetProject(projectId);
if (project == null) return NotFound();
if (project.OwnerId != _context.UserId) return Unauthorized();
if (_options.Enable) return Ok(new {url = $"https://{projectId}.{_options.Domain}/_/"});
return Ok(new {url = $"http://{_options.Host}:{project.Port}/_/"});
}
[Authorized]
[HttpGet("{projectId}/start")]
public async Task<IActionResult> StartProject(string projectId) {

View File

@@ -36,7 +36,7 @@ if (app.Environment.IsDevelopment()) {
app.UseCors(
options => options
.WithOrigins(new []{app.Configuration.GetSection("Frontend").Get<string>() ?? ""})
.WithOrigins(app.Configuration.GetSection("Frontend").Get<string>() ?? "")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
@@ -45,5 +45,10 @@ app.UseCors(
app.UseAuthorization();
app.MapControllers();
app.MapGet("", async context => {
context.Response.StatusCode = StatusCodes.Status200OK;
await context.Response.BodyWriter.WriteAsync("Ok"u8.ToArray());
await context.Response.BodyWriter.CompleteAsync();
});
app.Run();

View File

@@ -3,7 +3,7 @@ import 'zone.js/node';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { existsSync, readdirSync } from 'fs';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
@@ -25,11 +25,16 @@ export function app(): express.Express {
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
server.get('/backend', (req, res) => {
let backend = process.env['BACKEND']
let backend = process.env['BACKEND'] || "http://localhost:5110/";
if (!backend?.endsWith("/")) backend += "/";
res.json({url: backend});
});
server.get('/lang', (req, res) => {
const files: string[] = readdirSync(distFolder + "/assets/languages");
res.json({files});
})
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'

View File

@@ -26,6 +26,7 @@ import {MatSnackBarModule} from "@angular/material/snack-bar";
import {MatTooltipModule} from "@angular/material/tooltip";
import { TextDialogComponent } from './components/text-dialog/text-dialog.component';
import { ProjectComponent } from './sites/project/project.component';
import {MatMenuModule} from "@angular/material/menu";
@NgModule({
declarations: [
@@ -57,7 +58,8 @@ import { ProjectComponent } from './sites/project/project.component';
FormsModule,
MatDialogModule,
MatSnackBarModule,
MatTooltipModule
MatTooltipModule,
MatMenuModule
],
providers: [],
bootstrap: [AppComponent]

View File

@@ -8,24 +8,24 @@
<mat-selection-list id="actions">
<mat-list-item routerLink="/dashboard">
<mat-icon matListItemIcon>dashboard</mat-icon>
<div matListItemTitle>Übersicht</div>
<div matListItemTitle>{{langs.currentLang?.dashboard}}</div>
</mat-list-item>
<mat-list-item (click)="logout()">
<mat-icon matListItemIcon>logout</mat-icon>
<div matListItemTitle>Ausloggen</div>
<div matListItemTitle>{{langs.currentLang?.logout}}</div>
</mat-list-item>
<mat-list-item (click)="createProject()">
<mat-icon matListItemIcon>add</mat-icon>
<div matListItemTitle>Neues Projekt</div>
<div matListItemTitle>{{langs.currentLang?.createProject}}</div>
</mat-list-item>
<mat-divider></mat-divider>
<div mat-subheader>Projekte</div>
<mat-list-item *ngFor="let project of projects.projects" (click)="openProject(project.projectId)">
<div mat-subheader>{{langs.currentLang?.projects}}</div>
<mat-list-item *ngFor="let project of projects.projects" (click)="openProject(project.projectId)" class="project">
<mat-icon matListItemIcon>open_in_new</mat-icon>
<div matListItemTitle>{{project.name}}</div>
<div matListItemLine [ngClass]="{startColor: project.running, stopColor: !project.running}">{{project.running ? 'Läuft' : 'Gestoppt'}}</div>
<div matListItemLine [ngClass]="{startColor: project.running, stopColor: !project.running}">{{project.running ? langs.currentLang?.running : langs.currentLang?.stopped}}</div>
</mat-list-item>
</mat-selection-list>
@@ -37,8 +37,16 @@
<span>Project Manager</span>
<section id="top-actions">
<button mat-icon-button (click)="onModeChange()" matTooltip="Farbmodus ändern"><mat-icon>{{darkMode ? 'light_mode' : 'dark_mode'}}</mat-icon></button>
<button mat-icon-button routerLink="/profile" *ngIf="showActions()" matTooltip="Profil Einstellungen"><mat-icon>account_circle</mat-icon></button>
<button mat-icon-button [matMenuTriggerFor]="menu" [matTooltip]="langs.currentLang?.selectLang"><mat-icon>language</mat-icon></button>
<mat-menu #menu>
<button mat-menu-item (click)="langs.setLanguage(language)" *ngFor="let language of langs.allLanguages">
<mat-icon>language</mat-icon>
<span>{{language}}</span>
</button>
</mat-menu>
<button mat-icon-button (click)="onModeChange()" [matTooltip]="langs.currentLang?.design"><mat-icon>{{darkMode ? 'light_mode' : 'dark_mode'}}</mat-icon></button>
<button mat-icon-button routerLink="/profile" *ngIf="showActions()" [matTooltip]="langs.currentLang?.profileSettings"><mat-icon>account_circle</mat-icon></button>
</section>
</mat-toolbar>

View File

@@ -14,6 +14,10 @@ mat-drawer-container {
height: calc(100vh - 80px);
overflow-y: auto;
}
.project mat-icon {
margin-block: auto;
}
}
}

View File

@@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {Router} from "@angular/router";
import {CrudService} from "../../services/crud.service";
import {ProjectService} from "../../services/project.service";
@@ -7,6 +7,8 @@ import {TextDialogComponent} from "../text-dialog/text-dialog.component";
import {firstValueFrom} from "rxjs";
import {MatSnackBar} from "@angular/material/snack-bar";
import {StorageService} from "../../services/storage.service";
import {LangService} from "../../services/lang.service";
import {Language} from "../../entities/language";
@Component({
selector: 'app-navigation',
@@ -17,7 +19,7 @@ export class NavigationComponent {
public static spinnerVisible: boolean = false;
public darkMode: boolean;
public constructor(public router: Router, public crud: CrudService, public projects: ProjectService, public dialog: MatDialog, private snackBar: MatSnackBar, private storage: StorageService) {
public constructor(public router: Router, public langs: LangService, public crud: CrudService, public projects: ProjectService, public dialog: MatDialog, private snackBar: MatSnackBar, private storage: StorageService) {
this.darkMode = storage.getItem("darkMode") == "true";
}

View File

@@ -0,0 +1,54 @@
export interface Language {
// Navigation
selectLang: string,
design: string,
profileSettings: string,
dashboard: string,
logout: string,
addProject: string,
projects: string,
running: string,
stopped: string,
// Dashboard
welcome: string,
noProjects: string,
open: string,
edit: string,
delete: string,
// Popups
name: string,
cancel: string,
createProject: string,
editProject: string
// Profile
profile: string,
profileSub: string,
email: string,
username: string,
password: string,
passwordRepeat: string,
updateAccount: string,
deleteAccount: string,
saveChanges: string,
updateFailed: string,
accountUpdated: string,
deleteQuestion: string,
deleteWarning: string,
accountDeleted: string,
submit: string,
// Login / Register
login: string,
register: string,
noAccount: string,
alreadyAccount: string,
valueToLong: string,
validEmail: string,
isRequired: string,
emailOrPasswordWrong: string,
passwordsDontMatch: string,
registerFailed: string,
}

View File

@@ -25,7 +25,7 @@ export class CrudService {
'Authorization': ''
});
constructor(private client: HttpClient, private storage: StorageService) {
constructor(public client: HttpClient, private storage: StorageService) {
this.getBackendUrl().then(() => {
this.authKey = storage.getItem("api_key");
this.setAuthKey(this.authKey);

View File

@@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import {Language} from "../entities/language";
import {CrudService} from "./crud.service";
import {firstValueFrom} from "rxjs";
import {StorageService} from "./storage.service";
@Injectable({
providedIn: 'root'
})
export class LangService {
private languages: Map<string, Language> = new Map<string, Language>();
public allLanguages: string[] = [];
public currentLang: Language;
constructor(private crud: CrudService, private storage: StorageService) {
this.loadLanguages();
}
private async loadLanguages() {
this.languages = new Map<string, Language>();
const res = await firstValueFrom(this.crud.client.get<{files: string[]}>(location?.origin + "/lang"));
const languages = res.files;
this.allLanguages = languages.map(lang => lang.replace(".json", ""));
const tasks = [];
for (let lang of languages) {
const task = firstValueFrom(this.crud.client.get<Language>(location?.origin + "/assets/languages/" + lang))
.then(result => this.languages.set(lang.replace(".json", ""), result));
tasks.push(task);
}
await Promise.all(tasks);
this.currentLang = this.languages.get(this.storage.getItem("language") || "en-US");
}
public setLanguage(lang: string) {
this.currentLang = this.languages.get(lang);
this.storage.setItem("language", lang);
}
}

View File

@@ -1,9 +1,9 @@
<section id="main">
<h1 id="welcome">Willkommen {{crud.user.username}}</h1>
<h1 id="welcome">{{langs.currentLang?.welcome}} {{crud.user.username}}</h1>
<h2 id="title">Projekte</h2>
<h2 id="title">{{langs.currentLang?.projects}}</h2>
<div id="projects">
<span *ngIf="projects.projects.length == 0 && crud.user != undefined" class="disabled">Du hast noch keine Projekte erstellt</span>
<span *ngIf="projects.projects.length == 0 && crud.user != undefined" class="disabled">{{langs.currentLang?.noProjects}}</span>
<mat-card *ngFor="let project of projects.projects" class="project">
<mat-card-header>
<mat-card-title>{{project.name}}</mat-card-title>
@@ -11,9 +11,9 @@
</mat-card-header>
<mat-card-actions>
<button mat-button color="primary" (click)="router.navigate(['/project', project.projectId])">Öffnen</button>
<button mat-button color="accent" (click)="editProject(project.projectId)">Bearbeiten</button>
<button mat-button color="warn" (click)="deleteProject(project.projectId)">Löschen</button>
<button mat-button color="primary" (click)="openProject(project.projectId)">{{langs.currentLang?.open}}</button>
<button mat-button color="accent" (click)="editProject(project.projectId)">{{langs.currentLang?.edit}}</button>
<button mat-button color="warn" (click)="deleteProject(project.projectId)">{{langs.currentLang?.delete}}</button>
<button mat-icon-button color="warn" *ngIf="project.running" (click)="updateProjectStatus(project.projectId, false)"><mat-icon>pause</mat-icon></button>
<button mat-icon-button color="accent" *ngIf="!project.running" (click)="updateProjectStatus(project.projectId, true)"><mat-icon>play_arrow</mat-icon></button>

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {ProjectService} from "../../services/project.service";
import {CrudService} from "../../services/crud.service";
import {Router} from "@angular/router";
@@ -8,6 +8,9 @@ import {firstValueFrom} from "rxjs";
import {MatSnackBar} from "@angular/material/snack-bar";
import {TextDialogComponent} from "../../components/text-dialog/text-dialog.component";
import {NavigationComponent} from "../../components/navigation/navigation.component";
import {Project} from "../../entities/project";
import {LangService} from "../../services/lang.service";
import {Language} from "../../entities/language";
@Component({
selector: 'app-dashboard',
@@ -16,7 +19,17 @@ import {NavigationComponent} from "../../components/navigation/navigation.compon
})
export class DashboardComponent {
public constructor(public crud: CrudService, public projects: ProjectService, public router: Router, private dialog: MatDialog, private snackBar: MatSnackBar) {}
public constructor(public langs: LangService, public crud: CrudService, public projects: ProjectService, public router: Router, private dialog: MatDialog, private snackBar: MatSnackBar) {}
public async openProject(projectId: string) {
const response = await this.crud.sendGetRequest<{url: string}>('projects/' + projectId + '/url/string');
const url = response.content.url;
if (!url.startsWith("https")) {
window.open(`${this.crud.backendUrl}projects/${projectId}/url?token=${this.crud.authKey}`, '_blank').focus();
} else {
await this.router.navigate(['/project', projectId]);
}
}
public async editProject(projectId: string) {
const dialogRef = this.dialog.open(TextDialogComponent, {

View File

@@ -1,29 +1,29 @@
<mat-card>
<mat-card-title>Einloggen</mat-card-title>
<mat-card-title>{{langs.currentLang?.login}}</mat-card-title>
<mat-divider></mat-divider>
<mat-card-content>
<form [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<mat-label>{{langs.currentLang?.email}}</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'email')">{{langs.currentLang?.email}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
{{langs.currentLang?.validEmail}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort</mat-label>
<mat-label>{{langs.currentLang?.password}}</mat-label>
<input type="password" matInput formControlName="password" required>
<mat-error *ngIf="form.hasError('required', 'password')">Passwort ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'password')">{{langs.currentLang?.password}} {{langs.currentLang?.isRequired}}</mat-error>
</mat-form-field>
<mat-divider></mat-divider>
<span>Du besitzt keinen Account? <a routerLink="/register">Registrieren</a></span>
<span>{{langs.currentLang?.noAccount}} <a routerLink="/register">{{langs.currentLang?.register}}</a></span>
<mat-error *ngIf="error">{{error}}</mat-error>
<button type="submit" mat-button>Einloggen</button>
<button type="submit" mat-button>{{langs.currentLang?.login}}</button>
</form>
</mat-card-content>
</mat-card>

View File

@@ -1,8 +1,10 @@
import {Component} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {CrudService} from "../../services/crud.service";
import {User} from "../../entities/user";
import {Router} from "@angular/router";
import {Language} from "../../entities/language";
import {LangService} from "../../services/lang.service";
@Component({
selector: 'app-login',
@@ -16,7 +18,7 @@ export class LoginComponent {
});
public error: string;
public constructor(private crud: CrudService, private router: Router) {
public constructor(public langs: LangService, private crud: CrudService, private router: Router) {
this.form.reset();
this.error = "";
}
@@ -33,7 +35,7 @@ export class LoginComponent {
await this.crud.loadUser(true);
await this.router.navigate(["/dashboard"]);
}else {
this.error = "E-Mail oder Passwort ist falsch";
this.error = this.langs.currentLang.emailOrPasswordWrong;
}
}
}

View File

@@ -1,46 +1,46 @@
<mat-card id="content">
<mat-card-header>
<mat-card-title>Profil</mat-card-title>
<mat-card-subtitle>Einstellungen</mat-card-subtitle>
<mat-card-title>{{langs.currentLang?.profile}}</mat-card-title>
<mat-card-subtitle>{{langs.currentLang?.profileSub}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content id="main">
<form [formGroup]="form" (ngSubmit)="update()" id="form">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<mat-label>{{langs.currentLang?.email}}</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'email')">{{langs.currentLang?.email}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
{{langs.currentLang?.validEmail}}
</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'email') && !form.hasError('required', 'email')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Benutzername</mat-label>
<mat-label>{{langs.currentLang?.username}}</mat-label>
<input type="text" matInput formControlName="username" required>
<mat-error *ngIf="form.hasError('required', 'username')">Benutzername ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'username')">{{langs.currentLang?.username}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'username') && !form.hasError('required', 'username')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<div>
<mat-form-field>
<mat-label>Passwort</mat-label>
<mat-label>{{langs.currentLang?.password}}</mat-label>
<input type="password" matInput formControlName="password">
<mat-error *ngIf="form.hasError('maxlength', 'password')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort wiederholen</mat-label>
<mat-label>{{langs.currentLang?.passwordRepeat}}</mat-label>
<input type="password" matInput formControlName="passwordRepeat">
<mat-error *ngIf="form.hasError('maxlength', 'passwordRepeat')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
</div>
@@ -51,7 +51,7 @@
</mat-card-content>
<mat-card-actions id="actions">
<button mat-button color="primary" (click)="update()">Account aktualisieren</button>
<button mat-button color="warn" (click)="delete()">Account löschen</button>
<button mat-button color="primary" (click)="update()">{{langs.currentLang?.updateAccount}}</button>
<button mat-button color="warn" (click)="delete()">{{langs.currentLang?.deleteAccount}}</button>
</mat-card-actions>
</mat-card>

View File

@@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {CrudService} from "../../services/crud.service";
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {MatDialog} from "@angular/material/dialog";
@@ -7,6 +7,8 @@ import {firstValueFrom} from "rxjs";
import {User} from "../../entities/user";
import {MatSnackBar} from "@angular/material/snack-bar";
import {Router} from "@angular/router";
import {Language} from "../../entities/language";
import {LangService} from "../../services/lang.service";
@Component({
selector: 'app-profile',
@@ -22,14 +24,14 @@ export class ProfileComponent {
});
public error: string;
public constructor(public crud: CrudService, private router: Router, public dialog: MatDialog, private snackBar: MatSnackBar) {
public constructor(public langs: LangService, public crud: CrudService, private router: Router, public dialog: MatDialog, private snackBar: MatSnackBar) {
this.form.get("email").setValue(this.crud.user?.email);
this.form.get("username").setValue(this.crud.user?.username);
}
public async update() {
if (!this.form.valid) return;
const result = await this.openDialog("Änderungen speichern?");
const result = await this.openDialog(this.langs.currentLang?.saveChanges);
if (!result) return;
this.error = "";
@@ -39,7 +41,7 @@ export class ProfileComponent {
const passwordRepeat = this.form.get("passwordRepeat").value;
if (password != passwordRepeat) {
this.error = "Passwörter stimmen nicht überein";
this.error = this.langs.currentLang?.passwordsDontMatch;
return;
}
@@ -47,25 +49,25 @@ export class ProfileComponent {
const response = await this.crud.sendPutRequest("users", user);
if (!response.success) {
this.error = "Aktualiserung fehlgeschlagen!";
this.error = this.langs.currentLang?.updateFailed;
return;
}
await this.crud.loadUser(true);
this.form.reset();
this.snackBar.open("Account aktualisiert!", undefined, {duration: 2000});
this.snackBar.open(this.langs.currentLang?.updateAccount, undefined, {duration: 2000});
await this.router.navigate(["dashboard"]);
}
public async delete() {
const result = await this.openDialog("Möchtest du deinen Account wirklich löschen?", "All deine Projekte werden für immer gelöscht!", ['', 'warn']);
const result = await this.openDialog(this.langs.currentLang?.deleteQuestion, this.langs.currentLang?.deleteWarning, ['', 'warn']);
if (!result) return;
await this.crud.sendDeleteRequest("users");
this.crud.setAuthKey(undefined);
this.crud.user = undefined;
this.snackBar.open("Account gelöscht!", undefined, {duration: 2000});
this.snackBar.open(this.langs.currentLang?.accountDeleted, undefined, {duration: 2000});
await this.router.navigate(["login"]);
}
@@ -75,8 +77,8 @@ export class ProfileComponent {
return new Promise<boolean>(async (resolve) => {
const dialogRef = this.dialog.open(DialogComponent, {
data: {title, subtitle, buttons: [
{text: "Abbrechen", value: false, color: colors[0]},
{text: "Bestätigen", value: true, color: colors[1]},
{text: this.langs.currentLang?.cancel, value: false, color: colors[0]},
{text: this.langs.currentLang?.submit, value: true, color: colors[1]},
]}
});

View File

@@ -1,53 +1,53 @@
<mat-card>
<mat-card-title>Registrieren</mat-card-title>
<mat-card-title>{{langs.currentLang?.register}}</mat-card-title>
<mat-divider></mat-divider>
<mat-card-content>
<form [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<mat-label>{{langs.currentLang?.email}}</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'email')">{{langs.currentLang?.email}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
{{langs.currentLang?.validEmail}}
</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'email') && !form.hasError('required', 'email')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Benutzername</mat-label>
<mat-label>{{langs.currentLang?.username}}</mat-label>
<input type="text" matInput formControlName="username" required>
<mat-error *ngIf="form.hasError('required', 'username')">Benutzername ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'username')">{{langs.currentLang?.username}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'username') && !form.hasError('required', 'username')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort</mat-label>
<mat-label>{{langs.currentLang?.password}}</mat-label>
<input type="password" matInput formControlName="password" required>
<mat-error *ngIf="form.hasError('required', 'password')">Passwort ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'password')">{{langs.currentLang?.password}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'password') && !form.hasError('required', 'password')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort wiederholen</mat-label>
<mat-label>{{langs.currentLang?.passwordRepeat}}</mat-label>
<input type="password" matInput formControlName="passwordRepeat" required>
<mat-error *ngIf="form.hasError('required', 'passwordRepeat')">Passwort ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('required', 'passwordRepeat')">{{langs.currentLang?.password}} {{langs.currentLang?.isRequired}}</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'passwordRepeat') && !form.hasError('required', 'passwordRepeat')">
Der eingegebene Wert ist zu lang
{{langs.currentLang?.valueToLong}}
</mat-error>
</mat-form-field>
<mat-divider></mat-divider>
<span>Du hast bereits einen Account? <a routerLink="/login">Einloggen</a></span>
<span>{{langs.currentLang?.alreadyAccount}} <a routerLink="/login">{{langs.currentLang?.login}}</a></span>
<mat-error *ngIf="error">{{error}}</mat-error>
<button type="submit" mat-button>Registrieren</button>
<button type="submit" mat-button>{{langs.currentLang?.register}}</button>
</form>
</mat-card-content>
</mat-card>

View File

@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {CrudService} from "../../services/crud.service";
import {Router} from "@angular/router";
import {User} from "../../entities/user";
import {Language} from "../../entities/language";
import {LangService} from "../../services/lang.service";
@Component({
selector: 'app-register',
@@ -18,7 +20,7 @@ export class RegisterComponent {
});
public error: string;
public constructor(private crud: CrudService, private router: Router) {}
public constructor(public langs: LangService, private crud: CrudService, private router: Router) {}
public async submit() {
this.error = "";
@@ -28,14 +30,14 @@ export class RegisterComponent {
const passwordRepeat = this.form.get("passwordRepeat").value;
if (password != passwordRepeat) {
this.error = "Passwörter stimmen nicht überein";
this.error = this.langs.currentLang?.passwordsDontMatch;
return;
}
const user: User = {email, username, password};
const result = await this.crud.sendPostRequest<{token: string}>("users/register", user);
if (!result.success) {
this.error = "Registrierung fehlgeschlagen";
this.error = this.langs.currentLang?.registerFailed;
return;
}

View File

@@ -0,0 +1,49 @@
{
"selectLang": "Sprache auswählen",
"design": "Farbmodus wählen",
"profileSettings": "Profil Einstellungen",
"dashboard": "Übersicht",
"logout": "Ausloggen",
"addProject": "Neues Projekt",
"projects": "Projekte",
"running": "Läuft",
"stopped": "Gestoppt",
"welcome": "Willkommen",
"noProjects": "Du hast noch keine Projekte erstellt",
"open": "Öffnen",
"edit": "Bearbeiten",
"delete": "Löschen",
"name": "Name",
"cancel": "Abbrechen",
"createProject": "Projekt erstellen",
"editProject": "Projekt bearbeiten",
"profile": "Profil",
"profileSub": "Einstellungen",
"email": "E-Mail",
"username": "Benutzername",
"password": "Passwort",
"passwordRepeat": "Passwort wiederholen",
"updateAccount": "Account aktualisieren",
"deleteAccount": "Account löschen",
"saveChanges": "Änderungen speichern?",
"updateFailed": "Aktualisierung fehlgeschlagen",
"accountUpdated": "Account aktualisiert!",
"deleteQuestion": "Möchtest du deinen Account wirklich löschen?",
"deleteWarning": "All deine Projekte werden für immer gelöscht!",
"accountDeleted": "Account gelöscht!",
"submit": "Bestätigen",
"login": "Einloggen",
"register": "Registrieren",
"noAccount": "Du besitzt keinen Account?",
"alreadyAccount": "Du hast bereits einen Account?",
"valueToLong": "Der eingegebene Wert ist zu lang",
"validEmail": "Gebe eine gültige E-Mail Addresse an",
"isRequired": "ist erforderlich",
"emailOrPasswordWrong": "E-Mail oder Passwort ist falsch",
"passwordsDontMatch": "Passwörter stimmen nicht überein",
"registerFailed": "Registrierung fehlgeschlagen"
}

View File

@@ -0,0 +1,49 @@
{
"selectLang": "Select language",
"design": "Design",
"profileSettings": "Profile settings",
"dashboard": "Dashboard",
"logout": "Logout",
"addProject": "Add Project",
"projects": "Projects",
"running": "Running",
"stopped": "Stopped",
"welcome": "Welcome",
"noProjects": "You have not created a Project yet",
"open": "Open",
"edit": "Edit",
"delete": "Delete",
"name": "Name",
"cancel": "Cancel",
"createProject": "Create project",
"editProject": "Edit project",
"profile": "Profile",
"profileSub": "Settings",
"email": "Email",
"username": "Username",
"password": "Password",
"passwordRepeat": "Repeat password",
"updateAccount": "Update account",
"deleteAccount": "Delete account",
"saveChanges": "Save changes?",
"updateFailed": "Update failed",
"accountUpdated": "Account updated",
"deleteQuestion": "Do you really want to delete your account?",
"deleteWarning": "All your data will be lost!",
"accountDeleted": "Account deleted!",
"submit": "Submit",
"login": "Login",
"register": "Register",
"noAccount": "You don't have a account?",
"alreadyAccount": "You already have an account?",
"valueToLong": "The entered value is to long",
"validEmail": "Please enter a valid Email address",
"isRequired": "is required",
"emailOrPasswordWrong": "Email or Password is wrong",
"passwordsDontMatch": "Passwords doesn't match",
"registerFailed": "Register failed"
}

View File

@@ -6,7 +6,7 @@ Es handelt sich hierbei um ein einfach zu benutzendes WebInterface zum Erstellen
- [x] Automatische Docker Konfiguration
- [x] Automatisches DNS Mapping
- [x] Automatische SSL-Konfiguration mithilfe von NginxProxyManager
- [ ] Projekt Transferierung auf verschiedene Nodes
- [ ] Projekte exportieren / importieren
- [ ] Eigene Domains
- [ ] Mehrere Sprachen
@@ -60,7 +60,7 @@ services:
### SourceCode bearbeiten und Docker Images selber erstellen
Falls Sie den SourceCode selbst bearbeiten wollen, steht Ihnen dieser natürlich zur Verfügung. Um das Backend im Debug Modus zu verwenden sollten sie eine
``appsettings.Development.json`` Datei im Hauptverzeichnis anlegen um dort die entsprechende Konfiguration für die Entwicklungsumgebung erstellen.
``appsettings.Development.json`` Datei im Hauptverzeichnis anlegen, um dort die entsprechende Konfiguration für die Entwicklungsumgebung erstellen.
Zum Debuggen empfehle ich ``npm run dev:ssr`` um das Frontend zu starten und ``dotnet run`` um das Backend zu starten. Sofern Sie mit den Bearbeitungen fertig
sind, gibt es eine ``docker-compose.example.yml`` Datei im dem Repository die automatisch den SoruceCode neu baut und die Container startet.
Wahlweise können Sie diese mit ``docker build`` auch selbst bauen.