Add project files.
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
+382
@@ -0,0 +1,382 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
/MyOffice.SPA/.vscode
|
||||||
|
/MyOffice.SPA/dist/
|
||||||
|
/MyOffice.SPA/src/environments/environment.ts
|
||||||
|
/MyOffice.SPA/src/environments/environment.prod.ts
|
||||||
|
|
||||||
|
/MyOffice.Web/.config
|
||||||
|
/MyOffice.Web/appsettings.Production.json
|
||||||
|
/MyOffice.Web/appsettings.Development.json
|
||||||
|
/MyOffice.Web/tempkey.jwk
|
||||||
|
/MyOffice.Web/AppDbContext.Dev.db
|
||||||
|
/MyOffice.Web/AppDbContext.db
|
||||||
|
/MyOffice.Web/AppDbContext.Dev.db-shm
|
||||||
|
/MyOffice.Web/AppDbContext.Dev.db-wal
|
||||||
|
|
||||||
|
/_Published
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
.angular
|
||||||
|
/.template.config/template.json
|
||||||
|
/MyOffice.Shared/appsettings.shared.Development.json
|
||||||
|
/MyOffice.Shared/appsettings.shared.Production.json
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [year] [fullname]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
namespace MyOffice.Core;
|
||||||
|
|
||||||
|
public enum GeneralExecStatus
|
||||||
|
{
|
||||||
|
success,
|
||||||
|
failure,
|
||||||
|
not_found
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Exec<TResult> : Exec<TResult, GeneralExecStatus>
|
||||||
|
where TResult : class
|
||||||
|
{
|
||||||
|
public Exec(TResult result, GeneralExecStatus status) : base(result, status)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec(GeneralExecStatus status) : base(status)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec() : base(GeneralExecStatus.success)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Exec<TResult> Set(GeneralExecStatus status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Exec<TResult> Set(TResult? result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
Status = result == null ? GeneralExecStatus.failure : GeneralExecStatus.success;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Exec<TResult, TStatus>
|
||||||
|
where TResult : class
|
||||||
|
where TStatus : Enum
|
||||||
|
{
|
||||||
|
public static Exec<TResult, TStatus> Start(TResult result, TStatus status)
|
||||||
|
{
|
||||||
|
return new Exec<TResult, TStatus>(result, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exec<TResult, TStatus> Start(TStatus status)
|
||||||
|
{
|
||||||
|
return new Exec<TResult, TStatus>(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exec<TResult, GeneralExecStatus> StartSuccess()
|
||||||
|
{
|
||||||
|
return new Exec<TResult, GeneralExecStatus>(GeneralExecStatus.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exec<TResult, GeneralExecStatus> StartFailure()
|
||||||
|
{
|
||||||
|
return new Exec<TResult, GeneralExecStatus>(GeneralExecStatus.failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exec<TResult, GeneralExecStatus> StartNotFound()
|
||||||
|
{
|
||||||
|
return new Exec<TResult, GeneralExecStatus>(GeneralExecStatus.not_found);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec(TResult result, TStatus status)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec(TStatus status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult? Result { get; internal set; }
|
||||||
|
public TStatus Status { get; internal set; }
|
||||||
|
|
||||||
|
public Exec<TResult, TStatus> Set(TResult result, TStatus status)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
Status = status;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec<TResult, TStatus> Set(TStatus status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exec<TResult, TStatus> Set(TResult result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace MyOffice.Core.Extensions;
|
||||||
|
|
||||||
|
public static class BoolExtensions
|
||||||
|
{
|
||||||
|
public static string ToLowerCase(this bool value)
|
||||||
|
{
|
||||||
|
return value.ToString().ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? ToLowerCase(this bool? value)
|
||||||
|
{
|
||||||
|
return value == null ? null : value.ToString()?.ToLower();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
namespace MyOffice.Core.Extensions;
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
public static class ClaimsExtensions
|
||||||
|
{
|
||||||
|
public static string? GetValue(this IEnumerable<Claim> claims, string type)
|
||||||
|
{
|
||||||
|
return claims.FirstOrDefault(x => x.Type.Equals(type, StringComparison.OrdinalIgnoreCase))?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetValue(this IEnumerable<Claim> claims, string[] types)
|
||||||
|
{
|
||||||
|
return claims.FirstOrDefault(x => types.Any(z => x.Type.Equals(z, StringComparison.OrdinalIgnoreCase)))?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetEmail(this IEnumerable<Claim> claims)
|
||||||
|
{
|
||||||
|
return GetValue(claims, new[] { ClaimTypes.Email, "email" });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetNameIdentifier(this IEnumerable<Claim> claims)
|
||||||
|
{
|
||||||
|
return GetValue(claims, ClaimTypes.NameIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetSID(this IEnumerable<Claim> claims)
|
||||||
|
{
|
||||||
|
return GetValue(claims, "sid");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace MyOffice.Core.Extensions;
|
||||||
|
|
||||||
|
public static class GuidExtensions
|
||||||
|
{
|
||||||
|
public static bool EqualsString(this Guid guid, string str)
|
||||||
|
{
|
||||||
|
return guid.ToString().EqualsIgnoreCase(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace MyOffice.Core.Extensions;
|
||||||
|
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static bool IsMissing(this string? str)
|
||||||
|
{
|
||||||
|
return str == null || string.IsNullOrEmpty(str) || string.IsNullOrEmpty(str.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPresent(this string? str)
|
||||||
|
{
|
||||||
|
return !IsMissing(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? NullIfEmpty(this string? str)
|
||||||
|
{
|
||||||
|
return IsMissing(str) ? null : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool EqualsIgnoreCase(this string? str, string? value)
|
||||||
|
{
|
||||||
|
return str != null && str.Equals(value, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AsBool(this string? str, bool defaultValue = false)
|
||||||
|
{
|
||||||
|
return true.ToString().Equals(str, StringComparison.OrdinalIgnoreCase) || defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
namespace MyOffice.Core.Identity;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
|
public interface IExternalProviderValidator
|
||||||
|
{
|
||||||
|
string Provider { get; }
|
||||||
|
public bool IsConfigured { get; }
|
||||||
|
|
||||||
|
Task<ExternalProviderValidatorResult> ValidateAsync(string token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternalProviderValidatorResult
|
||||||
|
{
|
||||||
|
public static ExternalProviderValidatorResult Failed()
|
||||||
|
{
|
||||||
|
return new ExternalProviderValidatorResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExternalProviderValidatorResult Success(string email, bool emailVerified, string externalId,
|
||||||
|
string? fullName)
|
||||||
|
{
|
||||||
|
return new ExternalProviderValidatorResult(email, emailVerified, externalId, fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExternalProviderValidatorResult()
|
||||||
|
{
|
||||||
|
IsSuccessed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExternalProviderValidatorResult(string email, bool emailVerified, string externalId, string? fullName) :
|
||||||
|
this()
|
||||||
|
{
|
||||||
|
Email = email;
|
||||||
|
EmailVerified = emailVerified;
|
||||||
|
IsSuccessed = true;
|
||||||
|
ExternalId = externalId;
|
||||||
|
FullName = fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSuccessed { get; }
|
||||||
|
public string? Email { get; }
|
||||||
|
public bool EmailVerified { get; }
|
||||||
|
public string? ExternalId { get; }
|
||||||
|
public string? FullName { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
namespace MyOffice.Data.Models.Account;
|
||||||
|
|
||||||
|
using MyOffice.Data.Models.Users;
|
||||||
|
|
||||||
|
public class CurrencyGlobal
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
public IEnumerable<Currency>? Currencies { get; set; }
|
||||||
|
public IEnumerable<Account>? Accounts { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Currency
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string CurrencyGlobalId { get; set; }
|
||||||
|
public CurrencyGlobal? CurrencyGlobal { get; set; }
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public User? User { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
public IEnumerable<CurrencyRate>? Rates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CurrencyRate
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Guid CurrencyId { get; set; }
|
||||||
|
public Currency? Currency { get; set; }
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
public decimal Rate { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string CurrencyGlobalId { get; set; }
|
||||||
|
public CurrencyGlobal? CurrencyGlobal { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public IEnumerable<AccountAccess> AccessRights { get; set; }
|
||||||
|
public IEnumerable<AccountMotion> Motions { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountAccess
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Guid AccountId { get; set; }
|
||||||
|
public Account? Account { get; set; }
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public User? User { get; set; }
|
||||||
|
|
||||||
|
public bool IsAllowRead { get; set; }
|
||||||
|
public bool IsAllowWrite { get; set; }
|
||||||
|
public bool IsAllowManage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountMotion
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public DateTime CreatedOn { get; set; }
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
public Guid AccountId { get; set; }
|
||||||
|
public Account? Account { get; set; }
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public User? User { get; set; }
|
||||||
|
public decimal AmountIn { get; set; }
|
||||||
|
public decimal AmountOut { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace MyOffice.Data.Models.Users;
|
||||||
|
|
||||||
|
using Account;
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string UserName { get; set; } = null!;
|
||||||
|
public string Email { get; set; } = null!;
|
||||||
|
public string PasswordHash { get; set; } = null!;
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
public string? FullName { get; set; }
|
||||||
|
public string? Phone { get; set; }
|
||||||
|
public bool IsEmailConfirmed { get; set; }
|
||||||
|
public IEnumerable<UserExternal>? UserClaims { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<Currency>? Currencies { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace MyOffice.Data.Models.Users;
|
||||||
|
|
||||||
|
public class UserExternal
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public DateTime CreatedOn { get; set; }
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public User User { get; set; } = null!;
|
||||||
|
public string ExternalId { get; set; } = null!;
|
||||||
|
public string Email { get; set; } = null!;
|
||||||
|
public string Provider { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace MyOffice.Data.Repositories;
|
||||||
|
|
||||||
|
using DbContext;
|
||||||
|
|
||||||
|
public class AppRepository<TEntity> : RepositoryBase<TEntity> where TEntity : class
|
||||||
|
{
|
||||||
|
public AppRepository() : base(AppDbContextFactory.Instance.CreateDbContext())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="AppDbContext.cs" />
|
||||||
|
<Compile Remove="AppDbContextFactory.cs" />
|
||||||
|
<Compile Remove="RepositoryInitializer.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyOffice.Data.Models\MyOffice.Data.Models.csproj" />
|
||||||
|
<ProjectReference Include="..\MyOffice.DbContext\MyOffice.DbContext.csproj" />
|
||||||
|
<ProjectReference Include="..\MyOffice.Migration.Sqlite\MyOffice.Migrations.Sqlite.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
namespace MyOffice.Data.Repositories;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using DbContext;
|
||||||
|
|
||||||
|
public class RepositoryBase<TEntity> where TEntity : class
|
||||||
|
{
|
||||||
|
protected readonly AppDbContext _context;
|
||||||
|
|
||||||
|
protected RepositoryBase(
|
||||||
|
AppDbContext context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<int> SaveChangesAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException updateException)
|
||||||
|
{
|
||||||
|
throw updateException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int SaveChanges()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _context.SaveChanges();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException updateException)
|
||||||
|
{
|
||||||
|
throw updateException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async Task<TEntity?> GetAsync(Guid id)
|
||||||
|
{
|
||||||
|
return await _context.Set<TEntity>().FindAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<int> AddAsync(TEntity entity)
|
||||||
|
{
|
||||||
|
await _context.Set<TEntity>().AddAsync(entity);
|
||||||
|
return await SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int Add(TEntity entity)
|
||||||
|
{
|
||||||
|
_context.Set<TEntity>().Add(entity);
|
||||||
|
return SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int Remove(TEntity entity)
|
||||||
|
{
|
||||||
|
_context.Set<TEntity>().Remove(entity);
|
||||||
|
return SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<int> UpdateAsync(TEntity entity)
|
||||||
|
{
|
||||||
|
_context.Attach(entity);
|
||||||
|
_context.Entry(entity).State = EntityState.Modified;
|
||||||
|
|
||||||
|
return await SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int Update(TEntity entity)
|
||||||
|
{
|
||||||
|
_context.Attach(entity);
|
||||||
|
return SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace MyOffice.Data.Repositories.Users;
|
||||||
|
|
||||||
|
using Models.Users;
|
||||||
|
|
||||||
|
public interface IUserExternalRepository
|
||||||
|
{
|
||||||
|
Task<int> AddUserExternalAsync(UserExternal external);
|
||||||
|
int AddUserExternal(UserExternal external);
|
||||||
|
Task<int> AddUserExternalsAsync(IEnumerable<UserExternal> claims);
|
||||||
|
|
||||||
|
Task<List<UserExternal>> GetUserExternalsByUserIdAsync(Guid userId);
|
||||||
|
|
||||||
|
Task<UserExternal?> GetByUserIdAsync(Guid userId, string provider);
|
||||||
|
UserExternal? GetByUserId(Guid userId, string provider);
|
||||||
|
|
||||||
|
UserExternal? GetByExternalId(string externalId, string provider);
|
||||||
|
Task<UserExternal?> GetByExternalIdAsync(string externalId, string provider);
|
||||||
|
|
||||||
|
void RemoveUserExternal(UserExternal userExternal);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace MyOffice.Data.Repositories.Users;
|
||||||
|
|
||||||
|
using MyOffice.Data.Models.Users;
|
||||||
|
|
||||||
|
public interface IUserRepository
|
||||||
|
{
|
||||||
|
Task<User?> GetUserAsync(Guid id);
|
||||||
|
|
||||||
|
Task<User?> GetByUserUserNameAsync(string userName);
|
||||||
|
User? GetByUserUserName(string userName);
|
||||||
|
|
||||||
|
Task<int> AddUserAsync(User user);
|
||||||
|
Task<int> UpdateUserAsync(User user);
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
namespace MyOffice.Data.Repositories.Users;
|
||||||
|
|
||||||
|
using MyOffice.Data.Models.Users;
|
||||||
|
using Repositories;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
public class UserExternalRepository : AppRepository<UserExternal>, IUserExternalRepository
|
||||||
|
{
|
||||||
|
public async Task<int> AddUserExternalAsync(UserExternal userExternal)
|
||||||
|
{
|
||||||
|
return await AddAsync(userExternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddUserExternal(UserExternal userExternal)
|
||||||
|
{
|
||||||
|
return Add(userExternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddUserExternalsAsync(IEnumerable<UserExternal> claims)
|
||||||
|
{
|
||||||
|
_context.AddRange(claims);
|
||||||
|
return await SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<UserExternal>> GetUserExternalsByUserNameAsync(string userName)
|
||||||
|
{
|
||||||
|
return await _context
|
||||||
|
.UserClaims
|
||||||
|
.Where(x => x.User.UserName == userName)
|
||||||
|
.Include(x => x.User)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserExternal>> GetUserExternalsByUserIdAsync(Guid userId)
|
||||||
|
{
|
||||||
|
return await _context
|
||||||
|
.UserClaims
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Include(x => x.User)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserExternal> GetUserExternalsByUser(Guid userId)
|
||||||
|
{
|
||||||
|
return _context
|
||||||
|
.UserClaims
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Include(x => x.User)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserExternal> GetUserExternalsByUserName(string userName)
|
||||||
|
{
|
||||||
|
return _context
|
||||||
|
.UserClaims
|
||||||
|
.Where(x => x.User.UserName == userName)
|
||||||
|
.Include(x => x.User)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserExternal?> GetByUserIdAsync(Guid userId, string provider)
|
||||||
|
{
|
||||||
|
return await _context
|
||||||
|
.UserClaims
|
||||||
|
.Include(x => x.User)
|
||||||
|
.FirstOrDefaultAsync(x => x.UserId == userId && x.Provider == provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserExternal? GetByUserId(Guid userId, string provider)
|
||||||
|
{
|
||||||
|
return _context
|
||||||
|
.UserClaims
|
||||||
|
.Include(x => x.User)
|
||||||
|
.FirstOrDefault(x => x.UserId == userId && x.Provider == provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserExternal? GetByExternalId(string externalId, string provider)
|
||||||
|
{
|
||||||
|
return _context
|
||||||
|
.UserClaims
|
||||||
|
.Include(x => x.User)
|
||||||
|
.FirstOrDefault(x => x.ExternalId == externalId && x.Provider == provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UserExternal?> GetByExternalIdAsync(string externalId, string provider)
|
||||||
|
{
|
||||||
|
return _context
|
||||||
|
.UserClaims
|
||||||
|
.Include(x => x.User)
|
||||||
|
.FirstOrDefaultAsync(x => x.ExternalId == externalId && x.Provider == provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveUserExternal(UserExternal userExternal)
|
||||||
|
{
|
||||||
|
Remove(userExternal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace MyOffice.Data.Repositories.Users;
|
||||||
|
|
||||||
|
using MyOffice.Data.Models.Users;
|
||||||
|
using Repositories;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
public class UserRepository : AppRepository<User>, IUserRepository
|
||||||
|
{
|
||||||
|
public async Task<User?> GetUserAsync(Guid id)
|
||||||
|
{
|
||||||
|
return await GetAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> GetByUserUserNameAsync(string userName)
|
||||||
|
{
|
||||||
|
return await _context.Users.FirstOrDefaultAsync(x => x.UserName == userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User? GetByUserUserName(string userName)
|
||||||
|
{
|
||||||
|
return _context.Users.FirstOrDefault(x => x.UserName == userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddUserAsync(User user)
|
||||||
|
{
|
||||||
|
return await AddAsync(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> UpdateUserAsync(User user)
|
||||||
|
{
|
||||||
|
return await UpdateAsync(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
namespace MyOffice.DbContext;
|
||||||
|
|
||||||
|
using Data.Models.Account;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Data.Models.Users;
|
||||||
|
|
||||||
|
public enum AppDbContextProvidersEnum
|
||||||
|
{
|
||||||
|
sqlite,
|
||||||
|
mssql,
|
||||||
|
npgsql
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly AppDbContextProvidersEnum _provider;
|
||||||
|
|
||||||
|
private readonly Dictionary<AppDbContextProvidersEnum, string> _noCaseCollation = new()
|
||||||
|
{
|
||||||
|
{ AppDbContextProvidersEnum.sqlite, "NOCASE" },
|
||||||
|
{ AppDbContextProvidersEnum.npgsql, "my_ci_collation" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public AppDbContext(AppDbContextProvidersEnum provider, DbContextOptions<AppDbContext> options) : base(options)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<User> Users { get; set; } = null!;
|
||||||
|
public DbSet<UserExternal> UserClaims { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
var noCaseCollation = _noCaseCollation[_provider];
|
||||||
|
|
||||||
|
if (_provider == AppDbContextProvidersEnum.npgsql)
|
||||||
|
modelBuilder.HasCollation("my_ci_collation", "en-u-ks-primary", "icu", false);
|
||||||
|
|
||||||
|
/* NOCASE PROPERTIES */
|
||||||
|
modelBuilder.Entity<User>()
|
||||||
|
.Property(x => x.UserName)
|
||||||
|
.UseCollation(noCaseCollation);
|
||||||
|
|
||||||
|
modelBuilder.Entity<User>()
|
||||||
|
.Property(x => x.Email)
|
||||||
|
.UseCollation(noCaseCollation);
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserExternal>()
|
||||||
|
.Property(x => x.Provider)
|
||||||
|
.UseCollation(noCaseCollation);
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserExternal>()
|
||||||
|
.Property(x => x.Email)
|
||||||
|
.UseCollation(noCaseCollation);
|
||||||
|
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserExternal>()
|
||||||
|
.HasOne(x => x.User)
|
||||||
|
.WithMany(x => x.UserClaims)
|
||||||
|
.HasForeignKey(x => x.UserId);
|
||||||
|
|
||||||
|
CurrencyCreating(modelBuilder);
|
||||||
|
AccountCreating(modelBuilder);
|
||||||
|
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CurrencyCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<CurrencyGlobal>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Currency>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<CurrencyRate>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Currency>()
|
||||||
|
.HasOne(x => x.CurrencyGlobal)
|
||||||
|
.WithMany(x => x.Currencies)
|
||||||
|
.HasForeignKey(x => x.CurrencyGlobalId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Currency>()
|
||||||
|
.HasOne(x => x.User)
|
||||||
|
.WithMany(x => x.Currencies)
|
||||||
|
.HasForeignKey(x => x.UserId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<CurrencyRate>()
|
||||||
|
.HasOne(x => x.Currency)
|
||||||
|
.WithMany(x => x.Rates)
|
||||||
|
.HasForeignKey(x => x.CurrencyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AccountCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Account>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AccountAccess>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AccountMotion>()
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Account>()
|
||||||
|
.HasOne(x => x.CurrencyGlobal)
|
||||||
|
.WithMany(x => x.Accounts)
|
||||||
|
.HasForeignKey(x => x.CurrencyGlobalId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AccountAccess>()
|
||||||
|
.HasOne(x => x.Account)
|
||||||
|
.WithMany(x => x.AccessRights)
|
||||||
|
.HasForeignKey(x => x.AccountId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AccountAccess>()
|
||||||
|
.HasOne(x => x.User)
|
||||||
|
.WithMany(x => x.AccessRights)
|
||||||
|
.HasForeignKey(x => x.AccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
namespace MyOffice.DbContext;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
|
||||||
|
{
|
||||||
|
public static AppDbContextFactory Instance = new();
|
||||||
|
|
||||||
|
public AppDbContext CreateDbContext()
|
||||||
|
{
|
||||||
|
return CreateDbContext(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppDbContext CreateDbContext(string[]? args)
|
||||||
|
{
|
||||||
|
var connection = RepositoryInitializer.ConnectionString;
|
||||||
|
|
||||||
|
var builder = new DbContextOptionsBuilder<AppDbContext>();
|
||||||
|
|
||||||
|
var provider = connection?.Provider ?? args?[0] ?? "";
|
||||||
|
var connectionString = connection?.ConnectionString ?? args?[1] ?? "";
|
||||||
|
|
||||||
|
if (!Enum.TryParse<AppDbContextProvidersEnum>(provider, out var providerEnum))
|
||||||
|
throw new InvalidOperationException($"No such provider: [{provider}]");
|
||||||
|
|
||||||
|
switch (providerEnum)
|
||||||
|
{
|
||||||
|
case AppDbContextProvidersEnum.npgsql:
|
||||||
|
builder.UseNpgsql(connectionString, x => x.MigrationsAssembly("MyOffice.Migrations.Postgres"));
|
||||||
|
break;
|
||||||
|
case AppDbContextProvidersEnum.sqlite:
|
||||||
|
builder.UseSqlite(connectionString, x => x.MigrationsAssembly("MyOffice.Migrations.Sqlite"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"No such provider: [{provider}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
var db = new AppDbContext(providerEnum, builder.Options);
|
||||||
|
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyOffice.Data.Models\MyOffice.Data.Models.csproj" />
|
||||||
|
<ProjectReference Include="..\MyOffice.Shared\MyOffice.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
namespace MyOffice.DbContext;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
public record ConnectionConfiguration(string Provider, string ConnectionString);
|
||||||
|
|
||||||
|
public class RepositoryInitializer
|
||||||
|
{
|
||||||
|
public static void Initialize(
|
||||||
|
IServiceCollection services,
|
||||||
|
ConnectionConfiguration connectionString
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Services = services!;
|
||||||
|
ConnectionString = connectionString;
|
||||||
|
|
||||||
|
var db = AppDbContextFactory.Instance.CreateDbContext();
|
||||||
|
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
//db.Database.EnsureCreated();
|
||||||
|
db.Database.Migrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionConfiguration ConnectionString { get; internal set; } = null!;
|
||||||
|
public static IServiceCollection Services { get; internal set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace MyOffice.Migrations;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using DbContext;
|
||||||
|
using Shared;
|
||||||
|
|
||||||
|
public class AppDbContextFactoryPostgres : IDesignTimeDbContextFactory<AppDbContext>
|
||||||
|
{
|
||||||
|
public AppDbContext CreateDbContext(string[]? args)
|
||||||
|
{
|
||||||
|
var builder = new DbContextOptionsBuilder<AppDbContext>();
|
||||||
|
|
||||||
|
var sharedConfiguration = SharedConfiguration.CreateConfigurationContainer();
|
||||||
|
var connectionString =
|
||||||
|
args?.FirstOrDefault()
|
||||||
|
?? sharedConfiguration.GetConnectionString("npgsql");
|
||||||
|
|
||||||
|
builder.UseNpgsql(connectionString, b => b.MigrationsAssembly("MyOffice.Migrations.Postgres"));
|
||||||
|
|
||||||
|
var db = new AppDbContext(AppDbContextProvidersEnum.npgsql, builder.Options);
|
||||||
|
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20230408172132_init")]
|
||||||
|
partial class init
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class init : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterDatabase()
|
||||||
|
.Annotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
UserName = table.Column<string>(type: "text", nullable: false, collation: "my_ci_collation"),
|
||||||
|
Email = table.Column<string>(type: "text", nullable: false, collation: "my_ci_collation"),
|
||||||
|
PasswordHash = table.Column<string>(type: "text", nullable: false),
|
||||||
|
FirstName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
LastName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
FullName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
IsEmailConfirmed = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
CreatedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ExternalId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Email = table.Column<string>(type: "text", nullable: false, collation: "my_ci_collation"),
|
||||||
|
Provider = table.Column<string>(type: "text", nullable: false, collation: "my_ci_collation")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserClaims_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserClaims_UserId",
|
||||||
|
table: "UserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20230408172213_phone")]
|
||||||
|
partial class phone
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class phone : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Users",
|
||||||
|
type: "text",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.UseCollation("my_ci_collation");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyOffice.DbContext\MyOffice.DbContext.csproj" />
|
||||||
|
<ProjectReference Include="..\MyOffice.Shared\MyOffice.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Migrations\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace MyOffice.Migrations;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using DbContext;
|
||||||
|
using Shared;
|
||||||
|
|
||||||
|
public class AppDbContextFactorySqlite : IDesignTimeDbContextFactory<AppDbContext>
|
||||||
|
{
|
||||||
|
public AppDbContext CreateDbContext(string[]? args)
|
||||||
|
{
|
||||||
|
var builder = new DbContextOptionsBuilder<AppDbContext>();
|
||||||
|
|
||||||
|
var sharedConfiguration = SharedConfiguration.CreateConfigurationContainer();
|
||||||
|
var connectionString =
|
||||||
|
args?.FirstOrDefault()
|
||||||
|
?? sharedConfiguration.GetConnectionString("sqlite");
|
||||||
|
|
||||||
|
builder.UseSqlite(connectionString, b => b.MigrationsAssembly("MyOffice.Migrations.Sqlite"));
|
||||||
|
|
||||||
|
var db = new AppDbContext(AppDbContextProvidersEnum.sqlite, builder.Options);
|
||||||
|
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20230408180122_init")]
|
||||||
|
partial class init
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.0");
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class init : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
UserName = table.Column<string>(type: "TEXT", nullable: false, collation: "NOCASE"),
|
||||||
|
Email = table.Column<string>(type: "TEXT", nullable: false, collation: "NOCASE"),
|
||||||
|
PasswordHash = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
FirstName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
LastName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
FullName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
IsEmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UserId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
ExternalId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Email = table.Column<string>(type: "TEXT", nullable: false, collation: "NOCASE"),
|
||||||
|
Provider = table.Column<string>(type: "TEXT", nullable: false, collation: "NOCASE")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserClaims_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserClaims_UserId",
|
||||||
|
table: "UserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20230408180149_phone")]
|
||||||
|
partial class phone
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.0");
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class phone : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Users",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using MyOffice.DbContext;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MyOffice.Migrations.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.0");
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEmailConfirmed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<string>("ExternalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.UseCollation("NOCASE");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MyOffice.Data.Models.Users.User", "User")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("UserClaims");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyOffice.DbContext\MyOffice.DbContext.csproj" />
|
||||||
|
<ProjectReference Include="..\MyOffice.Shared\MyOffice.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = tabs
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"ignorePatterns": [
|
||||||
|
"projects/**/*"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.ts"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@angular-eslint/recommended",
|
||||||
|
"plugin:@angular-eslint/template/process-inline-templates"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@angular-eslint/directive-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"type": "attribute",
|
||||||
|
"prefix": "app",
|
||||||
|
"style": "camelCase"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@angular-eslint/component-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"prefix": "app",
|
||||||
|
"style": "kebab-case"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.html"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@angular-eslint/template/recommended"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
legacy-peer-deps=true
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/0.5.62-alpha">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>b02d9868-26cb-4e98-95d8-7286bf255f89</ProjectGuid>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<StartupCommand>npm start</StartupCommand>
|
||||||
|
<JavaScriptTestFramework>Jasmine</JavaScriptTestFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="src\app\layout\app-layout\" />
|
||||||
|
<Folder Include="src\app\shared\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<TypeScriptConfiguration Remove="dist\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\styles.08cfaa5c1b59bc73.css" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\silent-refresh.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\runtime.87754ad940103f45.js" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\polyfills.a8a7418bf37e8989.js" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\main.1adfc6f05d66bd98.js" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\index.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\favicon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="dist\angular-template\3rdpartylicenses.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# MyOffice
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"MyOffice": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/angular-template",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets",
|
||||||
|
"src/silent-refresh.html"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"@angular/material/prebuilt-themes/indigo-pink.css",
|
||||||
|
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||||
|
"src/assets/scss/style.scss",
|
||||||
|
"src/assets/scss/theme/all-themes.scss",
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"stylePreprocessorOptions": {
|
||||||
|
"includePaths": ["node_modules/"]
|
||||||
|
},
|
||||||
|
"scripts": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "1mb",
|
||||||
|
"maximumError": "3mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "2kb",
|
||||||
|
"maximumError": "4kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "MyOffice:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "MyOffice:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"proxyConfig": "src/proxy.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "MyOffice:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"karmaConfig": "karma.conf.js",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// This script sets up HTTPS for the application using the ASP.NET Core HTTPS certificate
|
||||||
|
const fs = require('fs');
|
||||||
|
const spawn = require('child_process').spawn;
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const baseFolder =
|
||||||
|
process.env.APPDATA !== undefined && process.env.APPDATA !== ''
|
||||||
|
? `${process.env.APPDATA}/ASP.NET/https`
|
||||||
|
: `${process.env.HOME}/.aspnet/https`;
|
||||||
|
|
||||||
|
const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
|
||||||
|
const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
|
||||||
|
|
||||||
|
if (!certificateName) {
|
||||||
|
console.error(
|
||||||
|
'Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<<app>> explicitly.');
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
|
||||||
|
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
|
||||||
|
|
||||||
|
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
|
||||||
|
spawn('dotnet',
|
||||||
|
[
|
||||||
|
'dev-certs',
|
||||||
|
'https',
|
||||||
|
'--export-path',
|
||||||
|
certFilePath,
|
||||||
|
'--format',
|
||||||
|
'Pem',
|
||||||
|
'--no-password',
|
||||||
|
],
|
||||||
|
{ stdio: 'inherit', })
|
||||||
|
.on('exit', (code) => process.exit(code));
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function(config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '',
|
||||||
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage'),
|
||||||
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
jasmine: {
|
||||||
|
// you can add configuration options for Jasmine here
|
||||||
|
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||||
|
// for example, you can disable the random execution with `random: false`
|
||||||
|
// or set a specific seed with `seed: 4321`
|
||||||
|
|
||||||
|
},
|
||||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
|
},
|
||||||
|
jasmineHtmlReporter: {
|
||||||
|
suppressAll: true // removes the duplicated traces
|
||||||
|
},
|
||||||
|
coverageReporter: {
|
||||||
|
dir: require('path').join(__dirname, './coverage/angular-template'),
|
||||||
|
subdir: '.',
|
||||||
|
reporters: [
|
||||||
|
{ type: 'html' },
|
||||||
|
{ type: 'text-summary' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<clear />
|
||||||
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
<disabledPackageSources>
|
||||||
|
<clear />
|
||||||
|
</disabledPackageSources>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"name": "angular-template",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"start-ssl":
|
||||||
|
"ng serve --ssl --ssl-cert %APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem --ssl-key %APPDATA%\\ASP.NET\\https\\%npm_package_name%.key",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test",
|
||||||
|
"prestart": "node aspnetcore-https"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@abacritt/angularx-social-login": "^2.0.0",
|
||||||
|
"@angular/animations": "^15.2.0",
|
||||||
|
"@angular/cdk": "^15.2.0",
|
||||||
|
"@angular/common": "^15.2.0",
|
||||||
|
"@angular/compiler": "^15.2.0",
|
||||||
|
"@angular/core": "^15.2.0",
|
||||||
|
"@angular/forms": "^15.2.0",
|
||||||
|
"@angular/google-maps": "^15.2.0",
|
||||||
|
"@angular/localize": "^15.2.0",
|
||||||
|
"@angular/material": "^15.2.0",
|
||||||
|
"@angular/platform-browser": "^15.2.0",
|
||||||
|
"@angular/platform-browser-dynamic": "^15.2.0",
|
||||||
|
"@angular/router": "^15.2.0",
|
||||||
|
"@ckeditor/ckeditor5-angular": "^5.1.0",
|
||||||
|
"@ckeditor/ckeditor5-build-classic": "^36.0.1",
|
||||||
|
"@danielmoncada/angular-datetime-picker": "^15.0.2",
|
||||||
|
"@fullcalendar/angular": "^6.1.4",
|
||||||
|
"@fullcalendar/core": "^6.1.4",
|
||||||
|
"@fullcalendar/daygrid": "^6.1.4",
|
||||||
|
"@fullcalendar/interaction": "^6.1.4",
|
||||||
|
"@fullcalendar/list": "^6.1.4",
|
||||||
|
"@fullcalendar/timegrid": "^6.1.4",
|
||||||
|
"@ngx-loading-bar/core": "^6.0.2",
|
||||||
|
"@ngx-loading-bar/http-client": "^6.0.2",
|
||||||
|
"@ngx-loading-bar/router": "^6.0.2",
|
||||||
|
"@ngx-translate/core": "^14.0.0",
|
||||||
|
"@ngx-translate/http-loader": "^7.0.0",
|
||||||
|
"@popperjs/core": "^2.11.6",
|
||||||
|
"@swimlane/ngx-charts": "^20.1.2",
|
||||||
|
"@swimlane/ngx-datatable": "^20.1.0",
|
||||||
|
"@types/d3-shape": "^3.1.1",
|
||||||
|
"angular-oauth2-oidc": "^15.0.1",
|
||||||
|
"angular-auth-oidc-client": "^15.0.3",
|
||||||
|
"@auth0/auth0-angular": "^2.0.2",
|
||||||
|
"angular-feather": "^6.5.0",
|
||||||
|
"angular-gauge": "^4.0.0",
|
||||||
|
"apexcharts": "^3.37.0",
|
||||||
|
"bootstrap": "^5.2.3",
|
||||||
|
"browser-sync": "^2.27.5",
|
||||||
|
"chart.js": "^4.2.1",
|
||||||
|
"core-js": "^3.28.0",
|
||||||
|
"echarts": "^5.4.1",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"ng-apexcharts": "^1.7.4",
|
||||||
|
"ng-image-fullscreen-view": "^3.0.3",
|
||||||
|
"ng2-charts": "^4.1.1",
|
||||||
|
"ngx-dropzone-wrapper": "^13.0.0",
|
||||||
|
"ngx-echarts": "^15.0.1",
|
||||||
|
"ngx-gauge": "^7.0.0",
|
||||||
|
"ngx-mask": "^14.2.4",
|
||||||
|
"ngx-scrollbar": "^11.0.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"subsink": "^1.0.2",
|
||||||
|
"sweetalert2": "^11.4.8",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"zone.js": "~0.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^15.2.0",
|
||||||
|
"@angular-eslint/builder": "15.2.1",
|
||||||
|
"@angular-eslint/eslint-plugin": "15.2.1",
|
||||||
|
"@angular-eslint/eslint-plugin-template": "15.2.1",
|
||||||
|
"@angular-eslint/schematics": "15.2.1",
|
||||||
|
"@angular-eslint/template-parser": "15.2.1",
|
||||||
|
"@angular/cli": "~15.2.0",
|
||||||
|
"@angular/compiler-cli": "^15.2.0",
|
||||||
|
"@types/ckeditor__ckeditor5-build-classic": "^29.0.1",
|
||||||
|
"@types/d3": "^7.4.0",
|
||||||
|
"@types/jasmine": "~4.3.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||||
|
"@typescript-eslint/parser": "5.48.2",
|
||||||
|
"eslint": "^8.33.0",
|
||||||
|
"jasmine-core": "~4.5.0",
|
||||||
|
"karma": "~6.4.0",
|
||||||
|
"karma-chrome-launcher": "~3.1.0",
|
||||||
|
"karma-coverage": "~2.2.0",
|
||||||
|
"karma-jasmine": "~5.1.0",
|
||||||
|
"karma-jasmine-html-reporter": "~2.0.0",
|
||||||
|
"typescript": "~4.9.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export enum ApiRoutes {
|
||||||
|
UserRegister = '/api/user/register',
|
||||||
|
UserLogin = '/api/user/login',
|
||||||
|
UserProfile = '/api/user/profile',
|
||||||
|
UserAttach = '/api/user/attach',
|
||||||
|
UserDeattach = '/api/user/deattach',
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { Page404Component } from './authentication/page404/page404.component';
|
||||||
|
import { AuthGuard } from './core/guard/auth.guard';
|
||||||
|
import { AuthLayoutComponent } from './layout/app-layout/auth-layout/auth-layout.component';
|
||||||
|
import { MainLayoutComponent } from './layout/app-layout/main-layout/main-layout.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: MainLayoutComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
children: [
|
||||||
|
{ path: '', redirectTo: '/authentication/signin', pathMatch: 'full' },
|
||||||
|
{
|
||||||
|
path: 'dashboard',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'extra-pages',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./extra-pages/extra-pages.module').then(
|
||||||
|
(m) => m.ExtraPagesModule
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'multilevel',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./multilevel/multilevel.module').then(
|
||||||
|
(m) => m.MultilevelModule
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'authentication',
|
||||||
|
component: AuthLayoutComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./authentication/authentication.module').then(
|
||||||
|
(m) => m.AuthenticationModule
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: MainLayoutComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./pages/pages.module').then(
|
||||||
|
(m) => m.PagesModule
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ path: '**', component: Page404Component },
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forRoot(routes, {})],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AppRoutingModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<app-page-loader></app-page-loader>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent',
|
||||||
|
() => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
RouterTestingModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app',
|
||||||
|
() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'spire'`,
|
||||||
|
() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app.title).toEqual('spire');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title',
|
||||||
|
() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
expect(compiled.querySelector('.content span')?.textContent).toContain('spire app is running!');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Event, Router, NavigationStart, NavigationEnd } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.scss'],
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
currentUrl!: string;
|
||||||
|
|
||||||
|
constructor(public _router: Router) {
|
||||||
|
this._router.events.subscribe((routerEvent: Event) => {
|
||||||
|
if (routerEvent instanceof NavigationStart) {
|
||||||
|
this.currentUrl = routerEvent.url.substring(
|
||||||
|
routerEvent.url.lastIndexOf('/') + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (routerEvent instanceof NavigationEnd) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// angular
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
|
||||||
|
import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
// libs
|
||||||
|
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||||
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||||
|
import { LoadingBarRouterModule } from '@ngx-loading-bar/router';
|
||||||
|
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client';
|
||||||
|
import { NgScrollbarModule } from 'ngx-scrollbar';
|
||||||
|
|
||||||
|
// app
|
||||||
|
import { CoreModule } from './core/core.module';
|
||||||
|
import { SharedModule } from './shared/shared.module';
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { HeaderComponent } from './layout/header/header.component';
|
||||||
|
import { PageLoaderComponent } from './layout/page-loader/page-loader.component';
|
||||||
|
import { SidebarComponent } from './layout/sidebar/sidebar.component';
|
||||||
|
import { RightSidebarComponent } from './layout/right-sidebar/right-sidebar.component';
|
||||||
|
import { AuthLayoutComponent } from './layout/app-layout/auth-layout/auth-layout.component';
|
||||||
|
import { MainLayoutComponent } from './layout/app-layout/main-layout/main-layout.component';
|
||||||
|
import { ErrorInterceptor } from './core/interceptor/error.interceptor';
|
||||||
|
import { JwtInterceptor } from './core/interceptor/jwt.interceptor';
|
||||||
|
|
||||||
|
export function createTranslateLoader(http: HttpClient) {
|
||||||
|
return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
HeaderComponent,
|
||||||
|
PageLoaderComponent,
|
||||||
|
SidebarComponent,
|
||||||
|
RightSidebarComponent,
|
||||||
|
AuthLayoutComponent,
|
||||||
|
MainLayoutComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
HttpClientModule,
|
||||||
|
LoadingBarHttpClientModule,
|
||||||
|
LoadingBarRouterModule,
|
||||||
|
NgScrollbarModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useFactory: createTranslateLoader,
|
||||||
|
deps: [HttpClient],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// core & shared
|
||||||
|
CoreModule,
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
|
||||||
|
],
|
||||||
|
bootstrap: [AppComponent],
|
||||||
|
})
|
||||||
|
export class AppModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { SigninComponent } from './signin/signin.component';
|
||||||
|
import { SignupComponent } from './signup/signup.component';
|
||||||
|
import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
|
||||||
|
import { LockedComponent } from './locked/locked.component';
|
||||||
|
import { Page404Component } from './page404/page404.component';
|
||||||
|
import { Page500Component } from './page500/page500.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'signin',
|
||||||
|
pathMatch: 'full',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'signin',
|
||||||
|
component: SigninComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'signup',
|
||||||
|
component: SignupComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'forgot-password',
|
||||||
|
component: ForgotPasswordComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'locked',
|
||||||
|
component: LockedComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'page404',
|
||||||
|
component: Page404Component,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'page500',
|
||||||
|
component: Page500Component,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AuthenticationRoutingModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// angular
|
||||||
|
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
|
||||||
|
// libs
|
||||||
|
import { GoogleSigninButtonModule } from '@abacritt/angularx-social-login';
|
||||||
|
|
||||||
|
// app
|
||||||
|
import { AuthenticationRoutingModule } from './authentication-routing.module';
|
||||||
|
import { Page500Component } from './page500/page500.component';
|
||||||
|
import { Page404Component } from './page404/page404.component';
|
||||||
|
import { SigninComponent } from './signin/signin.component';
|
||||||
|
import { SignupComponent } from './signup/signup.component';
|
||||||
|
import { LockedComponent } from './locked/locked.component';
|
||||||
|
import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AuthenticationRoutingModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatButtonModule,
|
||||||
|
GoogleSigninButtonModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
Page500Component,
|
||||||
|
Page404Component,
|
||||||
|
SigninComponent,
|
||||||
|
SignupComponent,
|
||||||
|
LockedComponent,
|
||||||
|
ForgotPasswordComponent,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
],
|
||||||
|
schemas: [
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AuthenticationModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-03.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<h2 class="welcome-msg"> Reset Password </h2>
|
||||||
|
<p class="auth-signup-text text-muted">Let Us Help You</p>
|
||||||
|
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<span class="error-subheader2 p-t-20 p-b-15">
|
||||||
|
Enter your registered email address.
|
||||||
|
</span>
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput formControlName="email" required>
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>mail</mat-icon>
|
||||||
|
<mat-error *ngIf="authForm.get('email')?.hasError('required') || authForm.get('email')?.touched">
|
||||||
|
Please enter a valid email address
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-auth-form-btn mt-5">
|
||||||
|
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||||
|
Reset My Password
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full p-t-25 text-center">
|
||||||
|
<div>
|
||||||
|
<a routerLink="/authentication/signin" class="txt1">
|
||||||
|
Login?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { ForgotPasswordComponent } from './forgot-password.component';
|
||||||
|
describe('ForgotPasswordComponent',
|
||||||
|
() => {
|
||||||
|
let component: ForgotPasswordComponent;
|
||||||
|
let fixture: ComponentFixture<ForgotPasswordComponent>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ForgotPasswordComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ForgotPasswordComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import {
|
||||||
|
UntypedFormBuilder,
|
||||||
|
UntypedFormGroup,
|
||||||
|
Validators,
|
||||||
|
} from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-forgot-password',
|
||||||
|
templateUrl: './forgot-password.component.html',
|
||||||
|
styleUrls: ['./forgot-password.component.scss'],
|
||||||
|
})
|
||||||
|
export class ForgotPasswordComponent implements OnInit {
|
||||||
|
authForm!: UntypedFormGroup;
|
||||||
|
submitted = false;
|
||||||
|
returnUrl!: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: UntypedFormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.authForm = this.formBuilder.group({
|
||||||
|
email: [
|
||||||
|
'',
|
||||||
|
[Validators.required, Validators.email, Validators.minLength(5)],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// get return url from route parameters or default to '/'
|
||||||
|
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
get f() {
|
||||||
|
return this.authForm.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
this.submitted = true;
|
||||||
|
// stop here if form is invalid
|
||||||
|
if (this.authForm.invalid) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/dashboard/main']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-01.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="auth-locked">
|
||||||
|
<div class="image">
|
||||||
|
<img src={{userImg}} alt="User">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="auth-locked-title p-b-34 p-t-27">
|
||||||
|
{{userFullName}}
|
||||||
|
</span>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="txt1 p-b-20">
|
||||||
|
Locked
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<span class="error-subheader2 p-t-20 p-b-15">
|
||||||
|
Enter your password here.
|
||||||
|
</span>
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Password</mat-label>
|
||||||
|
<input matInput formControlName="password" [type]="hide ? 'password' : 'text'" required>
|
||||||
|
<mat-icon matSuffix (click)="hide = !hide">
|
||||||
|
{{hide ? 'visibility_off' : 'visibility'}}
|
||||||
|
</mat-icon>
|
||||||
|
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||||
|
Password is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-auth-form-btn mt-5">
|
||||||
|
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||||
|
Reset My Password
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full p-t-15 p-b-15 text-center">
|
||||||
|
<div>
|
||||||
|
<a routerLink="/authentication/signin" class="txt1">
|
||||||
|
Need Help?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { LockedComponent } from './locked.component';
|
||||||
|
describe('LockedComponent',
|
||||||
|
() => {
|
||||||
|
let component: LockedComponent;
|
||||||
|
let fixture: ComponentFixture<LockedComponent>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [LockedComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LockedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
import { AuthService } from 'src/app/core/service/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-locked',
|
||||||
|
templateUrl: './locked.component.html',
|
||||||
|
styleUrls: ['./locked.component.scss'],
|
||||||
|
})
|
||||||
|
export class LockedComponent implements OnInit {
|
||||||
|
authForm!: UntypedFormGroup;
|
||||||
|
submitted = false;
|
||||||
|
userImg!: string;
|
||||||
|
userFullName!: string;
|
||||||
|
hide = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: UntypedFormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private authService: AuthService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.authForm = this.formBuilder.group({
|
||||||
|
password: ['', Validators.required],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.userImg = this.authService.currentUserValue.img || 'assets/images/user/admin.jpg';
|
||||||
|
|
||||||
|
this.userFullName =
|
||||||
|
this.authService.currentUserValue.firstName +
|
||||||
|
' ' +
|
||||||
|
this.authService.currentUserValue.lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get f() {
|
||||||
|
return this.authForm.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
this.submitted = true;
|
||||||
|
// stop here if form is invalid
|
||||||
|
if (this.authForm.invalid) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/dashboard/dashboard1']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-04.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<form>
|
||||||
|
<span class="error-header p-b-45">
|
||||||
|
404
|
||||||
|
</span>
|
||||||
|
<span class="error-subheader p-b-5">
|
||||||
|
Looks Like You're Lost
|
||||||
|
</span>
|
||||||
|
<span class="error-subheader2 p-b-5">
|
||||||
|
The Page You Are Looking For Not Available!
|
||||||
|
</span>
|
||||||
|
<div class="container-auth-form-btn mt-5">
|
||||||
|
<button mat-flat-button color="primary" class="auth-form-btn" type="submit" routerLink="/dashboard">
|
||||||
|
Go To Home Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full p-t-15 p-b-15 text-center">
|
||||||
|
<div>
|
||||||
|
<a routerLink="/authentication/signin" class="txt1">
|
||||||
|
Need Help?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { Page404Component } from './page404.component';
|
||||||
|
|
||||||
|
describe('Page404Component',
|
||||||
|
() => {
|
||||||
|
let component: Page404Component;
|
||||||
|
let fixture: ComponentFixture<Page404Component>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Page404Component],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(Page404Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page404',
|
||||||
|
templateUrl: './page404.component.html',
|
||||||
|
styleUrls: ['./page404.component.scss'],
|
||||||
|
})
|
||||||
|
export class Page404Component {
|
||||||
|
constructor() {
|
||||||
|
// constructor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-05.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<form>
|
||||||
|
<span class="error-header p-b-45">
|
||||||
|
500
|
||||||
|
</span>
|
||||||
|
<span class="error-subheader2 p-b-5">
|
||||||
|
Oops, Something went wrong. Please try after some times.
|
||||||
|
</span>
|
||||||
|
<div class="container-auth-form-btn mt-5">
|
||||||
|
<button mat-flat-button color="primary" class="auth-form-btn" type="submit">
|
||||||
|
Go To Home Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full p-t-15 p-b-15 text-center">
|
||||||
|
<div>
|
||||||
|
<a routerLink="/authentication/signin" class="txt1">
|
||||||
|
Need Help?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { Page500Component } from './page500.component';
|
||||||
|
|
||||||
|
describe('Page500Component',
|
||||||
|
() => {
|
||||||
|
let component: Page500Component;
|
||||||
|
let fixture: ComponentFixture<Page500Component>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Page500Component],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(Page500Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page500',
|
||||||
|
templateUrl: './page500.component.html',
|
||||||
|
styleUrls: ['./page500.component.scss'],
|
||||||
|
})
|
||||||
|
export class Page500Component {
|
||||||
|
constructor() {
|
||||||
|
// constructor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-01.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<h2 class="welcome-msg">
|
||||||
|
<div>Welcome to ase.com.ua</div>
|
||||||
|
<div>angular/asp.net core template</div>
|
||||||
|
</h2>
|
||||||
|
<p class="auth-signup-text text-muted">
|
||||||
|
Need an account?
|
||||||
|
<a routerLink="/authentication/signup" class="sign-up-link">
|
||||||
|
Sign Up
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h2 class="login-title">Sign in</h2>
|
||||||
|
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Username</mat-label>
|
||||||
|
<input matInput formControlName="username"/>
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>face</mat-icon>
|
||||||
|
<mat-error *ngIf="authForm.get('username')?.hasError('required')">
|
||||||
|
Username is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Password</mat-label>
|
||||||
|
<input matInput [type]="hide ? 'password' : 'text'" formControlName="password">
|
||||||
|
<a href="#" onClick="return false;" matSuffix (click)="hide = !hide"
|
||||||
|
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||||
|
{{hide ? 'visibility_off' : 'visibility'}}
|
||||||
|
</mat-icon>
|
||||||
|
</a>
|
||||||
|
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||||
|
Password is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-5">
|
||||||
|
<div class="form-check">
|
||||||
|
<label class="form-check-label">
|
||||||
|
<input class="form-check-input" type="checkbox" value=""> Remember me
|
||||||
|
<span class="form-check-sign">
|
||||||
|
<span class="check"></span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<a class="txt1" routerLink="/authentication/forgot-password">Forgot Password?</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>
|
||||||
|
|
||||||
|
<div class="container-auth-form-btn">
|
||||||
|
<div style="text-align: center">
|
||||||
|
<button mat-raised-button color="primary" [class.auth-spinner]="loading" [disabled]="loading"
|
||||||
|
class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<h6 class="social-login-title">OR</h6>
|
||||||
|
<ul class="list-unstyled social-icon mb-0 mt-3">
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded" (click)="loginAuth0()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 135 150">
|
||||||
|
<title>auth0-glyph</title>
|
||||||
|
<path d="M131.225 46.364L116.136 0H67.4l15.06 46.364h48.764zM67.4 0H18.664L3.602 46.364H52.34L67.399 0zM3.602 46.364c-8.982 27.639.856 57.918 24.368 75L43.032 75 3.602 46.364zm127.623 0L91.795 75l15.062 46.364c23.512-17.082 33.35-47.361 24.368-75zm-103.255 75L67.4 150l39.43-28.636L67.4 92.727l-39.43 28.637z" fill="#3c4858"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<!--
|
||||||
|
<a href="javascript:void(0)" class="rounded" (click)="loginGoogle()">
|
||||||
|
<i class="fab fa-google"></i>
|
||||||
|
</a>
|
||||||
|
-->
|
||||||
|
<asl-google-signin-button type="icon" size="medium" width="200" shape="rectangular" theme="filled_black" logo_alignment="center" locale=""></asl-google-signin-button>
|
||||||
|
</li>
|
||||||
|
<!--
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded flex-c-m">
|
||||||
|
<i class="fab fa-facebook-f"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded">
|
||||||
|
<i class="fab fa-linkedin-in"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
-->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { SigninComponent } from './signin.component';
|
||||||
|
|
||||||
|
describe('SigninComponent',
|
||||||
|
() => {
|
||||||
|
let component: SigninComponent;
|
||||||
|
let fixture: ComponentFixture<SigninComponent>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SigninComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SigninComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// angular
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
// libs
|
||||||
|
import { SocialAuthService } from '@abacritt/angularx-social-login';
|
||||||
|
|
||||||
|
// app
|
||||||
|
import { AuthService } from 'src/app/core/service/auth.service';
|
||||||
|
import { UnsubscribeOnDestroyAdapter } from 'src/app/shared/UnsubscribeOnDestroyAdapter';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-signin',
|
||||||
|
templateUrl: './signin.component.html',
|
||||||
|
styleUrls: ['./signin.component.scss'],
|
||||||
|
})
|
||||||
|
export class SigninComponent extends UnsubscribeOnDestroyAdapter
|
||||||
|
implements OnInit {
|
||||||
|
authForm!: UntypedFormGroup;
|
||||||
|
submitted = false;
|
||||||
|
loading = false;
|
||||||
|
error?= '';
|
||||||
|
hide = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: UntypedFormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private authService: AuthService,
|
||||||
|
//private externalAuthService: SocialAuthService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.authForm = this.formBuilder.group({
|
||||||
|
username: ['', Validators.required],
|
||||||
|
password: ['', Validators.required],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.authService.isAuthenticated$.subscribe(isAuthenticated => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
this.router.navigate([this.getRedirect() || '/dashboard/dashboard1']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.authForm.invalid) {
|
||||||
|
this.error = 'Username and Password not valid!';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitted = true;
|
||||||
|
this.loading = true;
|
||||||
|
this.error = '';
|
||||||
|
|
||||||
|
var user = {
|
||||||
|
username: this.authForm.controls['username'].value,
|
||||||
|
password: this.authForm.controls['password'].value
|
||||||
|
};
|
||||||
|
|
||||||
|
this.subs.sink = this.authService
|
||||||
|
.login(user)
|
||||||
|
.subscribe({
|
||||||
|
next: (resp) => {
|
||||||
|
if (!resp.success) {
|
||||||
|
this.error = 'Invalid Login';
|
||||||
|
if (!resp.success && resp.error?.code === 'invalid_grant') {
|
||||||
|
this.error = 'Invalid username or password';
|
||||||
|
}
|
||||||
|
this.submitted = false;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.error = 'Invalid username or password';
|
||||||
|
this.submitted = false;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loginAuth0() {
|
||||||
|
this.authService.loginAuth0().subscribe(resp => {
|
||||||
|
if (!resp.success) {
|
||||||
|
this.error = 'Invalid login';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRedirect(): string | undefined {
|
||||||
|
var redirectUrl = this.route.snapshot.queryParams['r'];
|
||||||
|
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||||
|
return redirectUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<div class="auth-container">
|
||||||
|
<div class="row auth-main">
|
||||||
|
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||||
|
<div class="left-img" style="background-image: url(assets/images/pages/bg-02.png);">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 auth-form-section">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<h2 class="welcome-msg"> Sign Up </h2>
|
||||||
|
<p class="auth-signup-text text-muted">Enter details to create your account</p>
|
||||||
|
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput formControlName="email" required autocomplete="off">
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>mail</mat-icon>
|
||||||
|
<mat-error *ngIf="authForm.get('email')?.hasError('required') || authForm.get('email')?.touched">
|
||||||
|
Please enter a valid email address
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Password</mat-label>
|
||||||
|
<input matInput formControlName="password" [type]="hide ? 'password' : 'text'" required autocomplete="off">
|
||||||
|
<a href="#" onClick="return false;" matSuffix (click)="hide = !hide"
|
||||||
|
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||||
|
{{hide ? 'visibility_off' : 'visibility'}}
|
||||||
|
</mat-icon>
|
||||||
|
</a>
|
||||||
|
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||||
|
Password is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||||
|
<mat-form-field class="example-full-width" appearance="outline">
|
||||||
|
<mat-label>Confirm Password</mat-label>
|
||||||
|
<input matInput formControlName="cpassword" [type]="chide ? 'password' : 'text'" required autocomplete="off">
|
||||||
|
<a href="#" onClick="return false;" matSuffix (click)="chide = !chide"
|
||||||
|
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="chide">
|
||||||
|
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||||
|
{{hide ? 'visibility_off' : 'visibility'}}
|
||||||
|
</mat-icon>
|
||||||
|
</a>
|
||||||
|
<mat-error *ngIf="authForm.get('cpassword')?.hasError('required')">
|
||||||
|
Confirm Password is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-sb-m w-full p-b-20" *ngIf="generalError">
|
||||||
|
<div *ngFor="let error of generalError.errors" class="alert alert-danger">
|
||||||
|
{{error.description}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-sb-m w-full p-b-20">
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
Already Registered?
|
||||||
|
<a routerLink="/authentication/signin">
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-auth-form-btn">
|
||||||
|
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<h6 class="social-login-title">OR</h6>
|
||||||
|
<ul class="list-unstyled social-icon mb-0 mt-3">
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded" (click)="loginAuth0()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 135 150">
|
||||||
|
<title>auth0-glyph</title>
|
||||||
|
<path d="M131.225 46.364L116.136 0H67.4l15.06 46.364h48.764zM67.4 0H18.664L3.602 46.364H52.34L67.399 0zM3.602 46.364c-8.982 27.639.856 57.918 24.368 75L43.032 75 3.602 46.364zm127.623 0L91.795 75l15.062 46.364c23.512-17.082 33.35-47.361 24.368-75zm-103.255 75L67.4 150l39.43-28.636L67.4 92.727l-39.43 28.637z" fill="#3c4858"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<!--
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded">
|
||||||
|
<i class="fab fa-google"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded flex-c-m">
|
||||||
|
<i class="fab fa-facebook-f"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="javascript:void(0)" class="rounded">
|
||||||
|
<i class="fab fa-linkedin-in"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
-->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { SignupComponent } from './signup.component';
|
||||||
|
|
||||||
|
describe('SignupComponent',
|
||||||
|
() => {
|
||||||
|
let component: SignupComponent;
|
||||||
|
let fixture: ComponentFixture<SignupComponent>;
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SignupComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SignupComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create',
|
||||||
|
() => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
import { GeneralErrorModel } from '../../core/models/general-error.model';
|
||||||
|
import { AuthService } from '../../core/service/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-signup',
|
||||||
|
templateUrl: './signup.component.html',
|
||||||
|
styleUrls: ['./signup.component.scss'],
|
||||||
|
})
|
||||||
|
export class SignupComponent implements OnInit {
|
||||||
|
authForm!: UntypedFormGroup;
|
||||||
|
submitted = false;
|
||||||
|
returnUrl!: string;
|
||||||
|
hide = true;
|
||||||
|
chide = true;
|
||||||
|
generalError?: GeneralErrorModel;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: UntypedFormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private authService: AuthService
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.authForm = this.formBuilder.group({
|
||||||
|
email: ['', [Validators.required, Validators.email, Validators.minLength(5)]],
|
||||||
|
password: ['', Validators.required],
|
||||||
|
cpassword: ['', Validators.required],
|
||||||
|
});
|
||||||
|
// get return url from route parameters or default to '/'
|
||||||
|
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
get f() {
|
||||||
|
return this.authForm.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
this.submitted = true;
|
||||||
|
// stop here if form is invalid
|
||||||
|
if (this.authForm.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//this.router.navigate(['/admin/dashboard/main']);
|
||||||
|
this.authService.register({
|
||||||
|
username: this.authForm.get('email')!.value!,
|
||||||
|
password: this.authForm.get('password')!.value!,
|
||||||
|
confirmPassword: this.authForm.get('cpassword')!.value!,
|
||||||
|
}).subscribe(
|
||||||
|
resp => {
|
||||||
|
if (!resp.succeeded) {
|
||||||
|
this.generalError = resp as GeneralErrorModel;
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['authentication/signin']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
this.generalError = err.error as GeneralErrorModel;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginAuth0() {
|
||||||
|
this.authService.loginAuth0().subscribe(x => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { SocialAuthServiceConfig } from '@abacritt/angularx-social-login';
|
||||||
|
import { GoogleLoginProvider } from '@abacritt/angularx-social-login';
|
||||||
|
import { FacebookLoginProvider } from '@abacritt/angularx-social-login';
|
||||||
|
import { MicrosoftLoginProvider } from '@abacritt/angularx-social-login';
|
||||||
|
|
||||||
|
import { environment } from '../environments/environment';
|
||||||
|
|
||||||
|
export class ExternalLoginConfig {
|
||||||
|
|
||||||
|
static readonly GOOGLE = 'google';
|
||||||
|
static readonly AUTH0 = 'auth0';
|
||||||
|
static readonly FACEBOOK = 'facebook';
|
||||||
|
static readonly MICROSOFT = 'microsoft';
|
||||||
|
|
||||||
|
static getConfiguredProviders() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
provider: ExternalLoginConfig.GOOGLE,
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: ExternalLoginConfig.AUTH0,
|
||||||
|
name: 'Auth0',
|
||||||
|
},
|
||||||
|
/*{
|
||||||
|
provider: ExternalLoginConfig.FACEBOOK,
|
||||||
|
name: "FaceBook",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: ExternalLoginConfig.MICROSOFT,
|
||||||
|
name: "Microsoft",
|
||||||
|
},*/
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSocialConfig(): SocialAuthServiceConfig {
|
||||||
|
var providers = [];
|
||||||
|
|
||||||
|
if (environment.externalLogins &&
|
||||||
|
environment.externalLogins.google &&
|
||||||
|
environment.externalLogins.google.clientId
|
||||||
|
) {
|
||||||
|
providers.push({
|
||||||
|
id: GoogleLoginProvider.PROVIDER_ID,
|
||||||
|
provider: new GoogleLoginProvider(
|
||||||
|
environment.externalLogins.google.clientId,
|
||||||
|
{ scopes: 'email', }
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
autoLogin: false,
|
||||||
|
providers: providers,
|
||||||
|
onError: (err) => {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAuth0Config() {
|
||||||
|
return {
|
||||||
|
clientId: environment.externalLogins.auth0.clientId,
|
||||||
|
domain: environment.externalLogins.auth0.domain,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||||
|
import { OAuthModuleConfig } from 'angular-oauth2-oidc';
|
||||||
|
|
||||||
|
import { environment } from '../environments/environment';
|
||||||
|
|
||||||
|
export const AuthCodeFlowConfig: AuthConfig = {
|
||||||
|
// Url of the Identity Provider
|
||||||
|
issuer: environment.identityServer,
|
||||||
|
requireHttps: false,
|
||||||
|
strictDiscoveryDocumentValidation: false,
|
||||||
|
|
||||||
|
// URL of the SPA to redirect the user to after login
|
||||||
|
redirectUri: window.location.origin + '/',
|
||||||
|
silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
|
||||||
|
useSilentRefresh: true,
|
||||||
|
silentRefreshTimeout: 5000, // For faster testing
|
||||||
|
timeoutFactor: 0.25, // For faster testing
|
||||||
|
sessionChecksEnabled: true,
|
||||||
|
|
||||||
|
// The SPA's id. The SPA is registerd with this id at the auth-server
|
||||||
|
// clientId: 'server.code',
|
||||||
|
clientId: 'angulartemplate_spa',
|
||||||
|
|
||||||
|
// Just needed if your auth server demands a secret. In general, this
|
||||||
|
// is a sign that the auth server is not configured with SPAs in mind
|
||||||
|
// and it might not enforce further best practices vital for security
|
||||||
|
// such applications.
|
||||||
|
// dummyClientSecret: 'secret',
|
||||||
|
|
||||||
|
responseType: 'code',
|
||||||
|
|
||||||
|
// set the scope for the permissions the client should request
|
||||||
|
// The first four are defined by OIDC.
|
||||||
|
// Important: Request offline_access to get a refresh token
|
||||||
|
// The api scope is a usecase specific one
|
||||||
|
scope: 'openid profile email api',
|
||||||
|
|
||||||
|
showDebugInformation: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AuthModuleConfig: OAuthModuleConfig = {
|
||||||
|
resourceServer: {
|
||||||
|
allowedUrls: environment.allowedUrls,
|
||||||
|
sendAccessToken: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { InConfiguration } from '../core/models/config.interface';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ConfigService {
|
||||||
|
configData!: InConfiguration;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.setConfigData();
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfigData() {
|
||||||
|
this.configData = {
|
||||||
|
layout: {
|
||||||
|
rtl: false, // options: true & false
|
||||||
|
variant: 'light', // options: light & dark
|
||||||
|
theme_color: 'white', // options: white, black, purple, blue, cyan, green, orange
|
||||||
|
logo_bg_color: 'white', // options: white, black, purple, blue, cyan, green, orange
|
||||||
|
sidebar: {
|
||||||
|
collapsed: false, // options: true & false
|
||||||
|
backgroundColor: 'light', // options: light & dark
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// angular
|
||||||
|
import { NgModule, Optional, SkipSelf, CUSTOM_ELEMENTS_SCHEMA, APP_INITIALIZER } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
// libs
|
||||||
|
import { OAuthModule, OAuthModuleConfig, OAuthStorage } from 'angular-oauth2-oidc';
|
||||||
|
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||||
|
import { AuthModule } from '@auth0/auth0-angular';
|
||||||
|
import { SocialLoginModule } from '@abacritt/angularx-social-login';
|
||||||
|
|
||||||
|
// app
|
||||||
|
import { AuthGuard } from './guard/auth.guard';
|
||||||
|
import { RightSidebarService } from './service/rightsidebar.service';
|
||||||
|
import { AuthService, authAppInitializerFactory } from './service/auth.service';
|
||||||
|
import { DirectionService } from './service/direction.service';
|
||||||
|
import { throwIfAlreadyLoaded } from './guard/module-import.guard';
|
||||||
|
import { OidcHelperService } from './service/oidc-helper.service';
|
||||||
|
import { AuthCodeFlowConfig, AuthModuleConfig } from '../config.oidc';
|
||||||
|
import { SubjectExtensions } from './extensions/general.extensions';
|
||||||
|
import { ExternalLoginConfig } from '../config.external-login';
|
||||||
|
|
||||||
|
export function storageFactory(): OAuthStorage {
|
||||||
|
return localStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SocialLoginModule,
|
||||||
|
OAuthModule.forRoot(),
|
||||||
|
AuthModule.forRoot(ExternalLoginConfig.getAuth0Config()),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: APP_INITIALIZER, useFactory: authAppInitializerFactory, deps: [AuthService], multi: true },
|
||||||
|
{ provide: AuthConfig, useValue: AuthCodeFlowConfig },
|
||||||
|
{ provide: OAuthModuleConfig, useValue: AuthModuleConfig },
|
||||||
|
{ provide: OAuthStorage, useFactory: storageFactory },
|
||||||
|
{ provide: 'SocialAuthServiceConfig', useValue: ExternalLoginConfig.getSocialConfig() },
|
||||||
|
RightSidebarService,
|
||||||
|
AuthGuard,
|
||||||
|
AuthService,
|
||||||
|
DirectionService,
|
||||||
|
OidcHelperService,
|
||||||
|
SubjectExtensions,
|
||||||
|
],
|
||||||
|
schemas: [
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CoreModule {
|
||||||
|
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
|
||||||
|
throwIfAlreadyLoaded(parentModule, 'CoreModule');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// angular
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
// libs
|
||||||
|
import { BehaviorSubject, Observable, Subject, of, throwError, from, combineLatest } from 'rxjs';
|
||||||
|
|
||||||
|
export { }
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface RouterExtensions {
|
||||||
|
addDays(days: number): Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExActivatedRoute extends ActivatedRoute {
|
||||||
|
|
||||||
|
getCurrentRoute(): string {
|
||||||
|
var redirectUrl = this.snapshot.queryParams['r'];
|
||||||
|
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||||
|
return redirectUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Action<T> {
|
||||||
|
(item: T): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Func<T, TResult> {
|
||||||
|
(item: T): TResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SubjectExtensions {
|
||||||
|
|
||||||
|
static start<T>(start: Action<Subject<T>>): Subject<T> {
|
||||||
|
var result = new Subject<T>();
|
||||||
|
|
||||||
|
start(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
export { }
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface RouterExtensions {
|
||||||
|
addDays(days: number): Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExActivatedRoute extends ActivatedRoute {
|
||||||
|
|
||||||
|
getCurrentRoute(): string {
|
||||||
|
var redirectUrl = this.snapshot.queryParams['r'];
|
||||||
|
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||||
|
return redirectUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
import { AuthService } from '../service/auth.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private authService: AuthService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||||
|
if (this.authService.isAuthenticated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.router.navigate(['/authentication/signin'], { queryParams: { r: encodeURIComponent(state.url) } });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { CoreModule } from '../core.module';
|
||||||
|
|
||||||
|
export function throwIfAlreadyLoaded(
|
||||||
|
parentModule: CoreModule,
|
||||||
|
moduleName: string
|
||||||
|
) {
|
||||||
|
if (parentModule) {
|
||||||
|
throw new Error(
|
||||||
|
`${moduleName} has already been loaded. Import ${moduleName} modules in the AppModule only.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { AuthService } from '../service/auth.service';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ErrorInterceptor implements HttpInterceptor {
|
||||||
|
constructor(private authenticationService: AuthService) {}
|
||||||
|
|
||||||
|
intercept(
|
||||||
|
request: HttpRequest<any>,
|
||||||
|
next: HttpHandler
|
||||||
|
): Observable<HttpEvent<any>> {
|
||||||
|
return next.handle(request).pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
if (err.status === 401) {
|
||||||
|
// auto logout if 401 response returned from api
|
||||||
|
this.authenticationService.logout();
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = err.error.message || err.statusText;
|
||||||
|
//return throwError(error);
|
||||||
|
return throwError(err.error || err);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpResponse,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor,
|
||||||
|
HTTP_INTERCEPTORS,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable, of, throwError } from 'rxjs';
|
||||||
|
import { mergeMap } from 'rxjs/operators';
|
||||||
|
import { UserModel } from '../models/user.model';
|
||||||
|
|
||||||
|
const users: UserModel[] = [
|
||||||
|
{
|
||||||
|
id: 'fake-user',
|
||||||
|
img: 'assets/images/user/admin.jpg',
|
||||||
|
username: 'admin@software.com',
|
||||||
|
//password: 'admin@123',
|
||||||
|
firstName: 'Sarah',
|
||||||
|
lastName: 'Smith',
|
||||||
|
token: 'admin-token',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FakeBackendInterceptor implements HttpInterceptor {
|
||||||
|
intercept(
|
||||||
|
request: HttpRequest<any>,
|
||||||
|
next: HttpHandler
|
||||||
|
): Observable<HttpEvent<any>> {
|
||||||
|
const { url, method, headers, body } = request;
|
||||||
|
// wrap in delayed observable to simulate server api call
|
||||||
|
return of(null).pipe(mergeMap(handleRoute));
|
||||||
|
|
||||||
|
function handleRoute() {
|
||||||
|
switch (true) {
|
||||||
|
case url.endsWith('/authenticate') && method === 'POST':
|
||||||
|
return authenticate();
|
||||||
|
default:
|
||||||
|
// pass through any requests not handled above
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// route functions
|
||||||
|
|
||||||
|
function authenticate() {
|
||||||
|
const { username, password } = body;
|
||||||
|
const user = users.find(
|
||||||
|
(x) => x.username === username && x.password === password
|
||||||
|
);
|
||||||
|
if (!user) {
|
||||||
|
return error('Username or password is incorrect');
|
||||||
|
}
|
||||||
|
return ok({
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
img: user.img,
|
||||||
|
firstName: user.firstName,
|
||||||
|
lastName: user.lastName,
|
||||||
|
token: user.token,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
|
||||||
|
function ok(body?: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
img?: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
token?: string;
|
||||||
|
}) {
|
||||||
|
return of(new HttpResponse({ status: 200, body }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(message: string) {
|
||||||
|
return throwError({ error: { message } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function unauthorized() {
|
||||||
|
return throwError({ status: 401, error: { message: 'Unauthorised' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function isLoggedIn() {
|
||||||
|
return headers.get('Authorization') === 'Bearer fake-jwt-token';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fakeBackendProvider = {
|
||||||
|
// use fake backend in place of Http service for backend-less development
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: FakeBackendInterceptor,
|
||||||
|
multi: true,
|
||||||
|
};
|
||||||
|
*/
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { AuthService } from '../service/auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtInterceptor implements HttpInterceptor {
|
||||||
|
constructor(private authenticationService: AuthService) {}
|
||||||
|
|
||||||
|
intercept(
|
||||||
|
request: HttpRequest<any>,
|
||||||
|
next: HttpHandler
|
||||||
|
): Observable<HttpEvent<any>> {
|
||||||
|
if (this.authenticationService.isAuthenticated) {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Bearer ${this.authenticationService.getAccessToken()}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export interface InConfiguration {
|
||||||
|
layout: {
|
||||||
|
rtl: boolean;
|
||||||
|
variant: string;
|
||||||
|
theme_color: string;
|
||||||
|
logo_bg_color: string;
|
||||||
|
sidebar: {
|
||||||
|
collapsed: boolean;
|
||||||
|
backgroundColor: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface GeneralErrorModel {
|
||||||
|
//succeeded?: boolean;
|
||||||
|
code?: string;
|
||||||
|
description?: string;
|
||||||
|
errors?: GeneralErrorModel[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { GeneralErrorModel } from './general-error.model';
|
||||||
|
|
||||||
|
export interface GeneralResultModel {
|
||||||
|
success: boolean;
|
||||||
|
error?: GeneralErrorModel;
|
||||||
|
data?: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface LoginModel {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface RegisterModel {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
confirmPassword: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export interface UserProfileModel {
|
||||||
|
id: string;
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
fullName?: string;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user