From 6dfb5a4b48f37d0453511ab9508eb69fdff3c02c Mon Sep 17 00:00:00 2001 From: "leon.hoppe" Date: Sat, 25 Mar 2023 18:36:19 +0100 Subject: [PATCH] Fixed minor issues + added language support --- ProjectManager.Backend/Apis/IProjectApi.cs | 2 + ProjectManager.Backend/Apis/IUserApi.cs | 3 +- .../Controllers/ProjectController.cs | 10 ++++ ProjectManager.Backend/Program.cs | 7 ++- ProjectManager.Frontend/server.ts | 9 +++- ProjectManager.Frontend/src/app/app.module.ts | 6 ++- .../navigation/navigation.component.html | 24 ++++++--- .../navigation/navigation.component.scss | 4 ++ .../navigation/navigation.component.ts | 6 ++- .../src/app/entities/language.ts | 54 +++++++++++++++++++ .../src/app/services/crud.service.ts | 2 +- .../src/app/services/lang.service.ts | 41 ++++++++++++++ .../sites/dashboard/dashboard.component.html | 12 ++--- .../sites/dashboard/dashboard.component.ts | 17 +++++- .../src/app/sites/login/login.component.html | 16 +++--- .../src/app/sites/login/login.component.ts | 8 +-- .../app/sites/profile/profile.component.html | 30 +++++------ .../app/sites/profile/profile.component.ts | 22 ++++---- .../sites/register/register.component.html | 32 +++++------ .../app/sites/register/register.component.ts | 10 ++-- .../src/assets/languages/de-DE.json | 49 +++++++++++++++++ .../src/assets/languages/en-US.json | 49 +++++++++++++++++ README.md | 4 +- 23 files changed, 334 insertions(+), 83 deletions(-) create mode 100644 ProjectManager.Frontend/src/app/entities/language.ts create mode 100644 ProjectManager.Frontend/src/app/services/lang.service.ts create mode 100644 ProjectManager.Frontend/src/assets/languages/de-DE.json create mode 100644 ProjectManager.Frontend/src/assets/languages/en-US.json diff --git a/ProjectManager.Backend/Apis/IProjectApi.cs b/ProjectManager.Backend/Apis/IProjectApi.cs index 893a9cd..9235232 100644 --- a/ProjectManager.Backend/Apis/IProjectApi.cs +++ b/ProjectManager.Backend/Apis/IProjectApi.cs @@ -82,6 +82,8 @@ public class ProjectApi : IProjectApi { await _docker.DeleteContainer(project.ContainerName); await _proxy.RemoveLocation(project.ProxyId, project.CertificateId); + + Directory.Delete(_options.Root + projectId, true); _context.Projects.Remove(project); await _context.SaveChangesAsync(); diff --git a/ProjectManager.Backend/Apis/IUserApi.cs b/ProjectManager.Backend/Apis/IUserApi.cs index 8d12edf..cf0fcbf 100644 --- a/ProjectManager.Backend/Apis/IUserApi.cs +++ b/ProjectManager.Backend/Apis/IUserApi.cs @@ -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; diff --git a/ProjectManager.Backend/Controllers/ProjectController.cs b/ProjectManager.Backend/Controllers/ProjectController.cs index 7687980..88b0990 100644 --- a/ProjectManager.Backend/Controllers/ProjectController.cs +++ b/ProjectManager.Backend/Controllers/ProjectController.cs @@ -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 StartProject(string projectId) { diff --git a/ProjectManager.Backend/Program.cs b/ProjectManager.Backend/Program.cs index c5d8059..2dff518 100644 --- a/ProjectManager.Backend/Program.cs +++ b/ProjectManager.Backend/Program.cs @@ -36,7 +36,7 @@ if (app.Environment.IsDevelopment()) { app.UseCors( options => options - .WithOrigins(new []{app.Configuration.GetSection("Frontend").Get() ?? ""}) + .WithOrigins(app.Configuration.GetSection("Frontend").Get() ?? "") .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(); \ No newline at end of file diff --git a/ProjectManager.Frontend/server.ts b/ProjectManager.Frontend/server.ts index 3191387..99242dd 100644 --- a/ProjectManager.Frontend/server.ts +++ b/ProjectManager.Frontend/server.ts @@ -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' diff --git a/ProjectManager.Frontend/src/app/app.module.ts b/ProjectManager.Frontend/src/app/app.module.ts index ee2aeeb..a6703dd 100644 --- a/ProjectManager.Frontend/src/app/app.module.ts +++ b/ProjectManager.Frontend/src/app/app.module.ts @@ -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: [ @@ -40,7 +41,7 @@ import { ProjectComponent } from './sites/project/project.component'; ProjectComponent ], imports: [ - BrowserModule.withServerTransition({ appId: 'serverApp' }), + BrowserModule.withServerTransition({appId: 'serverApp'}), AppRoutingModule, BrowserAnimationsModule, HttpClientModule, @@ -57,7 +58,8 @@ import { ProjectComponent } from './sites/project/project.component'; FormsModule, MatDialogModule, MatSnackBarModule, - MatTooltipModule + MatTooltipModule, + MatMenuModule ], providers: [], bootstrap: [AppComponent] diff --git a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.html b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.html index cdb8b18..e07ecca 100644 --- a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.html +++ b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.html @@ -8,24 +8,24 @@ dashboard -
Übersicht
+
{{langs.currentLang?.dashboard}}
logout -
Ausloggen
+
{{langs.currentLang?.logout}}
add -
Neues Projekt
+
{{langs.currentLang?.createProject}}
-
Projekte
- +
{{langs.currentLang?.projects}}
+ open_in_new
{{project.name}}
-
{{project.running ? 'Läuft' : 'Gestoppt'}}
+
{{project.running ? langs.currentLang?.running : langs.currentLang?.stopped}}
@@ -37,8 +37,16 @@ Project Manager
- - + + + + + + +
diff --git a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.scss b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.scss index 80752bd..2f61dec 100644 --- a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.scss +++ b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.scss @@ -14,6 +14,10 @@ mat-drawer-container { height: calc(100vh - 80px); overflow-y: auto; } + + .project mat-icon { + margin-block: auto; + } } } diff --git a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.ts b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.ts index 40b7227..5a6fb6b 100644 --- a/ProjectManager.Frontend/src/app/components/navigation/navigation.component.ts +++ b/ProjectManager.Frontend/src/app/components/navigation/navigation.component.ts @@ -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"; } diff --git a/ProjectManager.Frontend/src/app/entities/language.ts b/ProjectManager.Frontend/src/app/entities/language.ts new file mode 100644 index 0000000..c3db1da --- /dev/null +++ b/ProjectManager.Frontend/src/app/entities/language.ts @@ -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, +} diff --git a/ProjectManager.Frontend/src/app/services/crud.service.ts b/ProjectManager.Frontend/src/app/services/crud.service.ts index 89f8a70..da42104 100644 --- a/ProjectManager.Frontend/src/app/services/crud.service.ts +++ b/ProjectManager.Frontend/src/app/services/crud.service.ts @@ -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); diff --git a/ProjectManager.Frontend/src/app/services/lang.service.ts b/ProjectManager.Frontend/src/app/services/lang.service.ts new file mode 100644 index 0000000..6d51a70 --- /dev/null +++ b/ProjectManager.Frontend/src/app/services/lang.service.ts @@ -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 = new Map(); + public allLanguages: string[] = []; + public currentLang: Language; + + constructor(private crud: CrudService, private storage: StorageService) { + this.loadLanguages(); + } + + private async loadLanguages() { + this.languages = new Map(); + 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(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); + } + +} diff --git a/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.html b/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.html index 1917565..d00c139 100644 --- a/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.html +++ b/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.html @@ -1,9 +1,9 @@
-

Willkommen {{crud.user.username}}

+

{{langs.currentLang?.welcome}} {{crud.user.username}}

-

Projekte

+

{{langs.currentLang?.projects}}

- Du hast noch keine Projekte erstellt + {{langs.currentLang?.noProjects}} {{project.name}} @@ -11,9 +11,9 @@ - - - + + + diff --git a/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.ts b/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.ts index 0b2d5e2..bb7dff9 100644 --- a/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.ts +++ b/ProjectManager.Frontend/src/app/sites/dashboard/dashboard.component.ts @@ -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, { diff --git a/ProjectManager.Frontend/src/app/sites/login/login.component.html b/ProjectManager.Frontend/src/app/sites/login/login.component.html index 9b2a4d6..334c361 100644 --- a/ProjectManager.Frontend/src/app/sites/login/login.component.html +++ b/ProjectManager.Frontend/src/app/sites/login/login.component.html @@ -1,29 +1,29 @@ - Einloggen + {{langs.currentLang?.login}}
- E-Mail + {{langs.currentLang?.email}} - E-Mail ist erforderlich + {{langs.currentLang?.email}} {{langs.currentLang?.isRequired}} - Bitte geben Sie eine gültige E-Mail-Adresse ein + {{langs.currentLang?.validEmail}} - Passwort + {{langs.currentLang?.password}} - Passwort ist erforderlich + {{langs.currentLang?.password}} {{langs.currentLang?.isRequired}} - Du besitzt keinen Account? Registrieren + {{langs.currentLang?.noAccount}} {{langs.currentLang?.register}} {{error}} - +
diff --git a/ProjectManager.Frontend/src/app/sites/login/login.component.ts b/ProjectManager.Frontend/src/app/sites/login/login.component.ts index 6d916f3..67ba5be 100644 --- a/ProjectManager.Frontend/src/app/sites/login/login.component.ts +++ b/ProjectManager.Frontend/src/app/sites/login/login.component.ts @@ -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; } } } diff --git a/ProjectManager.Frontend/src/app/sites/profile/profile.component.html b/ProjectManager.Frontend/src/app/sites/profile/profile.component.html index 6f89397..4f55009 100644 --- a/ProjectManager.Frontend/src/app/sites/profile/profile.component.html +++ b/ProjectManager.Frontend/src/app/sites/profile/profile.component.html @@ -1,46 +1,46 @@ - Profil - Einstellungen + {{langs.currentLang?.profile}} + {{langs.currentLang?.profileSub}}
- E-Mail + {{langs.currentLang?.email}} - E-Mail ist erforderlich + {{langs.currentLang?.email}} {{langs.currentLang?.isRequired}} - Bitte geben Sie eine gültige E-Mail-Adresse ein + {{langs.currentLang?.validEmail}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Benutzername + {{langs.currentLang?.username}} - Benutzername ist erforderlich + {{langs.currentLang?.username}} {{langs.currentLang?.isRequired}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}}
- Passwort + {{langs.currentLang?.password}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Passwort wiederholen + {{langs.currentLang?.passwordRepeat}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}}
@@ -51,7 +51,7 @@ - - + + diff --git a/ProjectManager.Frontend/src/app/sites/profile/profile.component.ts b/ProjectManager.Frontend/src/app/sites/profile/profile.component.ts index 63262cc..b415de0 100644 --- a/ProjectManager.Frontend/src/app/sites/profile/profile.component.ts +++ b/ProjectManager.Frontend/src/app/sites/profile/profile.component.ts @@ -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(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]}, ]} }); diff --git a/ProjectManager.Frontend/src/app/sites/register/register.component.html b/ProjectManager.Frontend/src/app/sites/register/register.component.html index 82d2d46..d14c062 100644 --- a/ProjectManager.Frontend/src/app/sites/register/register.component.html +++ b/ProjectManager.Frontend/src/app/sites/register/register.component.html @@ -1,53 +1,53 @@ - Registrieren + {{langs.currentLang?.register}} - E-Mail + {{langs.currentLang?.email}} - E-Mail ist erforderlich + {{langs.currentLang?.email}} {{langs.currentLang?.isRequired}} - Bitte geben Sie eine gültige E-Mail-Adresse ein + {{langs.currentLang?.validEmail}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Benutzername + {{langs.currentLang?.username}} - Benutzername ist erforderlich + {{langs.currentLang?.username}} {{langs.currentLang?.isRequired}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Passwort + {{langs.currentLang?.password}} - Passwort ist erforderlich + {{langs.currentLang?.password}} {{langs.currentLang?.isRequired}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Passwort wiederholen + {{langs.currentLang?.passwordRepeat}} - Passwort ist erforderlich + {{langs.currentLang?.password}} {{langs.currentLang?.isRequired}} - Der eingegebene Wert ist zu lang + {{langs.currentLang?.valueToLong}} - Du hast bereits einen Account? Einloggen + {{langs.currentLang?.alreadyAccount}} {{langs.currentLang?.login}} {{error}} - + diff --git a/ProjectManager.Frontend/src/app/sites/register/register.component.ts b/ProjectManager.Frontend/src/app/sites/register/register.component.ts index ee9712f..e48a3e7 100644 --- a/ProjectManager.Frontend/src/app/sites/register/register.component.ts +++ b/ProjectManager.Frontend/src/app/sites/register/register.component.ts @@ -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; } diff --git a/ProjectManager.Frontend/src/assets/languages/de-DE.json b/ProjectManager.Frontend/src/assets/languages/de-DE.json new file mode 100644 index 0000000..2d6f6e1 --- /dev/null +++ b/ProjectManager.Frontend/src/assets/languages/de-DE.json @@ -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" +} diff --git a/ProjectManager.Frontend/src/assets/languages/en-US.json b/ProjectManager.Frontend/src/assets/languages/en-US.json new file mode 100644 index 0000000..57159da --- /dev/null +++ b/ProjectManager.Frontend/src/assets/languages/en-US.json @@ -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" +} diff --git a/README.md b/README.md index b2c10fb..261e224 100644 --- a/README.md +++ b/README.md @@ -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.