Базовый щаблон клиента

This commit is contained in:
Ymnuk 2023-07-26 11:48:45 +03:00
parent adcb7c9d2d
commit e9bd9665da
42 changed files with 369 additions and 246 deletions

2
.gitignore vendored
View File

@ -3,4 +3,4 @@
/example*.yml
/__debug_bin
/lib/tmpl/frontend/angular/node_modules
/lib/tmpl/frontend/angular/src/app/modules/api
/lib/tmpl/frontend/angular/src/app/module/api

View File

@ -8,7 +8,7 @@ contactUrl: ymnuktech.ru
contactEmail: ymnuktech@mail.ru
licenseName: Apache 2.0
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.html
host: example.com
host: localhost:4200
basePath: /api
db:
sqlite: true

View File

@ -50,25 +50,4 @@ func execCommands() {
}
}
if AppConfig.OutdirFrontend != "" {
if AppConfig.OutdirBackend != "" {
cmd := exec.Command("java",
"-jar",
"swagger-codegen-cli-2.4.32.jar",
"generate", "-i",
"docs/swagger.json",
"-l",
"typescript-angular",
"-o",
"../frontend/src/app/module/api",
"-c",
"options.json")
cmd.Dir = filepath.Join(AppConfig.OutdirFrontend)
cmd.Output()
if cmd.Err != nil {
log.Fatal(cmd.Err)
}
}
}
}

View File

@ -0,0 +1,48 @@
package lib
import (
"fmt"
"log"
"os/exec"
"path/filepath"
)
func execCommandsFrontendAngular() {
if AppConfig.OutdirFrontend != "" {
if AppConfig.OutdirBackend != "" {
cmd := exec.Command("java",
"-jar",
fmt.Sprintf("%s/swagger-codegen-cli-2.4.32.jar", AppConfig.OutdirFrontend),
"generate", "-i",
fmt.Sprintf("%s/docs/swagger.json", AppConfig.OutdirBackend),
"-l",
"typescript-angular",
"-o",
fmt.Sprintf("%s/src/app/module/api", AppConfig.OutdirFrontend),
"-c",
fmt.Sprintf("%s/options.json", AppConfig.OutdirFrontend),
)
cmd.Dir = filepath.Join(AppConfig.OutdirFrontend)
if buff, err := cmd.Output(); err != nil {
log.Fatal(err)
} else {
fmt.Println(string(buff))
}
if cmd.Err != nil {
log.Fatal(cmd.Err)
}
}
cmd := exec.Command("npm",
"i",
)
cmd.Dir = filepath.Join(AppConfig.OutdirFrontend)
if buff, err := cmd.Output(); err != nil {
log.Fatal(err)
} else {
fmt.Println(string(buff))
}
if cmd.Err != nil {
log.Fatal(cmd.Err)
}
}
}

View File

@ -0,0 +1,36 @@
package lib
import (
"fmt"
"log"
"os"
"regexp"
)
func fixApi() {
buff, err := os.ReadFile(fmt.Sprintf("%s/src/app/module/api/encoder.ts", AppConfig.OutdirFrontend))
if err != nil {
log.Fatal(err)
}
re1 := regexp.MustCompile(`\s{1,10}encodeKey`)
re2 := regexp.MustCompile(`\s{1,10}encodeValue`)
str := re1.ReplaceAllString(string(buff), "\n override encodeKey")
str = re2.ReplaceAllString(str, "\n override encodeValue")
//str := strings.ReplaceAll(string(buff), "encodeKey", "override encodeKey")
//str = strings.ReplaceAll(str, "encodeValue", "override encodeValue")
//fmt.Println(str)
if err = os.WriteFile(fmt.Sprintf("%s/src/app/module/api/encoder.ts", AppConfig.OutdirFrontend), []byte(str), 0755); err != nil {
log.Fatal(err)
}
re3 := regexp.MustCompile(`\s{1,10}protected basePath = '.+';`)
buff, err = os.ReadFile(fmt.Sprintf("%s/src/app/module/api/api/users.service.ts", AppConfig.OutdirFrontend))
if err != nil {
log.Fatal(err)
}
str = re3.ReplaceAllString(string(buff), "\n protected basePath = '/api';")
if err = os.WriteFile(fmt.Sprintf("%s/src/app/module/api/api/users.service.ts", AppConfig.OutdirFrontend), []byte(str), 0755); err != nil {
log.Fatal(err)
}
fmt.Println(str)
}

View File

@ -13,9 +13,13 @@ func generateFrontendAngularTmpl() {
dirs := []string{
filepath.Join(AppConfig.OutdirFrontend, ".vscode"),
filepath.Join(AppConfig.OutdirFrontend, "src", "assets"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "modules", "primeng"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "services"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "guard"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "module", "primeng"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "service"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "interfaces"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "template", "main"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "login"),
filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user-form"),
}
if len(dirs) > 0 {
for _, dir := range dirs {
@ -41,72 +45,72 @@ func generateFrontendAngularTmpl() {
FileOut: filepath.Join(AppConfig.OutdirFrontend, ".vscode", "tasks.json"),
},
{
FileIn: "tmpl/frontend/angular/src/app/modules/primeng/primeng.module.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "modules", "primeng", "primeng.module.ts"),
FileIn: "tmpl/frontend/angular/src/app/module/primeng/primeng.module.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "module", "primeng", "primeng.module.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/guards/auth.guard.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "guards", "auth.guard.ts"),
FileIn: "tmpl/frontend/angular/src/app/guard/auth.guard.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "guard", "auth.guard.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/services/auth.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "services", "auth.service.ts"),
FileIn: "tmpl/frontend/angular/src/app/service/auth.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "service", "auth.service.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/services/notify.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "services", "notify.service.ts"),
FileIn: "tmpl/frontend/angular/src/app/service/notify.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "service", "notify.service.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/services/token-interceptor.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "services", "token-interceptor.service.ts"),
FileIn: "tmpl/frontend/angular/src/app/service/token-interceptor.service.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "service", "token-interceptor.service.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/templates/main/main.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "templates", "main", "main.component.html"),
FileIn: "tmpl/frontend/angular/src/app/template/main/main.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "template", "main", "main.component.html"),
},
{
FileIn: "tmpl/frontend/angular/src/app/templates/main/main.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "templates", "main", "main.component.scss"),
FileIn: "tmpl/frontend/angular/src/app/template/main/main.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "template", "main", "main.component.scss"),
},
{
FileIn: "tmpl/frontend/angular/src/app/templates/main/main.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "templates", "main", "main.component.ts"),
FileIn: "tmpl/frontend/angular/src/app/template/main/main.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "template", "main", "main.component.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/login/login.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "login.component.html"),
FileIn: "tmpl/frontend/angular/src/app/page/login/login.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "login", "login.component.html"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/login/login.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "login", "login.component.scss"),
FileIn: "tmpl/frontend/angular/src/app/page/login/login.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "login", "login.component.scss"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/login/login.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "login", "login.component.ts"),
FileIn: "tmpl/frontend/angular/src/app/page/login/login.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "login", "login.component.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/user-form/user-form.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "user-form", "user-form.component.html"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user-form/user-form.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user-form", "user-form.component.html"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/user-form/user-form.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "user-form", "user-form.component.scss"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user-form/user-form.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user-form", "user-form.component.scss"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/user-form/user-form.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "user-form", "user-form.component.ts"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user-form/user-form.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user-form", "user-form.component.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/users.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "users.component.html"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user.component.html.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user.component.html"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/users.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "users.component.scss"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user.component.scss.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user.component.scss"),
},
{
FileIn: "tmpl/frontend/angular/src/app/pages/users/users.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "pages", "users", "users.component.ts"),
FileIn: "tmpl/frontend/angular/src/app/page/user/user.component.ts.tmpl",
FileOut: filepath.Join(AppConfig.OutdirFrontend, "src", "app", "page", "user", "user.component.ts"),
},
{
FileIn: "tmpl/frontend/angular/src/app/app-routing.module.ts.tmpl",
@ -206,4 +210,5 @@ func generateFrontendAngularTmpl() {
}
}
}
}

View File

@ -10,6 +10,8 @@ func Frontend() {
switch Project.Frontend.Lang {
case "angular":
generateFrontendAngularTmpl()
execCommandsFrontendAngular()
fixApi()
default:
log.Fatalf("Unknow param lang in frontend \"%s\"", Project.Frontend.Lang)
}

View File

@ -16,7 +16,7 @@ func Generate() {
if AppConfig.OutdirFrontend != "" {
// Генерация клиентской части
if err := os.MkdirAll(AppConfig.OutdirBackend, 0755); err != nil {
if err := os.MkdirAll(AppConfig.OutdirFrontend, 0755); err != nil {
log.Fatal(err)
}
Frontend()

View File

@ -17,6 +17,7 @@ type User struct {
Surname string `gorm:"column:surname;type:varchar;size:100;comment:Фамилия" json:"surname"`
Name string `gorm:"column:name;type:varchar;size:100;comment:Имя" json:"name"`
Farname *string `gorm:"column:farname;type:varchar;size:100;comment:Отчество" json:"farname"`
Email *string `gorm:"column:email;type:varchar;size:200;comment:Адрес электронной почты" json:"email"`
Password *string `gorm:"-" json:"password"`
UserRole []UserRole
Sha256pwd string `json:"-"`

View File

@ -37,7 +37,6 @@ var e *echo.Echo
// @license.name {{ .LicenseName }}
// @license.url {{ .LicenseUrl }}
// @host {{ .Host }}
// @BasePath {{ .BasePath }}
// @securityDefinitions.apikey BearerAuth

View File

@ -31,7 +31,7 @@ func Init(c *echo.Group) {
// @Failure 400 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/auth [post]
// @Router /auth/login [post]
func login(c echo.Context) error {
var err error
var userForm model.User

View File

@ -25,7 +25,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/{id} [delete]
// @Router /user/{id} [delete]
// @Security BearerAuth
func delete(c echo.Context) error {

View File

@ -23,7 +23,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/{id} [get]
// @Router /user/{id} [get]
// @Security BearerAuth
func get(c echo.Context) error {

View File

@ -14,6 +14,20 @@ import (
ldap "github.com/go-ldap/ldap/v3"
)
// LdapSearch ldapSearch
// @Summary Найти пользователя в LDAP
// @Description Возвращает список пользователей, найденных в LDAP
// @Tags users
// @Accept json
// @Produce json
// @Param value query string false "строка либо часть строки для поиска"
// @Success 200 {object} []model.User
// @Failure 400 {object} structs.Result
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /user/search-ldap [get]
// @Security BearerAuth
func searchLdap(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(*structs.JwtCustomClaims)

View File

@ -23,7 +23,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user [get]
// @Router /user [get]
// @Security BearerAuth
func listUsers(c echo.Context) error {

View File

@ -25,7 +25,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/{id}/lock [post]
// @Router /user/{id}/lock [post]
// @Security BearerAuth
func lock(c echo.Context) error {

View File

@ -26,7 +26,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user [post]
// @Router /user [post]
// @Security BearerAuth
func post(c echo.Context) error {

View File

@ -28,7 +28,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/{id} [post]
// @Router /user/{id} [put]
// @Security BearerAuth
func put(c echo.Context) error {

View File

@ -22,12 +22,12 @@ func Init(c *echo.Group) {
// @Tags users
// @Accept json
// @Produce json
// @Success 200 {object} structs.Result
// @Success 200 {object} []model.Role
// @Failure 400 {object} structs.Result
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/roles [get]
// @Router /user/roles [get]
// @Security BearerAuth
func listRoles(c echo.Context) error {
var err error

View File

@ -25,7 +25,7 @@ import (
// @Failure 401 {object} structs.Result
// @Failure 404 {object} structs.Result
// @Failure 500 {object} structs.Result
// @Router /api/user/{id}/unlock [post]
// @Router /user/{id}/unlock [post]
// @Security BearerAuth
func unlock(c echo.Context) error {

View File

@ -1,10 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { MainComponent } from './templates/main/main.component';
import { LoginComponent } from './pages/login/login.component';
import { UsersComponent } from './pages/users/users.component';
import { UserFormComponent } from './pages/users/user-form/user-form.component';
import { AuthGuard } from './guard/auth.guard';
import { MainComponent } from './template/main/main.component';
import { LoginComponent } from './page/login/login.component';
import { UsersComponent } from './page/user/user.component';
import { UserFormComponent } from './page/user/user-form/user-form.component';
const routes: Routes = [

View File

@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { APIS, ModelUser, UsersService } from './modules/api';
import { APIS, ModelUser, UsersService } from './module/api';
@Component({
selector: 'app-root',
@ -13,7 +13,7 @@ export class AppComponent {
private userService: UsersService
) {
let auth: ModelUser = { login: "test", password: "test" }
this.userService.apiAuthPost(auth)
this.userService.authLoginPost(auth)
}

View File

@ -8,12 +8,13 @@ import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { DialogService } from 'primeng/dynamicdialog';
import { AppRoutingModule } from './app-routing.module';
import { PrimengModule } from './modules/primeng/primeng.module';
import { LoginComponent } from './pages/login/login.component';
//import { UserFormComponent } from './pages/users/user-form/user-form.component';
import { UsersComponent } from './pages/users/users.component';
import { MainComponent } from './templates/main/main.component';
//import { TokenIncerceptorService } from './services/token-interceptor.service';
import { PrimengModule } from './module/primeng/primeng.module';
import { LoginComponent } from './page/login/login.component';
import { UsersComponent } from './page/user/user.component';
import { MainComponent } from './template/main/main.component';
import { UserFormComponent } from './page/user/user-form/user-form.component';
import { UsersService } from './module/api';
//import { TokenIncerceptorService } from './service/token-interceptor.service';
@NgModule({
declarations: [
@ -21,7 +22,7 @@ import { MainComponent } from './templates/main/main.component';
MainComponent,
LoginComponent,
UsersComponent,
//UserFormComponent
UserFormComponent
],
imports: [
BrowserModule,
@ -39,7 +40,8 @@ import { MainComponent } from './templates/main/main.component';
multi: true,
useClass: TokenIncerceptorService
},*/
DialogService
DialogService,
UsersService
],
bootstrap: [AppComponent]
})

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { AuthService } from '../services/auth.service';
import { AuthService } from '../service/auth.service';
@Injectable({
providedIn: 'root'

View File

@ -1,10 +1,10 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subscription, firstValueFrom } from 'rxjs';
import { AuthService } from 'src/app/services/auth.service';
import { AuthService } from 'src/app/service/auth.service';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { NotifyService } from 'src/app/services/notify.service';
import { UsersService } from 'src/app/modules/api';
import { NotifyService } from 'src/app/service/notify.service';
import { UsersService } from 'src/app/module/api';
@Component({
selector: 'app-login',
@ -46,7 +46,11 @@ export class LoginComponent implements OnInit, OnDestroy {
this.ngOnDestroy();
this.form.disable();
firstValueFrom(this.authService.login(this.form.value)).then(() => {
this.router.navigate(['/catalogue'])
if(this.authService.inRole('SEC_ADMIN')){
this.router.navigate(['/users'])
} else {
this.router.navigate(['/catalogue'])
}
}).catch(error => {
this.form.enable();
})

View File

@ -0,0 +1,105 @@
<div class="center">
<p-card header="Пользователь">
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<p class="p-float-label">
<p-autoComplete #login formControlName="login" [suggestions]="filteredUsers"
(completeMethod)="filterUser($event)" [minLength]="3" (onSelect)="onSelectedLogin($event)">
<ng-template let-item pTemplate="item">
{{item.surname}} {{item.name}} {{item.farname}}
</ng-template>
</p-autoComplete>
<label for="login" class="block">Логин</label>
<small id="login" class="p-invalid" *ngIf="form.get('login')?.invalid && form.get('login')?.touched">
<div *ngIf="form.get('login')?.errors?.['required']">Логин не должен быть пустым</div>
<div
*ngIf="form.get('login')?.errors?.['minlength'] && form.get('login')?.errors?.['minlength'].requiredLength">
Логин должен быть {{ form.get('login')?.errors?.['minlength']['requiredLength'] }} символов
и
более.
Сейчас {{ form.get('login')?.errors?.['minlength']['actualLength']}} символов.
</div>
</small>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="password">
<label for="float-input">Пароль</label>
<small id="float-input-help" class="p-invalid"
*ngIf="form.get('password')?.invalid && form.get('password')?.touched && form.get('password')?.errors?.['minlength'] && form.get('password')?.errors?.['minlength'].requiredLength">
<div
*ngIf="form.get('password')?.errors?.['minlength'] && form.get('password')?.errors?.['minlength'].requiredLength">
Пароль должен быть {{ form.get('password')?.errors?.['minlength']['requiredLength'] }} символов
и
более.
Сейчас {{ form.get('password')?.errors?.['minlength']['actualLength']}} символов.
</div>
</small>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="surname">
<label for="float-input">Фамилия</label>
<!--<input type="text" matInput placeholder="Фамилия" formControlName="surname">-->
<small id="float-input-help" class="p-invalid"
*ngIf="form.get('surname')?.invalid && form.get('surname')?.touched">
<div
*ngIf="form.get('surname')?.errors?.['minlength'] && form.get('surname')?.errors?.['minlength'].requiredLength">
Фамилия должна быть {{ form.get('surname')?.errors?.['minlength']['requiredLength'] }}
символов
и
более.
Сейчас {{ form.get('surname')?.errors?.['minlength']['actualLength']}} символов.
</div>
</small>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="name">
<label for="float-input">Имя</label>
<small id="float-input-help" class="p-invalid"
*ngIf="form.get('name')?.invalid && form.get('name')?.touched">
<div
*ngIf="form.get('name')?.errors?.['minlength'] && form.get('name')?.errors?.['minlength'].requiredLength">
Имя должно быть {{ form.get('name')?.errors?.['minlength']['requiredLength'] }} символов и
более.
Сейчас {{ form.get('name')?.errors?.['minlength']['actualLength']}} символов.
</div>
</small>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="farname">
<label for="float-input">Отчество</label>
<small id="float-input-help" class="p-invalid"
*ngIf="form.get('farname')?.invalid && form.get('farname')?.touched">
<div
*ngIf="form.get('farname')?.errors?.['minlength'] && form.get('farname')?.errors?.['minlength'].requiredLength">
Отчество должно быть {{ form.get('farname')?.errors?.['minlength']['requiredLength'] }}
символов и
более.
Сейчас {{ form.get('farname')?.errors?.['minlength']['actualLength']}} символов.
</div>
</small>
</p>
<p>
<p-checkbox formControlName="ldap" [binary]="true" inputId="binary"></p-checkbox>
<label for="binary">Аутентификация через LDAP</label>
</p>
<h2>Роли</h2>
<section>
<ul>
<li *ngFor="let role of roles">
<p-checkbox [formControlName]="getFormControlNameRole(role)" [binary]="true" inputId="binary">
</p-checkbox>
<label for="binary">{{role.description}}</label>
</li>
</ul>
</section>
<div class="button">
<button pButton pRipple type="submit" label="Сохранить" class="p-button-raised"
[disabled]="form.invalid || form.disabled"></button>
<!--<button type="submit" mat-flat-button color="primary"
[disabled]="form.invalid || form.disabled">Сохранить</button>-->
</div>
</form>
</p-card>
</div>

View File

@ -1,19 +1,16 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms';
import { UserService } from 'src/app/services/user.service';
import { Role } from 'src/app/interfaces/role';
import { IUser } from 'src/app/interfaces/user';
import { Subscription, Observable, firstValueFrom } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { MessageService } from 'primeng/api';
import { ModelRole, ModelUser, UsersService } from 'src/app/module/api';
@Component({
selector: 'app-user-form',
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.scss']
})
export class UserFormComponent implements OnInit, OnDestroy {
export class UserFormComponent implements OnInit, AfterViewInit, OnDestroy {
private id?: string; // Идентификатор пользователя при редактировании
@ -25,15 +22,15 @@ export class UserFormComponent implements OnInit, OnDestroy {
farname: new UntypedFormControl(null, [Validators.minLength(3)]),
email: new UntypedFormControl(null, [Validators.email]),
ldap: new UntypedFormControl(false)
});;
});
public roles?: Role[];
public roles?: ModelRole[];
loginChangedSub: Subscription | null = null;
filteredUsers: IUser[] = [];
filteredUsers: ModelUser[] = [];
constructor(
private userService: UserService,
private userService: UsersService,
private router: Router,
private route: ActivatedRoute,
// private toast: ToastrService
@ -41,31 +38,14 @@ export class UserFormComponent implements OnInit, OnDestroy {
) {
}
ngOnDestroy(): void {
if (this.loginChangedSub != null) {
this.loginChangedSub.unsubscribe();
this.loginChangedSub = null;
}
}
ngOnInit(): void {
// Получим все возможные роли и выведем на форму
firstValueFrom(this.userService.roles())
.then((res: Role[]) => {
for (const role of res) {
this.form.addControl(`${this.getFormControlNameRole(role)}`, new UntypedFormControl());
this.roles = res;
}
});
ngAfterViewInit(): void {
// Получим переданные параметры (id)
this.route.params.subscribe((params: Params) => {
if (params['id'] != null) {
this.id = params['id'];
// Получаем данные с сервера о выбранном пользователе
firstValueFrom(this.userService.get(this.id))
.then((result: IUser) => {
firstValueFrom(this.userService.userIdGet(this.id as string))
.then((result: ModelUser) => {
this.form.patchValue(result);
if (result.roles && result.roles.length > 0) {
for (const role of result.roles) {
@ -84,21 +64,40 @@ export class UserFormComponent implements OnInit, OnDestroy {
}
});
}
ngOnDestroy(): void {
if (this.loginChangedSub != null) {
this.loginChangedSub.unsubscribe();
this.loginChangedSub = null;
}
}
ngOnInit(): void {
// Получим все возможные роли и выведем на форму
firstValueFrom(this.userService.userRolesGet())
.then((res: ModelRole[]) => {
for (const role of res) {
this.form.addControl(`${this.getFormControlNameRole(role)}`, new UntypedFormControl());
this.roles = res;
}
});
}
filterUser(event: any): void {
if (event.query != null && typeof (event.query) === 'string' && event.query.length >= 3) {
firstValueFrom(this.userService.searchLDAP(event.query)).then((users: IUser[]) => {
firstValueFrom(this.userService.userSearchLdapGet(event.query)).then((users: ModelUser[]) => {
this.filteredUsers = users;
}).catch(err => {
console.error(err);
})
}
}
getFormControlNameRole(role: Role): string {
getFormControlNameRole(role: ModelRole): string {
return `role_${role.name}`;
}
onSubmit(): void {
const user: IUser = {
const user: ModelUser = {
//login: this.form.value.login.login,
surname: this.form.value.surname,
name: this.form.value.name,
@ -125,15 +124,15 @@ export class UserFormComponent implements OnInit, OnDestroy {
}
}
let obs$: Observable<IUser> | null = null;
let obs$: Observable<ModelUser> | null = null;
if (this.id != null) {
user.id = this.id;
obs$ = this.userService.update(user);
obs$ = this.userService.userIdPut(user.id, user);
} else {
delete user.id;
obs$ = this.userService.create(user);
obs$ = this.userService.userPost(user);
}
firstValueFrom(obs$)
firstValueFrom(obs$ as Observable<ModelUser>)
.then(() => {
this.router.navigate(['/users']);
})
@ -142,8 +141,9 @@ export class UserFormComponent implements OnInit, OnDestroy {
});
}
onSelectedLogin(user: IUser): void {
onSelectedLogin(user: ModelUser): void {
this.form.patchValue({
login: user.login,
surname: user.surname,
name: user.name,
farname: user.farname,

View File

@ -1,17 +1,14 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
//import { IUser } from 'src/app/interfaces/user';
// import { UserService } from 'src/app/services/user.service';
import { firstValueFrom, Observable, Subscription } from 'rxjs';
//import { IResult } from 'src/app/interfaces/result';
import { Router } from '@angular/router';
import { UntypedFormControl } from '@angular/forms';
import { MessageService } from 'primeng/api';
import { ModelUser, StructsResult, UsersService } from 'src/app/modules/api';
import { ModelUser, StructsResult, UsersService } from 'src/app/module/api';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UsersComponent implements OnInit, OnDestroy {
@ -26,7 +23,6 @@ export class UsersComponent implements OnInit, OnDestroy {
emailControl = new UntypedFormControl();
constructor(
//private userService: UserService,
private userService: UsersService,
private toast: MessageService,
private router: Router
@ -35,9 +31,11 @@ export class UsersComponent implements OnInit, OnDestroy {
ngOnDestroy(): void { }
private refreshData(): void {
firstValueFrom(this.userService.apiUserGet()).then((res: ModelUser[]) => {
this.users = res;
})
firstValueFrom(
this.userService.userGet())
.then((res: ModelUser[]) => {
this.users = res;
});
}
ngOnInit(): void {
@ -55,9 +53,9 @@ export class UsersComponent implements OnInit, OnDestroy {
public onLockUnlock(item: ModelUser): void {
let $lockObs: Observable<StructsResult> | null = null;
if (item.attempt && item.attempt >= 5) {
$lockObs = this.userService.apiUserIdLockPost(item.id as string);
$lockObs = this.userService.userIdUnlockPost(item.id as string);
} else {
$lockObs = this.userService.apiUserIdUnlockPost(item.id as string);
$lockObs = this.userService.userIdLockPost(item.id as string);
}
firstValueFrom($lockObs)
.then(_ => {
@ -69,7 +67,7 @@ export class UsersComponent implements OnInit, OnDestroy {
}
onDelete(item: ModelUser): void {
firstValueFrom(this.userService.apiUserIdDelete(item.id as string))
firstValueFrom(this.userService.userIdDelete(item.id as string))
.then((result: StructsResult) => {
if (result.result) {
this.refreshData();

View File

@ -1,99 +0,0 @@
<p-card header="Пользователь">
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<p class="p-float-label">
<p-autoComplete #login [suggestions]="filteredUsers" (completeMethod)="filterUser($event)" [minLength]="3"
field="login" (onSelect)="onSelectedLogin($event)" formControlName="login">
<ng-template let-item pTemplate="item">
{{item.surname}} {{item.name}} {{item.farname}}
</ng-template>
</p-autoComplete>
<label for="login" class="block">Логин</label>
<mat-error *ngIf="form.get('login')?.invalid && form.get('login')?.touched">
<div *ngIf="form.get('login')?.errors?.['required']">Логин не должен быть пустым</div>
<div
*ngIf="form.get('login')?.errors?.['minlength'] && form.get('login')?.errors?.['minlength'].requiredLength">
Логин должен быть {{ form.get('login')?.errors?.['minlength']['requiredLength'] }} символов
и
более.
Сейчас {{ form.get('login')?.errors?.['minlength']['actualLength']}} символов.
</div>
</mat-error>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="password">
<label for="float-input">Пароль</label>
<mat-error
*ngIf="form.get('password')?.invalid && form.get('password')?.touched && form.get('password')?.errors?.['minlength'] && form.get('password')?.errors?.['minlength'].requiredLength">
<div
*ngIf="form.get('password')?.errors?.['minlength'] && form.get('password')?.errors?.['minlength'].requiredLength">
Пароль должен быть {{ form.get('password')?.errors?.['minlength']['requiredLength'] }} символов
и
более.
Сейчас {{ form.get('password')?.errors?.['minlength']['actualLength']}} символов.
</div>
</mat-error>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="surname">
<label for="float-input">Фамилия</label>
<!--<input type="text" matInput placeholder="Фамилия" formControlName="surname">-->
<mat-error *ngIf="form.get('surname')?.invalid && form.get('surname')?.touched">
<div
*ngIf="form.get('surname')?.errors?.['minlength'] && form.get('surname')?.errors?.['minlength'].requiredLength">
Фамилия должна быть {{ form.get('surname')?.errors?.['minlength']['requiredLength'] }}
символов
и
более.
Сейчас {{ form.get('surname')?.errors?.['minlength']['actualLength']}} символов.
</div>
</mat-error>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="name">
<label for="float-input">Имя</label>
<mat-error *ngIf="form.get('name')?.invalid && form.get('name')?.touched">
<div
*ngIf="form.get('name')?.errors?.['minlength'] && form.get('name')?.errors?.['minlength'].requiredLength">
Имя должно быть {{ form.get('name')?.errors?.['minlength']['requiredLength'] }} символов и
более.
Сейчас {{ form.get('name')?.errors?.['minlength']['actualLength']}} символов.
</div>
</mat-error>
</p>
<p class="p-float-label">
<input id="float-input" type="text" pInputText formControlName="farname">
<label for="float-input">Отчество</label>
<mat-error *ngIf="form.get('farname')?.invalid && form.get('farname')?.touched">
<div
*ngIf="form.get('farname')?.errors?.['minlength'] && form.get('farname')?.errors?.['minlength'].requiredLength">
Отчество должно быть {{ form.get('farname')?.errors?.['minlength']['requiredLength'] }}
символов и
более.
Сейчас {{ form.get('farname')?.errors?.['minlength']['actualLength']}} символов.
</div>
</mat-error>
</p>
<p>
<p-checkbox formControlName="ldap" [binary]="true" inputId="binary"></p-checkbox>
<label for="binary">Аутентификация через LDAP</label>
</p>
<h2>Роли</h2>
<section>
<ul>
<li *ngFor="let role of roles">
<p-checkbox [formControlName]="getFormControlNameRole(role)" [binary]="true" inputId="binary">
</p-checkbox>
<label for="binary">{{role.description}}</label>
</li>
</ul>
</section>
<div class="button">
<button pButton pRipple type="submit" label="Сохранить" class="p-button-raised"
[disabled]="form.invalid || form.disabled"></button>
<!--<button type="submit" mat-flat-button color="primary"
[disabled]="form.invalid || form.disabled">Сохранить</button>-->
</div>
</form>
</p-card>

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Router } from '@angular/router';
import jwt_decode from "jwt-decode";
import { ModelUser, StructsResult, UsersService } from '../modules/api';
import { ModelUser, StructsResult, UsersService } from '../module/api';
@Injectable({
providedIn: 'root'
@ -22,6 +22,11 @@ export class AuthService {
) {
if (localStorage.getItem('auth-token') != null) {
this._tkn = localStorage.getItem('auth-token');
if (this.userService.configuration.apiKeys == null) {
this.userService.configuration.apiKeys = {}
}
this.userService.configuration.apiKeys['Authorization'] = `Bearer ${this._tkn}`;
let jwt = jwt_decode(this.token as string);
if ((jwt as any).roles) {
this._roles = (jwt as any).roles;
@ -30,7 +35,7 @@ export class AuthService {
}
login(login: ModelUser): Observable<StructsResult> {
return this.userService.apiAuthPost(login).pipe(
return this.userService.authLoginPost(login).pipe(
tap(
(result: StructsResult) => {
localStorage.setItem('auth-token', result.data);

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { AuthService } from 'src/app/services/auth.service';
import { AuthService } from 'src/app/service/auth.service';
@Component({
selector: 'app-main',
@ -27,6 +27,14 @@ export class MainComponent implements OnInit {
this.auth.logout()
}
});
if (this.auth.inRole('SEC_ADMIN')) {
items.push({
label: 'Пользователи',
command: () => {
this.router.navigate(['/users'])
}
});
}
} else {
items.push({

20
main.go
View File

@ -24,15 +24,18 @@ func main() {
flag.Usage()
os.Exit(0)
}
var err error
if lib.AppConfig.Filename, err = filepath.Abs(lib.AppConfig.Filename); err != nil {
log.Fatal(err)
}
lib.Project = &structs.Project{}
if _, err := os.Stat(lib.AppConfig.Filename); os.IsNotExist(err) {
if _, err = os.Stat(lib.AppConfig.Filename); os.IsNotExist(err) {
log.Fatal(`Метафайл не найден`)
} else if err != nil {
log.Fatal(err)
}
var buff []byte
var err error
if buff, err = os.ReadFile(lib.AppConfig.Filename); err != nil {
log.Fatal(err)
@ -48,6 +51,19 @@ func main() {
if err = defaults.Set(lib.Project); err != nil {
log.Fatal(err)
}
// Подгтовка абсолютных путей
if lib.AppConfig.OutdirBackend != "" {
if lib.AppConfig.OutdirBackend, err = filepath.Abs(lib.AppConfig.OutdirBackend); err != nil {
log.Fatal(err)
}
}
if lib.AppConfig.OutdirFrontend != "" {
if lib.AppConfig.OutdirFrontend, err = filepath.Abs(lib.AppConfig.OutdirFrontend); err != nil {
log.Fatal(err)
}
}
lib.PrepareMetadata(lib.Project)
lib.Generate()