mirror of https://github.com/abpframework/abp.git
242 changed files with 525344 additions and 1207 deletions
@ -0,0 +1,3 @@ |
|||||
|
<h1> @abp/ng.account </h1> |
||||
|
|
||||
|
[docs.abp.io](https://docs.abp.io) |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
||||
|
"dest": "../../dist/account/config", |
||||
|
"lib": { |
||||
|
"entryFile": "src/public-api.ts" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { ModuleWithProviders, NgModule } from '@angular/core'; |
||||
|
import { ACCOUNT_ROUTE_PROVIDERS } from './providers/route.provider'; |
||||
|
|
||||
|
@NgModule() |
||||
|
export class AccountConfigModule { |
||||
|
static forRoot(): ModuleWithProviders<AccountConfigModule> { |
||||
|
return { |
||||
|
ngModule: AccountConfigModule, |
||||
|
providers: [ACCOUNT_ROUTE_PROVIDERS], |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './route-names'; |
||||
@ -0,0 +1,6 @@ |
|||||
|
export const enum eAccountRouteNames { |
||||
|
Account = 'AbpAccount::Menu:Account', |
||||
|
Login = 'AbpAccount::Login', |
||||
|
Register = 'AbpAccount::Register', |
||||
|
ManageProfile = 'AbpAccount::ManageYourProfile', |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './route.provider'; |
||||
@ -0,0 +1,39 @@ |
|||||
|
import { eLayoutType, RoutesService } from '@abp/ng.core'; |
||||
|
import { APP_INITIALIZER } from '@angular/core'; |
||||
|
import { eAccountRouteNames } from '../enums/route-names'; |
||||
|
|
||||
|
export const ACCOUNT_ROUTE_PROVIDERS = [ |
||||
|
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, |
||||
|
]; |
||||
|
|
||||
|
export function configureRoutes(routes: RoutesService) { |
||||
|
return () => { |
||||
|
routes.add([ |
||||
|
{ |
||||
|
path: '/account', |
||||
|
name: eAccountRouteNames.Account, |
||||
|
invisible: true, |
||||
|
layout: eLayoutType.application, |
||||
|
order: 1, |
||||
|
}, |
||||
|
{ |
||||
|
path: '/account/login', |
||||
|
name: eAccountRouteNames.Login, |
||||
|
parentName: eAccountRouteNames.Account, |
||||
|
order: 1, |
||||
|
}, |
||||
|
{ |
||||
|
path: '/account/register', |
||||
|
name: eAccountRouteNames.Register, |
||||
|
parentName: eAccountRouteNames.Account, |
||||
|
order: 2, |
||||
|
}, |
||||
|
{ |
||||
|
path: '/account/manage-profile', |
||||
|
name: eAccountRouteNames.ManageProfile, |
||||
|
parentName: eAccountRouteNames.Account, |
||||
|
order: 3, |
||||
|
}, |
||||
|
]); |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
export * from './account-config.module'; |
||||
|
export * from './enums'; |
||||
|
export * from './providers'; |
||||
@ -0,0 +1,6 @@ |
|||||
|
const jestConfig = require('../../jest.config'); |
||||
|
|
||||
|
module.exports = { |
||||
|
...jestConfig, |
||||
|
name: 'account', |
||||
|
}; |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
||||
|
"dest": "../../dist/account", |
||||
|
"lib": { |
||||
|
"entryFile": "src/public-api.ts" |
||||
|
}, |
||||
|
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"] |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
module.exports = { |
||||
|
entryPoints: { |
||||
|
'.': {}, |
||||
|
'./config': {} |
||||
|
}, |
||||
|
}; |
||||
@ -0,0 +1,16 @@ |
|||||
|
{ |
||||
|
"name": "@abp/ng.account", |
||||
|
"version": "4.3.0", |
||||
|
"homepage": "https://abp.io", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/abpframework/abp.git" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@abp/ng.theme.shared": "~3.3.2", |
||||
|
"tslib": "^2.0.0" |
||||
|
}, |
||||
|
"publishConfig": { |
||||
|
"access": "public" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
import { |
||||
|
AuthGuard, |
||||
|
DynamicLayoutComponent, |
||||
|
ReplaceableComponents, |
||||
|
ReplaceableRouteContainerComponent, |
||||
|
} from '@abp/ng.core'; |
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { RouterModule, Routes } from '@angular/router'; |
||||
|
import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.component'; |
||||
|
import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component'; |
||||
|
import { LoginComponent } from './components/login/login.component'; |
||||
|
import { ManageProfileComponent } from './components/manage-profile/manage-profile.component'; |
||||
|
import { RegisterComponent } from './components/register/register.component'; |
||||
|
import { ResetPasswordComponent } from './components/reset-password/reset-password.component'; |
||||
|
import { eAccountComponents } from './enums/components'; |
||||
|
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard'; |
||||
|
|
||||
|
const routes: Routes = [ |
||||
|
{ path: '', pathMatch: 'full', redirectTo: 'login' }, |
||||
|
{ |
||||
|
path: '', |
||||
|
component: DynamicLayoutComponent, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: '', |
||||
|
component: AuthWrapperComponent, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: 'login', |
||||
|
component: ReplaceableRouteContainerComponent, |
||||
|
canActivate: [AuthenticationFlowGuard], |
||||
|
data: { |
||||
|
replaceableComponent: { |
||||
|
key: eAccountComponents.Login, |
||||
|
defaultComponent: LoginComponent, |
||||
|
} as ReplaceableComponents.RouteData<LoginComponent>, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
path: 'register', |
||||
|
component: ReplaceableRouteContainerComponent, |
||||
|
canActivate: [AuthenticationFlowGuard], |
||||
|
data: { |
||||
|
replaceableComponent: { |
||||
|
key: eAccountComponents.Register, |
||||
|
defaultComponent: RegisterComponent, |
||||
|
} as ReplaceableComponents.RouteData<RegisterComponent>, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
path: 'forgot-password', |
||||
|
component: ReplaceableRouteContainerComponent, |
||||
|
canActivate: [AuthenticationFlowGuard], |
||||
|
data: { |
||||
|
replaceableComponent: { |
||||
|
key: eAccountComponents.ForgotPassword, |
||||
|
defaultComponent: ForgotPasswordComponent, |
||||
|
} as ReplaceableComponents.RouteData<ForgotPasswordComponent>, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
path: 'reset-password', |
||||
|
component: ReplaceableRouteContainerComponent, |
||||
|
canActivate: [AuthenticationFlowGuard], |
||||
|
data: { |
||||
|
replaceableComponent: { |
||||
|
key: eAccountComponents.ResetPassword, |
||||
|
defaultComponent: ResetPasswordComponent, |
||||
|
} as ReplaceableComponents.RouteData<ResetPasswordComponent>, |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
path: 'manage-profile', |
||||
|
component: ReplaceableRouteContainerComponent, |
||||
|
canActivate: [AuthGuard], |
||||
|
data: { |
||||
|
replaceableComponent: { |
||||
|
key: eAccountComponents.ManageProfile, |
||||
|
defaultComponent: ManageProfileComponent, |
||||
|
} as ReplaceableComponents.RouteData<ManageProfileComponent>, |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
@NgModule({ |
||||
|
imports: [RouterModule.forChild(routes)], |
||||
|
exports: [RouterModule], |
||||
|
}) |
||||
|
export class AccountRoutingModule {} |
||||
@ -0,0 +1,63 @@ |
|||||
|
import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; |
||||
|
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
||||
|
import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; |
||||
|
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; |
||||
|
import { NgxValidateCoreModule } from '@ngx-validate/core'; |
||||
|
import { AccountRoutingModule } from './account-routing.module'; |
||||
|
import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.component'; |
||||
|
import { ChangePasswordComponent } from './components/change-password/change-password.component'; |
||||
|
import { LoginComponent } from './components/login/login.component'; |
||||
|
import { ManageProfileComponent } from './components/manage-profile/manage-profile.component'; |
||||
|
import { PersonalSettingsComponent } from './components/personal-settings/personal-settings.component'; |
||||
|
import { RegisterComponent } from './components/register/register.component'; |
||||
|
import { TenantBoxComponent } from './components/tenant-box/tenant-box.component'; |
||||
|
import { AccountConfigOptions } from './models/config-options'; |
||||
|
import { ACCOUNT_CONFIG_OPTIONS } from './tokens/config-options.token'; |
||||
|
import { accountConfigOptionsFactory } from './utils/factory-utils'; |
||||
|
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard'; |
||||
|
import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component'; |
||||
|
import { ResetPasswordComponent } from './components/reset-password/reset-password.component'; |
||||
|
|
||||
|
const declarations = [ |
||||
|
AuthWrapperComponent, |
||||
|
LoginComponent, |
||||
|
RegisterComponent, |
||||
|
TenantBoxComponent, |
||||
|
ChangePasswordComponent, |
||||
|
ManageProfileComponent, |
||||
|
PersonalSettingsComponent, |
||||
|
ForgotPasswordComponent, |
||||
|
ResetPasswordComponent, |
||||
|
]; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [...declarations], |
||||
|
imports: [ |
||||
|
CoreModule, |
||||
|
AccountRoutingModule, |
||||
|
ThemeSharedModule, |
||||
|
NgbDropdownModule, |
||||
|
NgxValidateCoreModule, |
||||
|
], |
||||
|
exports: [...declarations], |
||||
|
}) |
||||
|
export class AccountModule { |
||||
|
static forChild(options = {} as AccountConfigOptions): ModuleWithProviders<AccountModule> { |
||||
|
return { |
||||
|
ngModule: AccountModule, |
||||
|
providers: [ |
||||
|
AuthenticationFlowGuard, |
||||
|
{ provide: ACCOUNT_CONFIG_OPTIONS, useValue: options }, |
||||
|
{ |
||||
|
provide: 'ACCOUNT_OPTIONS', |
||||
|
useFactory: accountConfigOptionsFactory, |
||||
|
deps: [ACCOUNT_CONFIG_OPTIONS], |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
static forLazy(options = {} as AccountConfigOptions): NgModuleFactory<AccountModule> { |
||||
|
return new LazyModuleFactory(AccountModule.forChild(options)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
<div class="row"> |
||||
|
<div class="mx-auto col col-md-5"> |
||||
|
<ng-container *ngIf="(isMultiTenancyEnabled$ | async) && multiTenancy.isTenantBoxVisible"> |
||||
|
<abp-tenant-box *abpReplaceableTemplate="{ componentKey: tenantBoxKey }"></abp-tenant-box> |
||||
|
</ng-container> |
||||
|
|
||||
|
<div class="abp-account-container"> |
||||
|
<div |
||||
|
*ngIf="enableLocalLogin$ | async; else disableLocalLoginTemplate" |
||||
|
class="card mt-3 shadow-sm rounded" |
||||
|
> |
||||
|
<div class="card-body p-5"> |
||||
|
<router-outlet></router-outlet> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<ng-template #disableLocalLoginTemplate> |
||||
|
<div class="alert alert-warning"> |
||||
|
<strong>{{ 'AbpAccount::InvalidLoginRequest' | abpLocalization }}</strong> |
||||
|
{{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | abpLocalization }} |
||||
|
</div> |
||||
|
</ng-template> |
||||
@ -0,0 +1,28 @@ |
|||||
|
import { ConfigStateService, MultiTenancyService, SubscriptionService } from '@abp/ng.core'; |
||||
|
import { Component } from '@angular/core'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { map } from 'rxjs/operators'; |
||||
|
import { eAccountComponents } from '../../enums/components'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-auth-wrapper', |
||||
|
templateUrl: './auth-wrapper.component.html', |
||||
|
exportAs: 'abpAuthWrapper', |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
export class AuthWrapperComponent { |
||||
|
isMultiTenancyEnabled$ = this.configState.getDeep$('multiTenancy.isEnabled'); |
||||
|
|
||||
|
get enableLocalLogin$(): Observable<boolean> { |
||||
|
return this.configState |
||||
|
.getSetting$('Abp.Account.EnableLocalLogin') |
||||
|
.pipe(map(value => value?.toLowerCase() !== 'false')); |
||||
|
} |
||||
|
|
||||
|
tenantBoxKey = eAccountComponents.TenantBox; |
||||
|
|
||||
|
constructor( |
||||
|
public readonly multiTenancy: MultiTenancyService, |
||||
|
private configState: ConfigStateService, |
||||
|
) {} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
<form [formGroup]="form" (ngSubmit)="onSubmit()" [mapErrorsFn]="mapErrorsFn" validateOnSubmit> |
||||
|
<div *ngIf="!hideCurrentPassword" class="form-group"> |
||||
|
<label for="current-password">{{ |
||||
|
'AbpIdentity::DisplayName:CurrentPassword' | abpLocalization |
||||
|
}}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
type="password" |
||||
|
id="current-password" |
||||
|
class="form-control" |
||||
|
formControlName="password" |
||||
|
autofocus |
||||
|
autocomplete="current-password" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="new-password">{{ 'AbpIdentity::DisplayName:NewPassword' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
type="password" |
||||
|
id="new-password" |
||||
|
class="form-control" |
||||
|
formControlName="newPassword" |
||||
|
autocomplete="new-password" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="confirm-new-password">{{ |
||||
|
'AbpIdentity::DisplayName:NewPasswordConfirm' | abpLocalization |
||||
|
}}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
type="password" |
||||
|
id="confirm-new-password" |
||||
|
class="form-control" |
||||
|
formControlName="repeatNewPassword" |
||||
|
autocomplete="new-password" |
||||
|
/> |
||||
|
</div> |
||||
|
<abp-button |
||||
|
iconClass="fa fa-check" |
||||
|
buttonClass="btn btn-primary color-white" |
||||
|
buttonType="submit" |
||||
|
[loading]="inProgress" |
||||
|
[disabled]="form?.invalid" |
||||
|
>{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button |
||||
|
> |
||||
|
</form> |
||||
@ -0,0 +1,99 @@ |
|||||
|
import { Profile, ProfileService } from '@abp/ng.core'; |
||||
|
import { getPasswordValidators, ToasterService } from '@abp/ng.theme.shared'; |
||||
|
import { Component, Injector, Input, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { comparePasswords, Validation } from '@ngx-validate/core'; |
||||
|
import { finalize } from 'rxjs/operators'; |
||||
|
import snq from 'snq'; |
||||
|
import { Account } from '../../models/account'; |
||||
|
import { ManageProfileStateService } from '../../services/manage-profile.state.service'; |
||||
|
|
||||
|
const { required } = Validators; |
||||
|
|
||||
|
const PASSWORD_FIELDS = ['newPassword', 'repeatNewPassword']; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-change-password-form', |
||||
|
templateUrl: './change-password.component.html', |
||||
|
exportAs: 'abpChangePasswordForm', |
||||
|
}) |
||||
|
export class ChangePasswordComponent |
||||
|
implements OnInit, Account.ChangePasswordComponentInputs, Account.ChangePasswordComponentOutputs { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress: boolean; |
||||
|
|
||||
|
hideCurrentPassword: boolean; |
||||
|
|
||||
|
mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => { |
||||
|
if (PASSWORD_FIELDS.indexOf(String(control.name)) < 0) return errors; |
||||
|
|
||||
|
return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch')); |
||||
|
}; |
||||
|
|
||||
|
constructor( |
||||
|
private fb: FormBuilder, |
||||
|
private injector: Injector, |
||||
|
private toasterService: ToasterService, |
||||
|
private profileService: ProfileService, |
||||
|
private manageProfileState: ManageProfileStateService, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.hideCurrentPassword = !this.manageProfileState.getProfile()?.hasPassword; |
||||
|
|
||||
|
const passwordValidations = getPasswordValidators(this.injector); |
||||
|
|
||||
|
this.form = this.fb.group( |
||||
|
{ |
||||
|
password: ['', required], |
||||
|
newPassword: [ |
||||
|
'', |
||||
|
{ |
||||
|
validators: [required, ...passwordValidations], |
||||
|
}, |
||||
|
], |
||||
|
repeatNewPassword: [ |
||||
|
'', |
||||
|
{ |
||||
|
validators: [required, ...passwordValidations], |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
validators: [comparePasswords(PASSWORD_FIELDS)], |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
if (this.hideCurrentPassword) this.form.removeControl('password'); |
||||
|
} |
||||
|
|
||||
|
onSubmit() { |
||||
|
if (this.form.invalid) return; |
||||
|
this.inProgress = true; |
||||
|
this.profileService |
||||
|
.changePassword({ |
||||
|
...(!this.hideCurrentPassword && { currentPassword: this.form.get('password').value }), |
||||
|
newPassword: this.form.get('newPassword').value, |
||||
|
}) |
||||
|
.pipe(finalize(() => (this.inProgress = false))) |
||||
|
.subscribe({ |
||||
|
next: () => { |
||||
|
this.form.reset(); |
||||
|
this.toasterService.success('AbpAccount::PasswordChangedMessage', '', { |
||||
|
life: 5000, |
||||
|
}); |
||||
|
|
||||
|
if (this.hideCurrentPassword) { |
||||
|
this.hideCurrentPassword = false; |
||||
|
this.form.addControl('password', new FormControl('', [required])); |
||||
|
} |
||||
|
}, |
||||
|
error: err => { |
||||
|
this.toasterService.error( |
||||
|
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), |
||||
|
); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
<h4>{{ 'AbpAccount::ForgotPassword' | abpLocalization }}</h4> |
||||
|
|
||||
|
<form |
||||
|
*ngIf="!isEmailSent; else emailSentTemplate" |
||||
|
[formGroup]="form" |
||||
|
(ngSubmit)="onSubmit()" |
||||
|
validateOnSubmit |
||||
|
> |
||||
|
<p>{{ 'AbpAccount::SendPasswordResetLink_Information' | abpLocalization }}</p> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-email-address">{{ 'AbpAccount::EmailAddress' | abpLocalization }}</label |
||||
|
><span> * </span> |
||||
|
<input type="email" id="input-email-address" class="form-control" formControlName="email" /> |
||||
|
</div> |
||||
|
<abp-button |
||||
|
class="d-block" |
||||
|
buttonClass="mt-2 mb-3 btn btn-primary btn-block" |
||||
|
[loading]="inProgress" |
||||
|
buttonType="submit" |
||||
|
[disabled]="form?.invalid" |
||||
|
> |
||||
|
{{ 'AbpAccount::Submit' | abpLocalization }} |
||||
|
</abp-button> |
||||
|
<a routerLink="/account/login" |
||||
|
><i class="fa fa-long-arrow-left mr-1"></i>{{ 'AbpAccount::Login' | abpLocalization }}</a |
||||
|
> |
||||
|
</form> |
||||
|
|
||||
|
<ng-template #emailSentTemplate> |
||||
|
<p> |
||||
|
{{ 'AbpAccount::PasswordResetMailSentMessage' | abpLocalization }} |
||||
|
</p> |
||||
|
|
||||
|
<a routerLink="/account/login"> |
||||
|
<button class="d-block mt-2 mb-3 btn btn-primary btn-block"> |
||||
|
<i class="fa fa-long-arrow-left mr-1"></i> |
||||
|
{{ 'AbpAccount::BackToLogin' | abpLocalization }} |
||||
|
</button> |
||||
|
</a> |
||||
|
</ng-template> |
||||
@ -0,0 +1,35 @@ |
|||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { finalize } from 'rxjs/operators'; |
||||
|
import { AccountService } from '../../proxy/account/account.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-forgot-password', |
||||
|
templateUrl: 'forgot-password.component.html', |
||||
|
}) |
||||
|
export class ForgotPasswordComponent { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress: boolean; |
||||
|
|
||||
|
isEmailSent = false; |
||||
|
|
||||
|
constructor(private fb: FormBuilder, private accountService: AccountService) { |
||||
|
this.form = this.fb.group({ |
||||
|
email: ['', [Validators.required, Validators.email]], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onSubmit() { |
||||
|
if (this.form.invalid) return; |
||||
|
|
||||
|
this.inProgress = true; |
||||
|
|
||||
|
this.accountService |
||||
|
.sendPasswordResetCode({ email: this.form.get('email').value, appName: 'Angular' }) |
||||
|
.pipe(finalize(() => (this.inProgress = false))) |
||||
|
.subscribe(() => { |
||||
|
this.isEmailSent = true; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
export * from './change-password/change-password.component'; |
||||
|
export * from './forgot-password/forgot-password.component'; |
||||
|
export * from './login/login.component'; |
||||
|
export * from './manage-profile/manage-profile.component'; |
||||
|
export * from './register/register.component'; |
||||
|
export * from './personal-settings/personal-settings.component'; |
||||
|
export * from './reset-password/reset-password.component'; |
||||
|
export * from './tenant-box/tenant-box.component'; |
||||
@ -0,0 +1,60 @@ |
|||||
|
<h4>{{ 'AbpAccount::Login' | abpLocalization }}</h4> |
||||
|
<strong *ngIf="isSelfRegistrationEnabled"> |
||||
|
{{ 'AbpAccount::AreYouANewUser' | abpLocalization }} |
||||
|
<a class="text-decoration-none" routerLink="/account/register" queryParamsHandling="preserve">{{ |
||||
|
'AbpAccount::Register' | abpLocalization |
||||
|
}}</a> |
||||
|
</strong> |
||||
|
<form [formGroup]="form" (ngSubmit)="onSubmit()" validateOnSubmit class="mt-4"> |
||||
|
<div class="form-group"> |
||||
|
<label for="login-input-user-name-or-email-address">{{ |
||||
|
'AbpAccount::UserNameOrEmailAddress' | abpLocalization |
||||
|
}}</label> |
||||
|
<input |
||||
|
class="form-control" |
||||
|
type="text" |
||||
|
id="login-input-user-name-or-email-address" |
||||
|
formControlName="username" |
||||
|
autocomplete="username" |
||||
|
autofocus |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="login-input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label> |
||||
|
<input |
||||
|
class="form-control" |
||||
|
type="password" |
||||
|
id="login-input-password" |
||||
|
formControlName="password" |
||||
|
autocomplete="current-password" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<div class="col"> |
||||
|
<label class="custom-checkbox custom-control mb-2" for="login-input-remember-me"> |
||||
|
<input |
||||
|
class="form-check-input" |
||||
|
type="checkbox" |
||||
|
id="login-input-remember-me" |
||||
|
formControlName="rememberMe" |
||||
|
/> |
||||
|
{{ 'AbpAccount::RememberMe' | abpLocalization }} |
||||
|
</label> |
||||
|
</div> |
||||
|
<div class="text-right col"> |
||||
|
<a routerLink="/account/forgot-password">{{ |
||||
|
'AbpAccount::ForgotPassword' | abpLocalization |
||||
|
}}</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<abp-button |
||||
|
[loading]="inProgress" |
||||
|
buttonType="submit" |
||||
|
name="Action" |
||||
|
buttonClass="btn-block btn-lg mt-3 btn btn-primary" |
||||
|
> |
||||
|
{{ 'AbpAccount::Login' | abpLocalization }} |
||||
|
</abp-button> |
||||
|
</form> |
||||
@ -0,0 +1,80 @@ |
|||||
|
import { ConfigStateService, AuthService } from '@abp/ng.core'; |
||||
|
import { ToasterService } from '@abp/ng.theme.shared'; |
||||
|
import { Component, Injector, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { Store } from '@ngxs/store'; |
||||
|
import { throwError } from 'rxjs'; |
||||
|
import { catchError, finalize } from 'rxjs/operators'; |
||||
|
import snq from 'snq'; |
||||
|
import { eAccountComponents } from '../../enums/components'; |
||||
|
import { getRedirectUrl } from '../../utils/auth-utils'; |
||||
|
|
||||
|
const { maxLength, required } = Validators; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-login', |
||||
|
templateUrl: './login.component.html', |
||||
|
}) |
||||
|
export class LoginComponent implements OnInit { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress: boolean; |
||||
|
|
||||
|
isSelfRegistrationEnabled = true; |
||||
|
|
||||
|
authWrapperKey = eAccountComponents.AuthWrapper; |
||||
|
|
||||
|
constructor( |
||||
|
protected injector: Injector, |
||||
|
protected fb: FormBuilder, |
||||
|
protected toasterService: ToasterService, |
||||
|
protected authService: AuthService, |
||||
|
protected configState: ConfigStateService, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.init(); |
||||
|
this.buildForm(); |
||||
|
} |
||||
|
|
||||
|
protected init() { |
||||
|
this.isSelfRegistrationEnabled = |
||||
|
( |
||||
|
(this.configState.getSetting('Abp.Account.IsSelfRegistrationEnabled') as string) || '' |
||||
|
).toLowerCase() !== 'false'; |
||||
|
} |
||||
|
|
||||
|
protected buildForm() { |
||||
|
this.form = this.fb.group({ |
||||
|
username: ['', [required, maxLength(255)]], |
||||
|
password: ['', [required, maxLength(128)]], |
||||
|
rememberMe: [false], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onSubmit() { |
||||
|
if (this.form.invalid) return; |
||||
|
|
||||
|
this.inProgress = true; |
||||
|
|
||||
|
const { username, password, rememberMe } = this.form.value; |
||||
|
|
||||
|
const redirectUrl = getRedirectUrl(this.injector); |
||||
|
|
||||
|
this.authService |
||||
|
.login({ username, password, rememberMe, redirectUrl }) |
||||
|
.pipe( |
||||
|
catchError(err => { |
||||
|
this.toasterService.error( |
||||
|
snq(() => err.error.error_description) || |
||||
|
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), |
||||
|
'Error', |
||||
|
{ life: 7000 }, |
||||
|
); |
||||
|
return throwError(err); |
||||
|
}), |
||||
|
finalize(() => (this.inProgress = false)), |
||||
|
) |
||||
|
.subscribe(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
<div id="AbpContentToolbar"></div> |
||||
|
|
||||
|
<div class="card border-0 shadow-sm min-h-400" [abpLoading]="!(profile$ | async)"> |
||||
|
<div class="card-body"> |
||||
|
<div class="row"> |
||||
|
<div class="col-12 col-md-3"> |
||||
|
<ul class="nav flex-column nav-pills" id="nav-tab" role="tablist"> |
||||
|
<li |
||||
|
*ngIf="!hideChangePasswordTab && (profile$ | async)" |
||||
|
class="nav-item" |
||||
|
(click)="selectedTab = 0" |
||||
|
> |
||||
|
<a |
||||
|
class="nav-link" |
||||
|
[ngClass]="{ active: selectedTab === 0 }" |
||||
|
role="tab" |
||||
|
href="javascript:void(0)" |
||||
|
>{{ 'AbpUi::ChangePassword' | abpLocalization }}</a |
||||
|
> |
||||
|
</li> |
||||
|
<li class="nav-item mb-2" (click)="selectedTab = 1"> |
||||
|
<a |
||||
|
class="nav-link" |
||||
|
[ngClass]="{ active: selectedTab === 1 }" |
||||
|
role="tab" |
||||
|
href="javascript:void(0)" |
||||
|
>{{ 'AbpAccount::PersonalSettings' | abpLocalization }}</a |
||||
|
> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div *ngIf="profile$ | async" class="col-12 col-md-9"> |
||||
|
<div class="tab-content" *ngIf="selectedTab === 0" [@fadeIn]> |
||||
|
<div class="tab-pane active" role="tabpanel"> |
||||
|
<h4> |
||||
|
{{ 'AbpIdentity::ChangePassword' | abpLocalization }} |
||||
|
<hr /> |
||||
|
</h4> |
||||
|
<abp-change-password-form |
||||
|
*abpReplaceableTemplate="{ |
||||
|
componentKey: changePasswordKey |
||||
|
}" |
||||
|
></abp-change-password-form> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tab-content" *ngIf="selectedTab === 1" [@fadeIn]> |
||||
|
<div class="tab-pane active" role="tabpanel"> |
||||
|
<h4> |
||||
|
{{ 'AbpIdentity::PersonalSettings' | abpLocalization }} |
||||
|
<hr /> |
||||
|
</h4> |
||||
|
<abp-personal-settings-form |
||||
|
*abpReplaceableTemplate="{ |
||||
|
componentKey: personalSettingsKey |
||||
|
}" |
||||
|
></abp-personal-settings-form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,45 @@ |
|||||
|
import { Profile, ProfileService } from '@abp/ng.core'; |
||||
|
import { fadeIn } from '@abp/ng.theme.shared'; |
||||
|
import { transition, trigger, useAnimation } from '@angular/animations'; |
||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
import { eAccountComponents } from '../../enums/components'; |
||||
|
import { ManageProfileStateService } from '../../services/manage-profile.state.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-manage-profile', |
||||
|
templateUrl: './manage-profile.component.html', |
||||
|
animations: [trigger('fadeIn', [transition(':enter', useAnimation(fadeIn))])], |
||||
|
styles: [ |
||||
|
` |
||||
|
.min-h-400 { |
||||
|
min-height: 400px; |
||||
|
} |
||||
|
`,
|
||||
|
], |
||||
|
}) |
||||
|
export class ManageProfileComponent implements OnInit { |
||||
|
selectedTab = 0; |
||||
|
|
||||
|
changePasswordKey = eAccountComponents.ChangePassword; |
||||
|
|
||||
|
personalSettingsKey = eAccountComponents.PersonalSettings; |
||||
|
|
||||
|
profile$ = this.manageProfileState.getProfile$(); |
||||
|
|
||||
|
hideChangePasswordTab: boolean; |
||||
|
|
||||
|
constructor( |
||||
|
protected profileService: ProfileService, |
||||
|
protected manageProfileState: ManageProfileStateService, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.profileService.get().subscribe(profile => { |
||||
|
this.manageProfileState.setProfile(profile); |
||||
|
if (profile.isExternal) { |
||||
|
this.hideChangePasswordTab = true; |
||||
|
this.selectedTab = 1; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
<form validateOnSubmit *ngIf="form" [formGroup]="form" (ngSubmit)="submit()"> |
||||
|
<div class="form-group"> |
||||
|
<label for="username">{{ 'AbpIdentity::DisplayName:UserName' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
type="text" |
||||
|
id="username" |
||||
|
class="form-control" |
||||
|
formControlName="userName" |
||||
|
autofocus |
||||
|
(keydown.space)="$event.preventDefault()" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="row"> |
||||
|
<div class="col col-md-6"> |
||||
|
<div class="form-group"> |
||||
|
<label for="name">{{ 'AbpIdentity::DisplayName:Name' | abpLocalization }}</label |
||||
|
><input type="text" id="name" class="form-control" formControlName="name" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col col-md-6"> |
||||
|
<div class="form-group"> |
||||
|
<label for="surname">{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}</label |
||||
|
><input type="text" id="surname" class="form-control" formControlName="surname" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="email-address">{{ 'AbpIdentity::DisplayName:Email' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input type="text" id="email-address" class="form-control" formControlName="email" /> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="phone-number">{{ 'AbpIdentity::DisplayName:PhoneNumber' | abpLocalization }}</label |
||||
|
><input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" /> |
||||
|
</div> |
||||
|
<abp-button |
||||
|
buttonType="submit" |
||||
|
iconClass="fa fa-check" |
||||
|
buttonClass="btn btn-primary color-white" |
||||
|
[loading]="inProgress" |
||||
|
[disabled]="form?.invalid" |
||||
|
> |
||||
|
{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button |
||||
|
> |
||||
|
</form> |
||||
@ -0,0 +1,58 @@ |
|||||
|
import { ProfileService } from '@abp/ng.core'; |
||||
|
import { ToasterService } from '@abp/ng.theme.shared'; |
||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { finalize } from 'rxjs/operators'; |
||||
|
import { Account } from '../../models/account'; |
||||
|
import { ManageProfileStateService } from '../../services/manage-profile.state.service'; |
||||
|
|
||||
|
const { maxLength, required, email } = Validators; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-personal-settings-form', |
||||
|
templateUrl: './personal-settings.component.html', |
||||
|
exportAs: 'abpPersonalSettingsForm', |
||||
|
}) |
||||
|
export class PersonalSettingsComponent |
||||
|
implements |
||||
|
OnInit, |
||||
|
Account.PersonalSettingsComponentInputs, |
||||
|
Account.PersonalSettingsComponentOutputs { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress: boolean; |
||||
|
|
||||
|
constructor( |
||||
|
private fb: FormBuilder, |
||||
|
private toasterService: ToasterService, |
||||
|
private profileService: ProfileService, |
||||
|
private manageProfileState: ManageProfileStateService, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.buildForm(); |
||||
|
} |
||||
|
|
||||
|
buildForm() { |
||||
|
const profile = this.manageProfileState.getProfile(); |
||||
|
this.form = this.fb.group({ |
||||
|
userName: [profile.userName, [required, maxLength(256)]], |
||||
|
email: [profile.email, [required, email, maxLength(256)]], |
||||
|
name: [profile.name || '', [maxLength(64)]], |
||||
|
surname: [profile.surname || '', [maxLength(64)]], |
||||
|
phoneNumber: [profile.phoneNumber || '', [maxLength(16)]], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
submit() { |
||||
|
if (this.form.invalid) return; |
||||
|
this.inProgress = true; |
||||
|
this.profileService |
||||
|
.update(this.form.value) |
||||
|
.pipe(finalize(() => (this.inProgress = false))) |
||||
|
.subscribe(profile => { |
||||
|
this.manageProfileState.setProfile(profile); |
||||
|
this.toasterService.success('AbpAccount::PersonalSettingsSaved', 'Success', { life: 5000 }); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
<h4>{{ 'AbpAccount::Register' | abpLocalization }}</h4> |
||||
|
<strong> |
||||
|
{{ 'AbpAccount::AlreadyRegistered' | abpLocalization }} |
||||
|
<a class="text-decoration-none" routerLink="/account/login">{{ |
||||
|
'AbpAccount::Login' | abpLocalization |
||||
|
}}</a> |
||||
|
</strong> |
||||
|
<form |
||||
|
*ngIf="isSelfRegistrationEnabled" |
||||
|
[formGroup]="form" |
||||
|
(ngSubmit)="onSubmit()" |
||||
|
validateOnSubmit |
||||
|
class="mt-4" |
||||
|
> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-user-name">{{ 'AbpAccount::UserName' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
autofocus |
||||
|
type="text" |
||||
|
id="input-user-name" |
||||
|
class="form-control" |
||||
|
formControlName="username" |
||||
|
autocomplete="username" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-email-address">{{ 'AbpAccount::EmailAddress' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input type="email" id="input-email-address" class="form-control" formControlName="email" /> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label |
||||
|
><span> * </span |
||||
|
><input |
||||
|
type="password" |
||||
|
id="input-password" |
||||
|
class="form-control" |
||||
|
formControlName="password" |
||||
|
autocomplete="current-password" |
||||
|
/> |
||||
|
</div> |
||||
|
<abp-button |
||||
|
[loading]="inProgress" |
||||
|
buttonType="submit" |
||||
|
name="Action" |
||||
|
buttonClass="btn-block btn-lg mt-3 btn btn-primary" |
||||
|
> |
||||
|
{{ 'AbpAccount::Register' | abpLocalization }} |
||||
|
</abp-button> |
||||
|
</form> |
||||
@ -0,0 +1,102 @@ |
|||||
|
import { AuthService, ConfigStateService } from '@abp/ng.core'; |
||||
|
import { getPasswordValidators, ToasterService } from '@abp/ng.theme.shared'; |
||||
|
import { Component, Injector, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { throwError } from 'rxjs'; |
||||
|
import { catchError, finalize, switchMap } from 'rxjs/operators'; |
||||
|
import snq from 'snq'; |
||||
|
import { eAccountComponents } from '../../enums/components'; |
||||
|
import { AccountService } from '../../proxy/account/account.service'; |
||||
|
import { RegisterDto } from '../../proxy/account/models'; |
||||
|
import { getRedirectUrl } from '../../utils/auth-utils'; |
||||
|
const { maxLength, required, email } = Validators; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-register', |
||||
|
templateUrl: './register.component.html', |
||||
|
}) |
||||
|
export class RegisterComponent implements OnInit { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress: boolean; |
||||
|
|
||||
|
isSelfRegistrationEnabled = true; |
||||
|
|
||||
|
authWrapperKey = eAccountComponents.AuthWrapper; |
||||
|
|
||||
|
constructor( |
||||
|
protected fb: FormBuilder, |
||||
|
protected accountService: AccountService, |
||||
|
protected configState: ConfigStateService, |
||||
|
protected toasterService: ToasterService, |
||||
|
protected authService: AuthService, |
||||
|
protected injector: Injector, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.init(); |
||||
|
this.buildForm(); |
||||
|
} |
||||
|
|
||||
|
protected init() { |
||||
|
this.isSelfRegistrationEnabled = |
||||
|
(this.configState.getSetting('Abp.Account.IsSelfRegistrationEnabled') || '').toLowerCase() !== |
||||
|
'false'; |
||||
|
|
||||
|
if (!this.isSelfRegistrationEnabled) { |
||||
|
this.toasterService.warn( |
||||
|
{ |
||||
|
key: 'AbpAccount::SelfRegistrationDisabledMessage', |
||||
|
defaultValue: 'Self registration is disabled.', |
||||
|
}, |
||||
|
null, |
||||
|
{ life: 10000 }, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected buildForm() { |
||||
|
this.form = this.fb.group({ |
||||
|
username: ['', [required, maxLength(255)]], |
||||
|
password: ['', [required, ...getPasswordValidators(this.injector)]], |
||||
|
email: ['', [required, email]], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onSubmit() { |
||||
|
if (this.form.invalid) return; |
||||
|
|
||||
|
this.inProgress = true; |
||||
|
|
||||
|
const newUser = { |
||||
|
userName: this.form.get('username').value, |
||||
|
password: this.form.get('password').value, |
||||
|
emailAddress: this.form.get('email').value, |
||||
|
appName: 'Angular', |
||||
|
} as RegisterDto; |
||||
|
|
||||
|
this.accountService |
||||
|
.register(newUser) |
||||
|
.pipe( |
||||
|
switchMap(() => |
||||
|
this.authService.login({ |
||||
|
username: newUser.userName, |
||||
|
password: newUser.password, |
||||
|
redirectUrl: getRedirectUrl(this.injector), |
||||
|
}), |
||||
|
), |
||||
|
catchError(err => { |
||||
|
this.toasterService.error( |
||||
|
snq(() => err.error.error_description) || |
||||
|
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), |
||||
|
'Error', |
||||
|
{ life: 7000 }, |
||||
|
); |
||||
|
return throwError(err); |
||||
|
}), |
||||
|
finalize(() => (this.inProgress = false)), |
||||
|
) |
||||
|
.subscribe(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
<h4>{{ 'AbpAccount::ResetPassword' | abpLocalization }}</h4> |
||||
|
|
||||
|
<form |
||||
|
*ngIf="!isPasswordReset; else passwordResetTemplate" |
||||
|
[formGroup]="form" |
||||
|
[mapErrorsFn]="mapErrorsFn" |
||||
|
(ngSubmit)="onSubmit()" |
||||
|
validateOnSubmit |
||||
|
> |
||||
|
<p>{{ 'AbpAccount::ResetPassword_Information' | abpLocalization }}</p> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label |
||||
|
><span> * </span> |
||||
|
<input type="password" id="input-password" class="form-control" formControlName="password" /> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="input-confirm-password">{{ 'AbpAccount::ConfirmPassword' | abpLocalization }}</label |
||||
|
><span> * </span> |
||||
|
<input |
||||
|
type="password" |
||||
|
id="input-confirm-password" |
||||
|
class="form-control" |
||||
|
formControlName="confirmPassword" |
||||
|
/> |
||||
|
</div> |
||||
|
<button class="mr-2 btn btn-secondary" type="button" routerLink="/account/login"> |
||||
|
{{ 'AbpAccount::Cancel' | abpLocalization }} |
||||
|
</button> |
||||
|
<abp-button |
||||
|
buttonType="submit" |
||||
|
buttonClass="mr-2 btn btn-primary" |
||||
|
[loading]="inProgress" |
||||
|
(click)="onSubmit()" |
||||
|
> |
||||
|
{{ 'AbpAccount::Submit' | abpLocalization }} |
||||
|
</abp-button> |
||||
|
</form> |
||||
|
|
||||
|
<ng-template #passwordResetTemplate> |
||||
|
<p> |
||||
|
{{ 'AbpAccount::YourPasswordIsSuccessfullyReset' | abpLocalization }} |
||||
|
</p> |
||||
|
|
||||
|
<a routerLink="/account/login"> |
||||
|
<button class="d-block mt-2 mb-3 btn btn-primary"> |
||||
|
{{ 'AbpAccount::BackToLogin' | abpLocalization }} |
||||
|
</button> |
||||
|
</a> |
||||
|
</ng-template> |
||||
@ -0,0 +1,70 @@ |
|||||
|
import { getPasswordValidators } from '@abp/ng.theme.shared'; |
||||
|
import { Component, Injector, OnInit } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { ActivatedRoute, Router } from '@angular/router'; |
||||
|
import { comparePasswords, Validation } from '@ngx-validate/core'; |
||||
|
import { finalize } from 'rxjs/operators'; |
||||
|
import { AccountService } from '../../proxy/account/account.service'; |
||||
|
|
||||
|
const PASSWORD_FIELDS = ['password', 'confirmPassword']; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-reset-password', |
||||
|
templateUrl: './reset-password.component.html', |
||||
|
}) |
||||
|
export class ResetPasswordComponent implements OnInit { |
||||
|
form: FormGroup; |
||||
|
|
||||
|
inProgress = false; |
||||
|
|
||||
|
isPasswordReset = false; |
||||
|
|
||||
|
mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => { |
||||
|
if (PASSWORD_FIELDS.indexOf(String(control.name)) < 0) return errors; |
||||
|
|
||||
|
return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch')); |
||||
|
}; |
||||
|
|
||||
|
constructor( |
||||
|
private fb: FormBuilder, |
||||
|
private accountService: AccountService, |
||||
|
private route: ActivatedRoute, |
||||
|
private router: Router, |
||||
|
private injector: Injector, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.route.queryParams.subscribe(({ userId, resetToken }) => { |
||||
|
if (!userId || !resetToken) this.router.navigateByUrl('/account/login'); |
||||
|
|
||||
|
this.form = this.fb.group( |
||||
|
{ |
||||
|
userId: [userId, [Validators.required]], |
||||
|
resetToken: [resetToken, [Validators.required]], |
||||
|
password: ['', [Validators.required, ...getPasswordValidators(this.injector)]], |
||||
|
confirmPassword: ['', [Validators.required, ...getPasswordValidators(this.injector)]], |
||||
|
}, |
||||
|
{ |
||||
|
validators: [comparePasswords(PASSWORD_FIELDS)], |
||||
|
}, |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onSubmit() { |
||||
|
if (this.form.invalid || this.inProgress) return; |
||||
|
|
||||
|
this.inProgress = true; |
||||
|
|
||||
|
this.accountService |
||||
|
.resetPassword({ |
||||
|
userId: this.form.get('userId').value, |
||||
|
resetToken: this.form.get('resetToken').value, |
||||
|
password: this.form.get('password').value, |
||||
|
}) |
||||
|
.pipe(finalize(() => (this.inProgress = false))) |
||||
|
.subscribe(() => { |
||||
|
this.isPasswordReset = true; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
<ng-container *ngIf="(currentTenant$ | async) || {} as currentTenant"> |
||||
|
<div class="card shadow-sm rounded mb-3"> |
||||
|
<div class="card-body px-5"> |
||||
|
<div class="row"> |
||||
|
<div class="col"> |
||||
|
<span style="font-size: 0.8em;" class="text-uppercase text-muted">{{ |
||||
|
'AbpUiMultiTenancy::Tenant' | abpLocalization |
||||
|
}}</span |
||||
|
><br /> |
||||
|
<h6 class="m-0 d-inline-block"> |
||||
|
<i>{{ currentTenant.name || ('AbpUiMultiTenancy::NotSelected' | abpLocalization) }}</i> |
||||
|
</h6> |
||||
|
</div> |
||||
|
<div class="col-auto"> |
||||
|
<a |
||||
|
id="AbpTenantSwitchLink" |
||||
|
href="javascript:void(0);" |
||||
|
class="btn btn-sm mt-3 btn-outline-primary" |
||||
|
(click)="onSwitch()" |
||||
|
>{{ 'AbpUiMultiTenancy::Switch' | abpLocalization }}</a |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<abp-modal size="md" [(visible)]="isModalVisible" [busy]="modalBusy"> |
||||
|
<ng-template #abpHeader> |
||||
|
<h5>Switch Tenant</h5> |
||||
|
</ng-template> |
||||
|
<ng-template #abpBody> |
||||
|
<form (ngSubmit)="save()"> |
||||
|
<div class="mt-2"> |
||||
|
<div class="form-group"> |
||||
|
<label for="name">{{ 'AbpUiMultiTenancy::Name' | abpLocalization }}</label> |
||||
|
<input |
||||
|
[(ngModel)]="name" |
||||
|
type="text" |
||||
|
id="name" |
||||
|
name="tenant" |
||||
|
class="form-control" |
||||
|
autofocus |
||||
|
/> |
||||
|
</div> |
||||
|
<p>{{ 'AbpUiMultiTenancy::SwitchTenantHint' | abpLocalization }}</p> |
||||
|
</div> |
||||
|
</form> |
||||
|
</ng-template> |
||||
|
<ng-template #abpFooter> |
||||
|
<button #abpClose type="button" class="btn btn-secondary"> |
||||
|
{{ 'AbpTenantManagement::Cancel' | abpLocalization }} |
||||
|
</button> |
||||
|
<abp-button |
||||
|
type="abp-button" |
||||
|
iconClass="fa fa-check" |
||||
|
(click)="save()" |
||||
|
[disabled]="currentTenant?.name === name" |
||||
|
> |
||||
|
<span>{{ 'AbpTenantManagement::Save' | abpLocalization }}</span> |
||||
|
</abp-button> |
||||
|
</ng-template> |
||||
|
</abp-modal> |
||||
|
</ng-container> |
||||
@ -0,0 +1,73 @@ |
|||||
|
import { |
||||
|
AbpApplicationConfigurationService, |
||||
|
AbpTenantService, |
||||
|
ConfigStateService, |
||||
|
CurrentTenantDto, |
||||
|
SessionStateService, |
||||
|
} from '@abp/ng.core'; |
||||
|
import { ToasterService } from '@abp/ng.theme.shared'; |
||||
|
import { Component } from '@angular/core'; |
||||
|
import { finalize } from 'rxjs/operators'; |
||||
|
import { Account } from '../../models/account'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-tenant-box', |
||||
|
templateUrl: './tenant-box.component.html', |
||||
|
}) |
||||
|
export class TenantBoxComponent |
||||
|
implements Account.TenantBoxComponentInputs, Account.TenantBoxComponentOutputs { |
||||
|
currentTenant$ = this.sessionState.getTenant$(); |
||||
|
|
||||
|
name: string; |
||||
|
|
||||
|
isModalVisible: boolean; |
||||
|
|
||||
|
modalBusy: boolean; |
||||
|
|
||||
|
constructor( |
||||
|
private toasterService: ToasterService, |
||||
|
private tenantService: AbpTenantService, |
||||
|
private sessionState: SessionStateService, |
||||
|
private configState: ConfigStateService, |
||||
|
private appConfigService: AbpApplicationConfigurationService, |
||||
|
) {} |
||||
|
|
||||
|
onSwitch() { |
||||
|
const tenant = this.sessionState.getTenant(); |
||||
|
this.name = tenant?.name; |
||||
|
this.isModalVisible = true; |
||||
|
} |
||||
|
|
||||
|
save() { |
||||
|
if (!this.name) { |
||||
|
this.setTenant(null); |
||||
|
this.isModalVisible = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.modalBusy = true; |
||||
|
this.tenantService |
||||
|
.findTenantByName(this.name, {}) |
||||
|
.pipe(finalize(() => (this.modalBusy = false))) |
||||
|
.subscribe(({ success, tenantId: id, ...tenant }) => { |
||||
|
if (!success) { |
||||
|
this.showError(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.setTenant({ ...tenant, id, isAvailable: true }); |
||||
|
this.isModalVisible = false; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private setTenant(tenant: CurrentTenantDto) { |
||||
|
this.sessionState.setTenant(tenant); |
||||
|
this.appConfigService.get().subscribe(res => this.configState.setState(res)); |
||||
|
} |
||||
|
|
||||
|
private showError() { |
||||
|
this.toasterService.error('AbpUiMultiTenancy::GivenTenantIsNotAvailable', 'AbpUi::Error', { |
||||
|
messageLocalizationParams: [this.name], |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
export const enum eAccountComponents { |
||||
|
Login = 'Account.LoginComponent', |
||||
|
Register = 'Account.RegisterComponent', |
||||
|
ForgotPassword = 'Account.ForgotPasswordComponent', |
||||
|
ResetPassword = 'Account.ResetPasswordComponent', |
||||
|
ManageProfile = 'Account.ManageProfileComponent', |
||||
|
TenantBox = 'Account.TenantBoxComponent', |
||||
|
AuthWrapper = 'Account.AuthWrapperComponent', |
||||
|
ChangePassword = 'Account.ChangePasswordComponent', |
||||
|
PersonalSettings = 'Account.PersonalSettingsComponent', |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './components'; |
||||
@ -0,0 +1,15 @@ |
|||||
|
import { AuthService } from '@abp/ng.core'; |
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { CanActivate } from '@angular/router'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class AuthenticationFlowGuard implements CanActivate { |
||||
|
constructor(private authService: AuthService) {} |
||||
|
|
||||
|
canActivate() { |
||||
|
if (this.authService.isInternalAuth) return true; |
||||
|
|
||||
|
this.authService.navigateToLogin(); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './authentication-flow.guard'; |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { TemplateRef } from '@angular/core'; |
||||
|
|
||||
|
export namespace Account { |
||||
|
//tslint:disable
|
||||
|
export interface TenantBoxComponentInputs {} |
||||
|
export interface TenantBoxComponentOutputs {} |
||||
|
export interface PersonalSettingsComponentInputs {} |
||||
|
export interface PersonalSettingsComponentOutputs {} |
||||
|
export interface ChangePasswordComponentInputs {} |
||||
|
export interface ChangePasswordComponentOutputs {} |
||||
|
// tslint:enable
|
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
export interface AccountConfigOptions { |
||||
|
redirectUrl?: string; |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './account'; |
||||
|
export * from './config-options'; |
||||
@ -0,0 +1,17 @@ |
|||||
|
# Proxy Generation Output |
||||
|
|
||||
|
This directory includes the output of the latest proxy generation. |
||||
|
The files and folders in it will be overwritten when proxy generation is run again. |
||||
|
Therefore, please do not place your own content in this folder. |
||||
|
|
||||
|
In addition, `generate-proxy.json` works like a lock file. |
||||
|
It includes information used by the proxy generator, so please do not delete or modify it. |
||||
|
|
||||
|
Finally, the name of the files and folders should not be changed for two reasons: |
||||
|
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content. |
||||
|
- ABP Suite generates files which include imports from this folder. |
||||
|
|
||||
|
> **Important Notice:** If you are building a module and are planning to publish to npm, |
||||
|
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case, |
||||
|
> please make sure you export files directly and not from barrel exports. In other words, |
||||
|
> do not include index.ts exports in your public-api.ts exports. |
||||
@ -0,0 +1,37 @@ |
|||||
|
import type { RegisterDto, ResetPasswordDto, SendPasswordResetCodeDto } from './models'; |
||||
|
import { RestService } from '@abp/ng.core'; |
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import type { IdentityUserDto } from '../identity/models'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root', |
||||
|
}) |
||||
|
export class AccountService { |
||||
|
apiName = 'AbpAccount'; |
||||
|
|
||||
|
register = (input: RegisterDto) => |
||||
|
this.restService.request<any, IdentityUserDto>({ |
||||
|
method: 'POST', |
||||
|
url: '/api/account/register', |
||||
|
body: input, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
resetPassword = (input: ResetPasswordDto) => |
||||
|
this.restService.request<any, void>({ |
||||
|
method: 'POST', |
||||
|
url: '/api/account/reset-password', |
||||
|
body: input, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
sendPasswordResetCode = (input: SendPasswordResetCodeDto) => |
||||
|
this.restService.request<any, void>({ |
||||
|
method: 'POST', |
||||
|
url: '/api/account/send-password-reset-code', |
||||
|
body: input, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
constructor(private restService: RestService) {} |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './account.service'; |
||||
|
export * from './models'; |
||||
@ -0,0 +1,20 @@ |
|||||
|
|
||||
|
export interface RegisterDto { |
||||
|
userName: string; |
||||
|
emailAddress: string; |
||||
|
password: string; |
||||
|
appName: string; |
||||
|
} |
||||
|
|
||||
|
export interface ResetPasswordDto { |
||||
|
userId?: string; |
||||
|
resetToken: string; |
||||
|
password: string; |
||||
|
} |
||||
|
|
||||
|
export interface SendPasswordResetCodeDto { |
||||
|
email: string; |
||||
|
appName: string; |
||||
|
returnUrl?: string; |
||||
|
returnUrlHash?: string; |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1 @@ |
|||||
|
export * from './models'; |
||||
@ -0,0 +1,15 @@ |
|||||
|
import type { ExtensibleFullAuditedEntityDto } from '@abp/ng.core'; |
||||
|
|
||||
|
export interface IdentityUserDto extends ExtensibleFullAuditedEntityDto<string> { |
||||
|
tenantId?: string; |
||||
|
userName?: string; |
||||
|
name?: string; |
||||
|
surname?: string; |
||||
|
email?: string; |
||||
|
emailConfirmed: boolean; |
||||
|
phoneNumber?: string; |
||||
|
phoneNumberConfirmed: boolean; |
||||
|
lockoutEnabled: boolean; |
||||
|
lockoutEnd?: string; |
||||
|
concurrencyStamp?: string; |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
import * as Account from './account'; |
||||
|
import * as Identity from './identity'; |
||||
|
export { Account, Identity }; |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './manage-profile.state.service'; |
||||
@ -0,0 +1,28 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { InternalStore, Profile } from '@abp/ng.core'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
export interface ManageProfileState { |
||||
|
profile: Profile.Response; |
||||
|
} |
||||
|
|
||||
|
@Injectable({ providedIn: 'root' }) |
||||
|
export class ManageProfileStateService { |
||||
|
private readonly store = new InternalStore({} as ManageProfileState); |
||||
|
|
||||
|
get createOnUpdateStream() { |
||||
|
return this.store.sliceUpdate; |
||||
|
} |
||||
|
|
||||
|
getProfile$(): Observable<Profile.Response> { |
||||
|
return this.store.sliceState(state => state.profile); |
||||
|
} |
||||
|
|
||||
|
getProfile(): Profile.Response { |
||||
|
return this.store.state.profile; |
||||
|
} |
||||
|
|
||||
|
setProfile(profile: Profile.Response) { |
||||
|
this.store.patch({ profile }); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
import { InjectionToken } from '@angular/core'; |
||||
|
import { AccountConfigOptions } from '../models/config-options'; |
||||
|
|
||||
|
export const ACCOUNT_CONFIG_OPTIONS = new InjectionToken<AccountConfigOptions>( |
||||
|
'ACCOUNT_CONFIG_OPTIONS', |
||||
|
); |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './config-options.token'; |
||||
@ -0,0 +1,9 @@ |
|||||
|
import { Injector } from '@angular/core'; |
||||
|
import { ActivatedRoute } from '@angular/router'; |
||||
|
import { ACCOUNT_CONFIG_OPTIONS } from '../tokens'; |
||||
|
|
||||
|
export function getRedirectUrl(injector: Injector) { |
||||
|
const route = injector.get(ActivatedRoute); |
||||
|
const options = injector.get(ACCOUNT_CONFIG_OPTIONS); |
||||
|
return route.snapshot.queryParams.returnUrl || options.redirectUrl || '/'; |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
import { AccountConfigOptions } from '../models/config-options'; |
||||
|
|
||||
|
export function accountConfigOptionsFactory(options: AccountConfigOptions) { |
||||
|
return { |
||||
|
redirectUrl: '/', |
||||
|
...options, |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './auth-utils'; |
||||
|
export * from './factory-utils'; |
||||
@ -0,0 +1,9 @@ |
|||||
|
export * from './lib/account.module'; |
||||
|
export * from './lib/components'; |
||||
|
export * from './lib/enums'; |
||||
|
export * from './lib/guards'; |
||||
|
export * from './lib/models'; |
||||
|
export * from './lib/services'; |
||||
|
export * from './lib/tokens'; |
||||
|
export * from './lib/proxy/account'; |
||||
|
export * from './lib/proxy/identity'; |
||||
@ -0,0 +1,19 @@ |
|||||
|
{ |
||||
|
"extends": "../../tsconfig.prod.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../../out-tsc/lib", |
||||
|
"target": "es2015", |
||||
|
"declaration": true, |
||||
|
"inlineSources": true, |
||||
|
"types": [], |
||||
|
"lib": ["dom", "es2018"] |
||||
|
}, |
||||
|
"angularCompilerOptions": { |
||||
|
"skipTemplateCodegen": true, |
||||
|
"strictMetadataEmit": true, |
||||
|
"fullTemplateTypeCheck": true, |
||||
|
"strictInjectionParameters": true, |
||||
|
"enableResourceInlining": true |
||||
|
}, |
||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"] |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"extends": "./tsconfig.lib.json", |
||||
|
"angularCompilerOptions": { |
||||
|
"enableIvy": false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"extends": "../../tsconfig.prod.json", |
||||
|
"compilerOptions": { |
||||
|
"emitDecoratorMetadata": true, |
||||
|
"esModuleInterop": true, |
||||
|
"outDir": "../../dist/out-tsc", |
||||
|
"module": "commonjs", |
||||
|
"types": ["jest", "node"] |
||||
|
}, |
||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"] |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
{ |
||||
|
"extends": "../../tslint.json", |
||||
|
"rules": { |
||||
|
"directive-selector": [ |
||||
|
true, |
||||
|
"attribute", |
||||
|
"abp", |
||||
|
"camelCase" |
||||
|
], |
||||
|
"component-selector": [ |
||||
|
true, |
||||
|
"element", |
||||
|
"abp", |
||||
|
"kebab-case" |
||||
|
] |
||||
|
} |
||||
|
} |
||||
@ -1,47 +1,61 @@ |
|||||
import { Injectable, Injector } from '@angular/core'; |
import { Injectable, Injector } from '@angular/core'; |
||||
import { Observable } from 'rxjs'; |
import { from, Observable } from 'rxjs'; |
||||
import { AuthFlowStrategy, AUTH_FLOW_STRATEGY } from '../strategies/auth-flow.strategy'; |
import { filter, map, switchMap, take, tap } from 'rxjs/operators'; |
||||
|
import { |
||||
|
AuthFlowStrategy, |
||||
|
AUTH_FLOW_STRATEGY, |
||||
|
LoginParams, |
||||
|
} from '../strategies/auth-flow.strategy'; |
||||
import { EnvironmentService } from './environment.service'; |
import { EnvironmentService } from './environment.service'; |
||||
|
|
||||
@Injectable({ |
@Injectable({ |
||||
providedIn: 'root', |
providedIn: 'root', |
||||
}) |
}) |
||||
export class AuthService { |
export class AuthService { |
||||
private flow: string; |
|
||||
private strategy: AuthFlowStrategy; |
private strategy: AuthFlowStrategy; |
||||
|
|
||||
get isInternalAuth() { |
get isInternalAuth() { |
||||
return this.strategy.isInternalAuth; |
return this.strategy.isInternalAuth; |
||||
} |
} |
||||
|
|
||||
constructor(private environment: EnvironmentService, private injector: Injector) { |
constructor(protected injector: Injector) {} |
||||
this.setStrategy(); |
|
||||
this.listenToSetEnvironment(); |
|
||||
} |
|
||||
|
|
||||
private setStrategy = () => { |
|
||||
const flow = this.environment.getEnvironment().oAuthConfig.responseType || 'password'; |
|
||||
if (this.flow === flow) return; |
|
||||
|
|
||||
if (this.strategy) this.strategy.destroy(); |
|
||||
|
|
||||
this.flow = flow; |
|
||||
this.strategy = AUTH_FLOW_STRATEGY.Code(this.injector); |
|
||||
}; |
|
||||
|
|
||||
private listenToSetEnvironment() { |
|
||||
this.environment.createOnUpdateStream(state => state.oAuthConfig).subscribe(this.setStrategy); |
|
||||
} |
|
||||
|
|
||||
async init() { |
async init() { |
||||
return await this.strategy.init(); |
const environmentService = this.injector.get(EnvironmentService); |
||||
|
|
||||
|
return environmentService |
||||
|
.getEnvironment$() |
||||
|
.pipe( |
||||
|
map(env => env?.oAuthConfig), |
||||
|
filter(oAuthConfig => !!oAuthConfig), |
||||
|
tap(oAuthConfig => { |
||||
|
this.strategy = |
||||
|
oAuthConfig.responseType === 'code' |
||||
|
? AUTH_FLOW_STRATEGY.Code(this.injector) |
||||
|
: AUTH_FLOW_STRATEGY.Password(this.injector); |
||||
|
}), |
||||
|
switchMap(() => from(this.strategy.init())), |
||||
|
take(1), |
||||
|
) |
||||
|
.toPromise(); |
||||
} |
} |
||||
|
|
||||
logout(): Observable<any> { |
logout(): Observable<any> { |
||||
return this.strategy.logout(); |
return this.strategy.logout(); |
||||
} |
} |
||||
|
|
||||
|
/** |
||||
|
* @deprecated Use navigateToLogin method instead. To be deleted in v5.0 |
||||
|
*/ |
||||
initLogin() { |
initLogin() { |
||||
this.strategy.login(); |
this.strategy.navigateToLogin(); |
||||
|
} |
||||
|
|
||||
|
navigateToLogin() { |
||||
|
this.strategy.navigateToLogin(); |
||||
|
} |
||||
|
|
||||
|
login(params: LoginParams) { |
||||
|
return this.strategy.login(params); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,345 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
namespace MyCompanyName.MyProjectName.Migrations |
||||
|
{ |
||||
|
public partial class CmsKit : Migration |
||||
|
{ |
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsBlogFeatures", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
BlogId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
FeatureName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
IsEnabled = table.Column<bool>(type: "bit", nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsBlogFeatures", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsBlogs", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
Slug = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsBlogs", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsComments", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
Text = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false), |
||||
|
RepliedCommentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsComments", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsEntityTags", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
TagId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
EntityId = table.Column<string>(type: "nvarchar(450)", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsEntityTags", x => new { x.EntityId, x.TagId }); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsMediaDescriptors", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
Name = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false), |
||||
|
MimeType = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false), |
||||
|
Size = table.Column<long>(type: "bigint", maxLength: 2147483647, nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsMediaDescriptors", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsPages", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
||||
|
Slug = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
||||
|
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 2147483647, nullable: true), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsPages", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsRatings", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
StarCount = table.Column<short>(type: "smallint", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsRatings", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsTags", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
Name = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsTags", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsUserReactions", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
ReactionName = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsUserReactions", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsUsers", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
||||
|
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
||||
|
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true), |
||||
|
Surname = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true), |
||||
|
EmailConfirmed = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
PhoneNumber = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: true), |
||||
|
PhoneNumberConfirmed = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsUsers", x => x.Id); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "CmsBlogPosts", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
BlogId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
Title = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), |
||||
|
Slug = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
||||
|
ShortDescription = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true), |
||||
|
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 2147483647, nullable: true), |
||||
|
CoverImageMediaId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
AuthorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_CmsBlogPosts", x => x.Id); |
||||
|
table.ForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_AuthorId", |
||||
|
column: x => x.AuthorId, |
||||
|
principalTable: "CmsUsers", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
}); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_AuthorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "AuthorId"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_Slug_BlogId", |
||||
|
table: "CmsBlogPosts", |
||||
|
columns: new[] { "Slug", "BlogId" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsComments_TenantId_EntityType_EntityId", |
||||
|
table: "CmsComments", |
||||
|
columns: new[] { "TenantId", "EntityType", "EntityId" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsComments_TenantId_RepliedCommentId", |
||||
|
table: "CmsComments", |
||||
|
columns: new[] { "TenantId", "RepliedCommentId" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsEntityTags_TenantId_EntityId_TagId", |
||||
|
table: "CmsEntityTags", |
||||
|
columns: new[] { "TenantId", "EntityId", "TagId" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsPages_TenantId_Slug", |
||||
|
table: "CmsPages", |
||||
|
columns: new[] { "TenantId", "Slug" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsRatings_TenantId_EntityType_EntityId_CreatorId", |
||||
|
table: "CmsRatings", |
||||
|
columns: new[] { "TenantId", "EntityType", "EntityId", "CreatorId" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsTags_TenantId_Name", |
||||
|
table: "CmsTags", |
||||
|
columns: new[] { "TenantId", "Name" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsUserReactions_TenantId_CreatorId_EntityType_EntityId_ReactionName", |
||||
|
table: "CmsUserReactions", |
||||
|
columns: new[] { "TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsUserReactions_TenantId_EntityType_EntityId_ReactionName", |
||||
|
table: "CmsUserReactions", |
||||
|
columns: new[] { "TenantId", "EntityType", "EntityId", "ReactionName" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsUsers_TenantId_Email", |
||||
|
table: "CmsUsers", |
||||
|
columns: new[] { "TenantId", "Email" }); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsUsers_TenantId_UserName", |
||||
|
table: "CmsUsers", |
||||
|
columns: new[] { "TenantId", "UserName" }); |
||||
|
} |
||||
|
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsBlogFeatures"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsBlogs"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsComments"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsEntityTags"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsMediaDescriptors"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsPages"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsRatings"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsTags"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsUserReactions"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "CmsUsers"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue