put in signal for edit
This commit is contained in:
@@ -12,9 +12,9 @@ WORKDIR "/src/FamilyTreeAPI"
|
||||
RUN dotnet build "./FamilyTreeAPI.csproj" -c Release -o /app/build
|
||||
|
||||
# create directory for import folder, and image folder
|
||||
RUN mkdir /app_import
|
||||
RUN mkdir app_import
|
||||
|
||||
RUN mkdir /app_images
|
||||
RUN mkdir app_images
|
||||
|
||||
VOLUME /app_import
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApplicationConfig, provideAppInitializer, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
||||
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { providePrimeNG } from 'primeng/config';
|
||||
@@ -27,7 +27,7 @@ export const appConfig: ApplicationConfig = {
|
||||
}
|
||||
}
|
||||
}),
|
||||
provideRouter(routes)
|
||||
provideRouter(routes, withComponentInputBinding())
|
||||
]
|
||||
};
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Subject, Subscription} from 'rxjs';
|
||||
@@ -23,9 +23,15 @@ export class StaffEditComponent implements OnInit, OnDestroy {
|
||||
loginUser ='';
|
||||
_error ='';
|
||||
_id= -1;
|
||||
Roles: Code[];
|
||||
Roles: Code[] = [];
|
||||
id = input(0, { transform: numberAttribute });
|
||||
isNew = false;
|
||||
validationPoints?: Code[];
|
||||
private staffService = inject(StaffService);
|
||||
private messageService = inject(MessageService);
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private lookupService = inject(LookupService);
|
||||
msg="[adminUser Component] ";
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
//for focus input
|
||||
@@ -46,12 +52,11 @@ export class StaffEditComponent implements OnInit, OnDestroy {
|
||||
|
||||
});
|
||||
constructor(
|
||||
private staffService: StaffService,
|
||||
private messageService: MessageService,
|
||||
private lookupService: LookupService,
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
) {
|
||||
|
||||
this.fillRole();
|
||||
}
|
||||
fillRole() :void
|
||||
{
|
||||
this.Roles = [];
|
||||
let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
|
||||
this.Roles.push(item);
|
||||
@@ -61,11 +66,9 @@ constructor(
|
||||
this.Roles.push(item);
|
||||
item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
|
||||
this.Roles.push(item);
|
||||
|
||||
|
||||
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
|
||||
//this.Roles.push(item);
|
||||
}
|
||||
}
|
||||
getClassForRequire(prev:string,name:string){
|
||||
const notok = !this.adminuserForm.controls[name].valid &&
|
||||
this.adminuserForm.controls[name].touched;
|
||||
@@ -76,7 +79,7 @@ getClassForRequire(prev:string,name:string){
|
||||
}
|
||||
ngOnInit():void {
|
||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
const id = Number(this.route.snapshot.paramMap.get('id'));
|
||||
const id = this.id(); // Number(this.route.snapshot.paramMap.get('id'));
|
||||
// now load thing up
|
||||
const user = Utils.getCurrentUser();
|
||||
// console.log(this.msg + "current login user ", user);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
@@ -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
|
||||
@@ -0,0 +1,363 @@
|
||||
## 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/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34202.233
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FamilyTreeAPI", "FamilyTreeAPI\FamilyTreeAPI.csproj", "{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}"
|
||||
EndProject
|
||||
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{81DDED9D-158B-E303-5F62-77A2896D2A5A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81DDED9D-158B-E303-5F62-77A2896D2A5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81DDED9D-158B-E303-5F62-77A2896D2A5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81DDED9D-158B-E303-5F62-77A2896D2A5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81DDED9D-158B-E303-5F62-77A2896D2A5A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C7DF2338-DFC8-47CE-9AA7-C05C3C2ABEF7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "9.0.8",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace FamilyTreeAPI.Authorization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class AllowAnonymousAttribute : Attribute
|
||||
{ }
|
||||
@@ -0,0 +1,36 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace FamilyTreeAPI.Authorization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
|
||||
{
|
||||
// private readonly IList<AdminRole> _roles;
|
||||
/*
|
||||
public AuthorizeAttribute(params AdminRole[] roles)
|
||||
{
|
||||
_roles = roles ?? new AdminRole[] { };
|
||||
}
|
||||
*/
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
// skip authorization if action is decorated with [AllowAnonymous] attribute
|
||||
var allowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();
|
||||
if (allowAnonymous)
|
||||
return;
|
||||
|
||||
// authorization
|
||||
// var user = (User)context.HttpContext.Items["User"];
|
||||
// if (user == null || (_roles.Any() && !_roles.Contains(user.Role)))
|
||||
// {
|
||||
// not logged in or role not authorized
|
||||
// context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Authorization;
|
||||
|
||||
public class JwtMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_next = next;
|
||||
_appSettings = appSettings.Value;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context, IJwtUtils jwtUtils)
|
||||
{
|
||||
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||
if (token != null)
|
||||
{
|
||||
var user = jwtUtils.ValidateJwtToken(token);
|
||||
if (user != null)
|
||||
{
|
||||
// attach user to context on successful jwt validation
|
||||
//here to put in the real user
|
||||
//TODO if you want to add information for User
|
||||
context.Items["User"] = user;
|
||||
}
|
||||
}
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
|
||||
namespace FamilyTreeAPI.Authorization;
|
||||
|
||||
|
||||
public class JwtUtils : IJwtUtils
|
||||
{
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public JwtUtils(IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_appSettings = appSettings.Value;
|
||||
}
|
||||
|
||||
public string GenerateJwtToken(UserDto user)
|
||||
{
|
||||
// generate token that is valid for 2 days
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(new[] {
|
||||
new Claim("id", user.Id.ToString()),
|
||||
new Claim("subject", user.Username),
|
||||
new Claim("firstName", user.FirstName + " " + user.LastName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//add more if we need it
|
||||
}
|
||||
),
|
||||
Expires = DateTime.UtcNow.AddDays(2),
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
// get back id and username. in subject
|
||||
public UserDto? ValidateJwtToken(string token)
|
||||
{
|
||||
if (token == null)
|
||||
return null;
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||
try
|
||||
{
|
||||
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
|
||||
ClockSkew = TimeSpan.Zero
|
||||
}, out SecurityToken validatedToken);
|
||||
|
||||
var jwtToken = (JwtSecurityToken)validatedToken;
|
||||
// var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value); //old
|
||||
int userId = 0;
|
||||
string username = "";
|
||||
string firstName = "";
|
||||
Claim claim;
|
||||
for (int i = 0; i< jwtToken.Claims.Count(); i++)
|
||||
{
|
||||
claim = jwtToken.Claims.ElementAt(i);
|
||||
if (claim.Type == "id")
|
||||
{
|
||||
userId = int.Parse(claim.Value);
|
||||
}
|
||||
else if (claim.Type == "subject")
|
||||
{
|
||||
username = claim.Value;
|
||||
}
|
||||
else if (claim.Type == "firstName")
|
||||
{
|
||||
firstName = claim.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(username)
|
||||
&& !string.IsNullOrEmpty(firstName)
|
||||
&& userId > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UserDto user = new UserDto();
|
||||
user.Id = userId;
|
||||
user.Username = username;
|
||||
user.FirstName = firstName;
|
||||
|
||||
// return user id from JWT token if validation successful
|
||||
return user;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// return null if validation fails
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Repository;
|
||||
|
||||
namespace FamilyTreeAPI.Controllers;
|
||||
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class FileUploadController : ControllerBase
|
||||
{
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly ImportPersonRepository _importPersonRepository;
|
||||
private readonly IConfiguration _config;
|
||||
public FileUploadController(IWebHostEnvironment hostingEnvironment, ImportPersonRepository importPersonRepository, IConfiguration config)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_importPersonRepository = importPersonRepository;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> UploadFile(IFormFile file)
|
||||
{
|
||||
List<CodeDto<string>> output = new ();
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return BadRequest("No file uploaded.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Define the upload directory
|
||||
// var uploadsFolder = Path.Combine(_hostingEnvironment.WebRootPath, "uploads");
|
||||
string importFolder = _config.GetValue<string>("AppSettings:ImportFolder");
|
||||
var uploadsFolder = Path.Combine(_hostingEnvironment.ContentRootPath, importFolder);
|
||||
if (!Directory.Exists(uploadsFolder))
|
||||
{
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
}
|
||||
|
||||
// Create a unique file name to avoid overwriting
|
||||
var uniqueFileName = Path.GetFileNameWithoutExtension(file.FileName)
|
||||
+ "_" + System.Guid.NewGuid().ToString()
|
||||
+ Path.GetExtension(file.FileName);
|
||||
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
|
||||
|
||||
// Save the file to the server
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
// await file.CopyToAsync(stream);
|
||||
await file.CopyToAsync(stream);
|
||||
// output = await _importPersonRepository.ImportPerson(stream, "Sheet1");
|
||||
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
output = await _importPersonRepository.ImportPerson(stream, "Sheet1");
|
||||
}
|
||||
|
||||
//return Ok(new { FileName = uniqueFileName, FilePath = filePath });
|
||||
return Ok(output);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Internal server error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> DownloadFile(DownloadFileCriteria criteria)
|
||||
{
|
||||
var rev = await _importPersonRepository.DownloadFile(criteria);
|
||||
return Ok(rev);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace FamilyTreeAPI.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class LookupController : ControllerBase
|
||||
{
|
||||
private readonly ILookup _repo;
|
||||
public LookupController(ILookup repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
[HttpGet("[action]")]
|
||||
public async Task<IActionResult> LoadLookup(string type)
|
||||
{
|
||||
var list = await _repo.GetLookupAsync(type);
|
||||
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]")]
|
||||
public async Task<IActionResult> LoadLookupEdit(string type)
|
||||
{
|
||||
var list = await _repo.GetLookupEditAsync(type);
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]")]
|
||||
public async Task<IActionResult> GetPersons()
|
||||
{
|
||||
var list = await _repo.GetPersonsAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]")]
|
||||
public async Task<IActionResult> GetStaffs()
|
||||
{
|
||||
var list = await _repo.GetStaffAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Lookup([FromBody] LookupEditDto model)
|
||||
{
|
||||
//var currentUser = (User?)(HttpContext.Items["User"]);
|
||||
//if (null == currentUser)
|
||||
// return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var response = await _repo.SaveLookupAsync(model);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Lookup(int id, string type)
|
||||
{
|
||||
//lookup/id?type='abc'
|
||||
/*
|
||||
// only admins can access other user records
|
||||
var currentUser = (User)HttpContext.Items["User"];
|
||||
if (id != currentUser.Id && currentUser.Role != Role.Admin)
|
||||
return Unauthorized(new { message = "Unauthorized" });
|
||||
*/
|
||||
var retval = await _repo.GetLookupEditByIdAsync(id, type);
|
||||
return Ok(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace FamilyTreeAPI.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class PersonController : ControllerBase
|
||||
{
|
||||
private readonly IPerson _repo;
|
||||
public PersonController(IPerson repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> UploadImage(IFormFile file)
|
||||
{
|
||||
var keys = Request.Form;
|
||||
var files = Request.Form.Files;
|
||||
ResultModel<string> ret = new();
|
||||
if (files.Count > 0)
|
||||
{
|
||||
StringValues familyId = "";
|
||||
|
||||
keys.TryGetValue("familyId", out familyId);
|
||||
|
||||
UploadCriteria criteria = new();
|
||||
criteria.File = file;
|
||||
criteria.FamilyId = familyId;
|
||||
|
||||
criteria.FileName = file.FileName;
|
||||
|
||||
ret = await _repo.UploadImage(criteria);
|
||||
}
|
||||
return Ok(ret);
|
||||
|
||||
|
||||
}
|
||||
//DeleteUploadFile
|
||||
[HttpPost("[action]")]
|
||||
public ActionResult DeleteUploadFile(DeleteFileCriteria criteria)
|
||||
{
|
||||
|
||||
ResultModel<int> ret = new();
|
||||
if (!string.IsNullOrEmpty(criteria.Filename))
|
||||
{
|
||||
|
||||
ret = _repo.DeleteUploadFile(criteria);
|
||||
}
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> SearchPerson(PersonCriteria criteria)
|
||||
{
|
||||
var list = await _repo.GetPerson(criteria);
|
||||
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> GetChildress(ChildCriteria criteria)
|
||||
{
|
||||
var list = await _repo.GetChildren(criteria);
|
||||
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> GetFamilyTreeBy(FamilyCriteria criteria)
|
||||
{
|
||||
var list = await _repo.GetFamilyTreeBy(criteria);
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]/{id}")]
|
||||
public async Task<IActionResult> GetByPersonFamily(int id)
|
||||
{
|
||||
var list = await _repo.GetByFamilyAsync(id);
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]/{id}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var list = await _repo.GetByIdAsync(id);
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> DeleteById(DeleteCriteria<int> criteria)
|
||||
{
|
||||
var list = await _repo.DeleteAsync(criteria.Id);
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> SavePerson([FromBody] PersonForSave model)
|
||||
{
|
||||
//var currentUser = (User?)(HttpContext.Items["User"]);
|
||||
//if (null == currentUser)
|
||||
// return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var response = await _repo.SaveAsync(model);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace FamilyTreeAPI.Controllers;
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class RelationShipController : ControllerBase
|
||||
{
|
||||
private readonly IRelationShipd _repo;
|
||||
public RelationShipController(IRelationShipd repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
[HttpGet("[action]/{id}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var list = await _repo.GetByIdAsync(id);
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpGet("[action]/{id}")]
|
||||
public async Task<IActionResult> GetByPersonId(int id)
|
||||
{
|
||||
var list = await _repo.GetByPersonIdAsync(id);
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpPost("[action]/{id}")]
|
||||
public async Task<IActionResult> DeleteById(int id)
|
||||
{
|
||||
var list = await _repo.GetByIdAsync(id);
|
||||
return Ok(list);
|
||||
}
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> SaveRelationShip([FromBody] RelationShiftContainer list)
|
||||
{
|
||||
//var currentUser = (User?)(HttpContext.Items["User"]);
|
||||
//if (null == currentUser)
|
||||
// return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var response = await _repo.SaveAsync(list.relationShips);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FamilyTreeAPI.Controllers;
|
||||
|
||||
// [Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class StaffController : ControllerBase
|
||||
{
|
||||
private readonly IStaff _staff;
|
||||
public StaffController(IStaff staff)
|
||||
{
|
||||
_staff = staff;
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> SaveStaff([FromBody] StaffDto model)
|
||||
{
|
||||
//var currentUser = (User?)HttpContext.Items["User"];
|
||||
//if (null == currentUser)
|
||||
// return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var response = await _staff.SaveStaff(model);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> ResetPassStaff([FromBody] ResetPassDto model)
|
||||
{
|
||||
//var currentUser = (User?)HttpContext.Items["User"];
|
||||
//if (null == currentUser)
|
||||
// return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var response = await _staff.ResetPassword(model);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> SearchStaff([FromBody] StaffCriteria criteria)
|
||||
{
|
||||
|
||||
var retval = await _staff.GetStaff(criteria);
|
||||
return Ok(retval);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> Staff( int id)
|
||||
{
|
||||
/*
|
||||
// only admins can access other user records
|
||||
var currentUser = (User)HttpContext.Items["User"];
|
||||
if (id != currentUser.Id && currentUser.Role != Role.Admin)
|
||||
return Unauthorized(new { message = "Unauthorized" });
|
||||
*/
|
||||
var retval = await _staff.GetStaffById(id);
|
||||
return Ok(retval);
|
||||
}
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> DeleteStaff(DeleteCriteria<int> criteria)
|
||||
{
|
||||
var currentUser = (UserDto?)HttpContext.Items["User"];
|
||||
if (null == currentUser)
|
||||
return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
var retval = await _staff.Delete(criteria.Id);
|
||||
return Ok(retval);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using FamilyTreeAPI.Interface;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FamilyTreeAPI.Entities;
|
||||
namespace FamilyTreeAPI.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public UsersController(IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> Login([FromBody] AuthenticateRequest model)
|
||||
{
|
||||
string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
|
||||
var response = await _userService.Login(model);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
//[AllowAnonymous]
|
||||
//[HttpPost("[action]")]
|
||||
//public async Task<IActionResult> LoginAD(AuthenticateRequest model)
|
||||
//{
|
||||
// var response = await _userService.LoginAD(model);
|
||||
// return Ok(response);
|
||||
//}
|
||||
[AllowAnonymous]
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> LoginApiAD([FromBody] AuthenticateRequest model)
|
||||
{
|
||||
string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
|
||||
var response = await _userService.LoginApiAD(model, remoteIpAddress);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
var currentUser = (UserDto)HttpContext.Items["User"];
|
||||
if (null == currentUser)
|
||||
return Unauthorized(new { message = "Unauthorized" });
|
||||
|
||||
string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||
string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
var response = await _userService.Logout(token, currentUser, remoteIpAddress);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
//[AllowAnonymous]
|
||||
//[HttpGet("[action]")]
|
||||
//public async Task<IActionResult> SearchADStaff(string stafflinkNo)
|
||||
//{
|
||||
// var user = await _userService.SearchADStaff(stafflinkNo);
|
||||
// return Ok(user);
|
||||
//}
|
||||
/*
|
||||
[AllowAnonymous]
|
||||
[HttpGet("[action]")]
|
||||
public async Task<ResultModel<User>> SearchADStaff(string stafflinkNo)
|
||||
{
|
||||
var user = await _userService.SearchADStaff(stafflinkNo);
|
||||
return user;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Data.Common;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
|
||||
namespace FamilyTreeAPI.Models
|
||||
{
|
||||
public partial class FamilyTreeDBContext
|
||||
{
|
||||
private enum EnumStaff
|
||||
{
|
||||
Id =0,
|
||||
Firstname,
|
||||
Lastname,
|
||||
Email,
|
||||
Stype,
|
||||
Active,
|
||||
Role,
|
||||
Password
|
||||
};
|
||||
private enum EnumStaffWork
|
||||
{
|
||||
id =0,
|
||||
staffid,
|
||||
description,
|
||||
startdate,
|
||||
sactive,
|
||||
did,
|
||||
starttime,
|
||||
stoptime,
|
||||
task
|
||||
}
|
||||
private enum EnumServiceTask
|
||||
{
|
||||
id = 0,
|
||||
staffid,
|
||||
sdate,
|
||||
description,
|
||||
code,
|
||||
status,
|
||||
assignTo,
|
||||
clientid,
|
||||
serviceno
|
||||
}
|
||||
|
||||
public async Task<List<StaffDto>> LoadStaffAsync(StaffCriteria criteria)
|
||||
{
|
||||
List<StaffDto> list = new();
|
||||
using (DbConnection connect = this.Database.GetDbConnection())
|
||||
{
|
||||
DbCommand command = connect.CreateCommand();
|
||||
command.CommandType = System.Data.CommandType.Text;
|
||||
command.CommandText = "SELECT * From usp_search_user(:iemail,:ifirstname,:ilastname)";
|
||||
|
||||
var loginParam = command.CreateParameter();
|
||||
|
||||
loginParam.ParameterName = "iemail";
|
||||
loginParam.Value = string.IsNullOrEmpty(criteria.Email) ? "" : criteria.Email;
|
||||
|
||||
var fistnameParam = command.CreateParameter();
|
||||
|
||||
fistnameParam.ParameterName = "ifirstname";
|
||||
fistnameParam.Value = string.IsNullOrEmpty(criteria.FirstName) ? "" : criteria.FirstName;
|
||||
|
||||
var surnameParam = command.CreateParameter() ;
|
||||
|
||||
surnameParam.ParameterName = "ilastname";
|
||||
surnameParam.Value = string.IsNullOrEmpty(criteria.LastName) ? "" : criteria.LastName;
|
||||
|
||||
command.Parameters.Add(loginParam);
|
||||
command.Parameters.Add(fistnameParam);
|
||||
command.Parameters.Add(surnameParam);
|
||||
await connect.OpenAsync();
|
||||
DbDataReader reader = await command.ExecuteReaderAsync();
|
||||
StaffDto staff;
|
||||
int idx = 0;
|
||||
while (reader.Read())
|
||||
{
|
||||
staff = new();
|
||||
idx = (int)EnumStaff.Id;
|
||||
staff.Id = reader.GetFieldValue<int>(idx);
|
||||
idx = (int)EnumStaff.Firstname;
|
||||
staff.Firstname = reader.GetFieldValue<string?>(idx);
|
||||
idx = (int) EnumStaff.Lastname;
|
||||
staff.Lastname = reader.GetFieldValue<string?>(idx);
|
||||
idx = (int)EnumStaff.Email;
|
||||
staff.Email = reader.GetFieldValue<string?>(idx);
|
||||
idx = (int)EnumStaff.Active;
|
||||
staff.Active = reader.GetFieldValue<bool?>(idx);
|
||||
idx = (int)EnumStaff.Role;
|
||||
staff.RoleType = reader.GetFieldValue<int?>(idx);
|
||||
idx = (int)EnumStaff.Password;
|
||||
if (!reader.IsDBNull(idx))
|
||||
staff.Password = reader.GetFieldValue<string?>(idx);
|
||||
|
||||
list.Add(staff);
|
||||
}
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||
#FROM mcr.microsoft.com/dotnet/aspnet:9::.0 AS base
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"]
|
||||
RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/FamilyTreeAPI"
|
||||
RUN dotnet build "./FamilyTreeAPI.csproj" -c Release -o /app/build
|
||||
|
||||
# create directory for import folder, and image folder
|
||||
RUN mkdir app_import
|
||||
|
||||
RUN mkdir app_images
|
||||
|
||||
VOLUME /app_import
|
||||
|
||||
VOLUME /app_images
|
||||
# docker run -v my_path_name_volume_host:/app_import my_image_name
|
||||
# docker run -v my_path_named_volume_host:/app_images my_image_name
|
||||
# docker cp /home/user/my_app/config.json my_container:/app/config.json
|
||||
# docker cp /home/user/my_app/data my_container:/app/data ### copy folder data
|
||||
# docker cp mycontainer:/app/output.txt /home/user/documents/
|
||||
|
||||
# This stage is used to publish the service project to be copied to the final stage
|
||||
FROM build AS publish
|
||||
|
||||
RUN dotnet publish "./FamilyTreeAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
WORKDIR /app
|
||||
ENV ASPNETCORE_HTTP_PORTS=8080
|
||||
EXPOSE 8080
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "FamilyTreeAPI.dll"]
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class AppSettings
|
||||
{
|
||||
public string Secret { get; set; }
|
||||
public string SQLConnectionString { get; set; }
|
||||
public string LoginWebAPI { get; set; }
|
||||
|
||||
public string ClientURL { get; set; }
|
||||
}
|
||||
|
||||
/*
|
||||
entity.HasOne(d => d.Staff)
|
||||
.WithMany(p => p.Staffworks)
|
||||
.HasForeignKey(d => d.Staffid)
|
||||
.HasConstraintName("staffwork_staffid_fkey")
|
||||
.OnDelete(DeleteBehavior.ClientCascade);
|
||||
*/
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class AuthenticateRequest
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
// public string Lhd { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class AuthenticateResponse
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string? Department { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Position { get; set; }
|
||||
public string? ManagerEmail { get; set; }
|
||||
public int Role { get; set; }
|
||||
|
||||
public AuthenticateResponse(UserDto user, string token, int role)
|
||||
{
|
||||
Id = user.Id;
|
||||
FirstName = user.FirstName;
|
||||
LastName = user.LastName;
|
||||
Username = user.Username;
|
||||
Email = user.Email;
|
||||
Department = user.Department;
|
||||
Position = user.Position;
|
||||
ManagerEmail = user.ManagerEmail;
|
||||
Role = role;
|
||||
Token = token;
|
||||
Phone = user.Phone;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class FileContent
|
||||
{
|
||||
public byte[] Content { get; set; }
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
public class DownloadFileCriteria
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
public class UploadCriteria
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string FamilyId { get; set; }
|
||||
|
||||
public IFormFile File { get; set; }
|
||||
}
|
||||
public class DeleteFileCriteria
|
||||
{
|
||||
public int FamilyId { get; set; }
|
||||
public string Filename { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class CodeDto<T>
|
||||
{
|
||||
public T Code { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
//using to store id from file then after insert now the real id from db
|
||||
// the put that in and update the db
|
||||
public class MappingFatherMother
|
||||
{
|
||||
public int TId { get; set; }
|
||||
public int IId { get; set; }
|
||||
public int TFatherId { get; set; }
|
||||
public int TMotherId { get; set; }
|
||||
public int IFatherId { get; set; }
|
||||
public int IMotherId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class LookupDto
|
||||
{
|
||||
public int Id { get; set; } = 0;
|
||||
public string CodeId { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
public class LookupEditDto
|
||||
{
|
||||
public int Id { get; set; } = 0;
|
||||
public string CodeId { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool Active { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class ChildCriteria
|
||||
{
|
||||
public int FatherId { get; set; }
|
||||
public int MotherId { get; set; }
|
||||
|
||||
}
|
||||
public class FamilyCriteria
|
||||
{
|
||||
public bool UseFather { get; set; }
|
||||
public bool UseMother { get; set; }
|
||||
|
||||
}
|
||||
public class PersonCriteria
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
}
|
||||
|
||||
public class PersonForSave
|
||||
{
|
||||
public PersonDto Person { get; set; }
|
||||
public string? FormData { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
|
||||
}
|
||||
public partial class PersonDto
|
||||
{
|
||||
public PersonDto()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Image { get; set; }
|
||||
public string? Sex { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public bool? Alive { get; set; }
|
||||
public string? dob { get; set; }
|
||||
public int? FatherId { get; set; }
|
||||
public int? MotherId { get; set; }
|
||||
|
||||
public List<RelationShipDto>? RelationShips { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class RelationShiftContainer
|
||||
{
|
||||
public int familyId {get; set;}
|
||||
public List<RelationShipDto> relationShips {get; set;}
|
||||
}
|
||||
|
||||
public class ImportRelation
|
||||
{
|
||||
public int FatherId { get; set; }
|
||||
public int MotherId { get; set; }
|
||||
}
|
||||
public class RelationShipDto
|
||||
{
|
||||
public enumState State { get; set; }
|
||||
public int Id { get; set; }
|
||||
|
||||
public int PersonId { get; set; }
|
||||
public int RelatePersonId { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public enum enumState
|
||||
{
|
||||
Delete = -1,
|
||||
NoChange = 0,
|
||||
New = 1,
|
||||
Modify = 2,
|
||||
|
||||
}
|
||||
public class ResultModel<T>
|
||||
{
|
||||
public T Data { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteCriteria<T>
|
||||
{
|
||||
public T Id { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public partial class StaffCriteria
|
||||
{
|
||||
|
||||
public string Email { get; set; } = null!;
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
|
||||
}
|
||||
public partial class StaffDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Phone { get; set; } = null!;
|
||||
public string? Firstname { get; set; }
|
||||
public string? Lastname { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? Email { get; set; }
|
||||
|
||||
public int? Type { get; set; }
|
||||
|
||||
public bool? Active { get; set; }
|
||||
public int? RoleType { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public partial class ResetPassDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Password { get; set; } = null!;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
public class TreeNode<T>
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public T? Data { get; set; }
|
||||
public List<TreeNode<T>>? Children { get; set; }
|
||||
public string? Icon { get; set; }
|
||||
public bool? Checked { get; set; }
|
||||
public bool? Leaf { get; set; }
|
||||
public bool? Expanded { get; set; }
|
||||
public string? Type { get; set; }
|
||||
|
||||
public string StyleClass { get; set; }
|
||||
public string? Key { get; set; }
|
||||
public bool? Loading { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FamilyTreeAPI.Entities;
|
||||
|
||||
/*
|
||||
* ROLE ID
|
||||
* Normal = 1,
|
||||
Admin = 2,
|
||||
Manager =3,
|
||||
*/
|
||||
|
||||
public class UserDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Department { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Position { get; set; }
|
||||
public string? ManagerEmail { get; set; }
|
||||
public int Role { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Models_old\**" />
|
||||
<Content Remove="Models_old\**" />
|
||||
<EmbeddedResource Remove="Models_old\**" />
|
||||
<None Remove="Models_old\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.6" />
|
||||
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.6" />
|
||||
<PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="SpreadsheetLight" Version="3.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using FamilyTreeAPI.Models;
|
||||
|
||||
namespace FamilyTreeAPI.GraphQL.Query
|
||||
{
|
||||
public class QueryFamilyTree
|
||||
{
|
||||
[UsePaging]
|
||||
[UseProjection]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
public IQueryable<Person> GetPersons([Service] FamilyTreeDBContext dBContext) => dBContext.Persons;
|
||||
[UsePaging]
|
||||
[UseProjection]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
public IQueryable<staff> GetStaffs([Service] FamilyTreeDBContext dBContext) => dBContext.staff;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace FamilyTreeAPI.Helper
|
||||
{
|
||||
public class Helpers
|
||||
{
|
||||
public static string? DateToStr(DateTime? date)
|
||||
{
|
||||
string? result = null;
|
||||
if (date.HasValue)
|
||||
{
|
||||
result = date.Value.ToString("yyyy-MM-dd");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static DateTime? DateToDateTime(string? date)
|
||||
{
|
||||
DateTime? result = null;
|
||||
if (!string.IsNullOrEmpty(date))
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTime.TryParse(date, out dt))
|
||||
{
|
||||
result = dt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
The family Tree
|
||||
|
||||
Man and Woman
|
||||
|
||||
top level
|
||||
|
||||
1) TAM (M) has partner Jenny (F)
|
||||
|
||||
had child CAO (M)
|
||||
had Child Emmy (F)
|
||||
has Child Loan (F)
|
||||
|
||||
|
||||
2) CAO (M) has partner Lin (F)
|
||||
|
||||
has child Joe (M)
|
||||
has child TOM (M)
|
||||
|
||||
|
||||
3) Joe has partner Sophia (F)
|
||||
|
||||
has child Olivia (F)
|
||||
has child Mai (F)
|
||||
|
||||
|
||||
4) Henry has partner Loan (F)
|
||||
|
||||
has child Enya (F)
|
||||
has child Thanh (M)
|
||||
has child Chuong (M)
|
||||
|
||||
5) Henry has partner Brisa (F)
|
||||
has child Fern (F)
|
||||
has child Tim (M)
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
1)
|
||||
ubuntu
|
||||
$) sudo bash
|
||||
$) apt-get update
|
||||
$) apt-get install -y dotnet-sdk-9.0
|
||||
$) apt-get install -y aspnetcore-runtime-9.0
|
||||
$) apt-get install -y dotnet-runtime-9.0
|
||||
$) dotnet --info
|
||||
$) apt-get install nginx
|
||||
|
||||
windows
|
||||
net 9 bundle.
|
||||
Windows Hosting Bundle Installer!
|
||||
|
||||
Copy the Files to the Linux Server
|
||||
Next, we need to copy the deployment files to the Ubuntu server. Before we move the files,
|
||||
let’s create the destination folder on the server and set the permissions
|
||||
so that we can add files to it:
|
||||
|
||||
$) cd /var/www
|
||||
$) sudo mkdir app
|
||||
$) sudo chmod 777 app
|
||||
|
||||
Run the App on a Kestrel Web Server
|
||||
In a terminal, navigate to the deployment path and run the app in Kestrel:
|
||||
cd /var/www/app
|
||||
sudo dotnet DeployingToLinuxWithNginx.dll
|
||||
|
||||
//edit nginx config file
|
||||
$ sudo nano /etc/nginx/sites-avaible/default
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection keep-alive;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
sudo nginx -t
|
||||
sudo nginx -s reload
|
||||
|
||||
To create the service, we first need to create the configurations in a systemd unit file.
|
||||
The unit file contains information regarding the unit, which is a service in this case.
|
||||
For services, it should have the .service extension and contain some
|
||||
information about the service. These files are required to be in the
|
||||
/etc/systemd/system directory.
|
||||
|
||||
Let’s use “nano” to create the unit file and name it kestrel-app.service:
|
||||
|
||||
[Unit]
|
||||
Description=ASP.NET Core Web App running on Ubuntu
|
||||
[Service]
|
||||
WorkingDirectory=/var/www/app
|
||||
ExecStart=/usr/bin/dotnet /var/www/app/DeployingToLinuxWithNginx.dll
|
||||
Restart=always
|
||||
# Restart service after 10 seconds if the dotnet service crashes:
|
||||
RestartSec=10
|
||||
KillSignal=SIGINT
|
||||
SyslogIdentifier=dotnet-web-app
|
||||
# This user should exist on the server and have ownership of the deployment directory
|
||||
User=www-data
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
After adding the content, save it. Then let’s enable and start the service:
|
||||
sudo systemctl enable kestrel-app.service
|
||||
sudo systemctl start kestrel-app.service
|
||||
|
||||
postgresql.conf:
|
||||
|
||||
Edit postgresql.conf:
|
||||
Locate the postgresql.conf file, typically in /etc/postgresql/<version>/main/postgresql.conf.
|
||||
Change the listen_addresses parameter to '*' to allow connections from any IP address, or specify the IP address(es) you want to allow.
|
||||
```
|
||||
|
||||
listen_addresses = '*'
|
||||
|
||||
|
||||
Locate the postgresql.conf file within the container (e.g., /etc/postgresql/<version>/main/postgresql.conf).
|
||||
Modify listen_addresses:
|
||||
Change listen_addresses = 'localhost' to listen_addresses = '*' to allow connections from any IP address.
|
||||
Alternatively, specify the container's IP address.
|
||||
|
||||
host all all 0.0.0.0/0 md5
|
||||
pg_hba.conf
|
||||
|
||||
**Edit `pg_hba.conf`:**
|
||||
* Locate the `pg_hba.conf` file, typically in `/etc/postgresql/<version>/main/pg_hba.conf`.
|
||||
* Add a line to allow connections from your desired network or specific IP addresses. For example, to allow connections from any IP address on the network using MD5 authentication:
|
||||
* ```
|
||||
host all all 0.0.0.0/0 md5
|
||||
|
||||
|
||||
setup password for postgresql
|
||||
sudo -u postgres psql
|
||||
postgres=#
|
||||
then type \password postgres
|
||||
|
||||
docker run -p 5432:5432 -e POSTGRES_PASSWORD=123456789 \
|
||||
-d postgres:9.3.6 \
|
||||
-c config_file=/path/to/postgresql.conf
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
|
||||
public interface IJwtUtils
|
||||
{
|
||||
public string GenerateJwtToken(UserDto user);
|
||||
public UserDto? ValidateJwtToken(string token);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
|
||||
public interface ILookup
|
||||
{
|
||||
Task<ResultModel<int>> SaveLookupAsync(LookupEditDto lookup);
|
||||
Task<ResultModel<List<LookupDto>>> GetLookupAsync(string type);
|
||||
Task<ResultModel<Dictionary<string, LookupDto>>> GetLookupDicAsync(string type);
|
||||
Task<ResultModel<List<LookupEditDto>>> GetLookupEditAsync(string type);
|
||||
Task<ResultModel<LookupEditDto>> GetLookupEditByIdAsync(int codeId, string type);
|
||||
Task<ResultModel<List<LookupDto>>> GetPersonsAsync();
|
||||
Task<ResultModel<List<LookupDto>>> GetStaffAsync();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
|
||||
public interface IPerson
|
||||
{
|
||||
Task<ResultModel<TreeNode<string>>> GetByFamilyAsync(int id);
|
||||
Task<ResultModel<List<TreeNode<string>>>> GetFamilyTreeBy(FamilyCriteria criteria);
|
||||
Task<ResultModel<List<PersonDto>>> GetChildren(ChildCriteria criteria);
|
||||
Task<ResultModel<List<PersonDto>>> GetPerson(PersonCriteria criteria);
|
||||
Task<ResultModel<Dictionary<int,PersonDto>>> GetDicFamily();
|
||||
Task<ResultModel<PersonDto>> GetByIdAsync(int id);
|
||||
Task<ResultModel<int>> DeleteAsync(int id);
|
||||
|
||||
Task<ResultModel<string>> UploadImage(UploadCriteria criteria);
|
||||
ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria);
|
||||
Task<ResultModel<int>> SaveAsync(PersonForSave dto);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
|
||||
public interface IRelationShipd
|
||||
{
|
||||
Task<ResultModel<int>> SaveAsync(List<RelationShipDto> dto);
|
||||
//load by personId
|
||||
Task<ResultModel<List<RelationShipDto>>> GetByPersonIdAsync(int personId);
|
||||
Task<ResultModel<RelationShipDto>> GetByIdAsync(int Id);
|
||||
|
||||
Task<ResultModel<int>> DeleteAsync(int personId);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
public interface IReport
|
||||
{
|
||||
// Task<ResultModel<FileContent>> GetMotorVehicleReportAsync(MotorVehicleCriteria criteria);
|
||||
// Task<ResultModel<FileContent>> GetStaffWorkReportAsync(StaffWorkCriteria criteria);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
|
||||
public interface IStaff
|
||||
{
|
||||
|
||||
Task<ResultModel<Dictionary<int,StaffDto>>> GetDicStaffs();
|
||||
Task<ResultModel<List<StaffDto>>> GetStaff(StaffCriteria criteria);
|
||||
Task<ResultModel<StaffDto>> GetStaffById(int id);
|
||||
Task<ResultModel<int>> SaveStaff(StaffDto code);
|
||||
Task<ResultModel<int>> ResetPassword(ResetPassDto code);
|
||||
Task<ResultModel<int>> SaveStaffNew(StaffDto adminUser);
|
||||
|
||||
Task<ResultModel<int>> Delete(int id);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
namespace FamilyTreeAPI.Interface;
|
||||
public interface IUserService
|
||||
{
|
||||
|
||||
Task<ResultModel<AuthenticateResponse>> LoginApiAD(AuthenticateRequest model, string remoteIpAddress);
|
||||
Task<ResultModel<AuthenticateResponse>> Login(AuthenticateRequest model);
|
||||
Task<ResultModel<int>> Logout(string token, UserDto user, string remoteIpAddress);
|
||||
|
||||
// Task<ResultModel<User>> SearchApiStaff(string staffLinkNo);
|
||||
// Task<ResultModel<User>> SearchADStaff(string staffLinkNo);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace FamilyTreeAPI.Models
|
||||
{
|
||||
public partial class FamilyTreeDBContext : DbContext
|
||||
{
|
||||
public FamilyTreeDBContext()
|
||||
{
|
||||
}
|
||||
|
||||
public FamilyTreeDBContext(DbContextOptions<FamilyTreeDBContext> options)
|
||||
: base(options)
|
||||
{
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
}
|
||||
|
||||
public virtual DbSet<Person> Persons { get; set; } = null!;
|
||||
public virtual DbSet<RelationShip> RelationShips { get; set; } = null!;
|
||||
public virtual DbSet<Lookup> Lookups { get; set; } = null!;
|
||||
public virtual DbSet<staff> staff { get; set; } = null!;
|
||||
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Person>(entity =>
|
||||
{
|
||||
entity.ToTable("person");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
|
||||
entity.Property(e => e.Address)
|
||||
.HasMaxLength(120)
|
||||
.HasColumnName("address");
|
||||
|
||||
entity.Property(e => e.Alive).HasColumnName("alive");
|
||||
entity.Property(e => e.dob).HasColumnName("dob");
|
||||
entity.Property(e => e.MotherId).HasColumnName("motherid");
|
||||
entity.Property(e => e.FatherId).HasColumnName("fatherid");
|
||||
|
||||
entity.Property(e => e.FirstName)
|
||||
.HasMaxLength(70)
|
||||
.HasColumnName("firstname");
|
||||
entity.Property(e => e.Sex)
|
||||
.HasMaxLength(10)
|
||||
.HasColumnName("sex");
|
||||
entity.Property(e => e.Image)
|
||||
.HasMaxLength(300)
|
||||
.HasColumnName("image");
|
||||
|
||||
entity.Property(e => e.LastName)
|
||||
.HasMaxLength(70)
|
||||
.HasColumnName("lastname");
|
||||
|
||||
entity.Property(e => e.Email)
|
||||
.HasMaxLength(80)
|
||||
.HasColumnName("email");
|
||||
|
||||
entity.Property(e => e.Phone)
|
||||
.HasMaxLength(30)
|
||||
.HasColumnName("phone");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<RelationShip>(entity =>
|
||||
{
|
||||
entity.ToTable("relationship");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.RelatePersonId).HasColumnName("relatepersonid");
|
||||
entity.Property(e => e.PersonId).HasColumnName("personid");
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Lookup>(entity =>
|
||||
{
|
||||
entity.ToTable("lookup");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
|
||||
entity.Property(e => e.Code)
|
||||
.HasMaxLength(10)
|
||||
.HasColumnName("code");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
.HasMaxLength(80)
|
||||
.HasColumnName("description");
|
||||
|
||||
entity.Property(e => e.Lactive).HasColumnName("lactive");
|
||||
|
||||
entity.Property(e => e.Type).HasMaxLength(20)
|
||||
.HasColumnName("ltype");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<staff>(entity =>
|
||||
{
|
||||
entity.ToTable("staff");
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
|
||||
entity.Property(e => e.Email)
|
||||
.HasMaxLength(80)
|
||||
.HasColumnName("email");
|
||||
|
||||
entity.Property(e => e.Firstname)
|
||||
.HasMaxLength(70)
|
||||
.HasColumnName("firstname");
|
||||
|
||||
entity.Property(e => e.Lastname)
|
||||
.HasMaxLength(70)
|
||||
.HasColumnName("lastname");
|
||||
|
||||
entity.Property(e => e.Phone)
|
||||
.HasMaxLength(30)
|
||||
.HasColumnName("phone");
|
||||
|
||||
entity.Property(e => e.Sactive).HasColumnName("sactive");
|
||||
|
||||
entity.Property(e => e.Spassword)
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("spassword");
|
||||
|
||||
entity.Property(e => e.Srole).HasColumnName("srole");
|
||||
|
||||
entity.Property(e => e.Stype).HasColumnName("stype");
|
||||
});
|
||||
|
||||
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FamilyTreeAPI.Models
|
||||
{
|
||||
public partial class Lookup
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Code { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public bool? Lactive { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FamilyTreeAPI.Models
|
||||
{
|
||||
public partial class Person
|
||||
{
|
||||
public Person()
|
||||
{
|
||||
// Servicetasks = new HashSet<Servicetask>();
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? Sex { get; set; }
|
||||
|
||||
public string? Image { get; set; }
|
||||
public bool? Alive { get; set; }
|
||||
public int? MotherId { get; set; }
|
||||
public int? FatherId { get; set; }
|
||||
|
||||
public DateTime? dob { get; set; }
|
||||
|
||||
//public virtual ICollection<Staffwork> Staffworks { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FamilyTreeAPI.Models;
|
||||
|
||||
public class RelationShip
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int RelatePersonId { get; set; }
|
||||
public int PersonId { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FamilyTreeAPI.Models
|
||||
{
|
||||
public partial class staff
|
||||
{
|
||||
public staff()
|
||||
{
|
||||
// ServicetaskAssigntoNavigations = new HashSet<Servicetask>();
|
||||
// ServicetaskStaffs = new HashSet<Servicetask>();
|
||||
// Staffworks = new HashSet<Staffwork>();
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string? Firstname { get; set; }
|
||||
public string? Lastname { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public int? Stype { get; set; }
|
||||
public int? Srole { get; set; }
|
||||
public string? Spassword { get; set; }
|
||||
public bool? Sactive { get; set; }
|
||||
|
||||
// public virtual ICollection<Servicetask> ServicetaskAssigntoNavigations { get; set; }
|
||||
// public virtual ICollection<Servicetask> ServicetaskStaffs { get; set; }
|
||||
//public virtual ICollection<Staffwork> Staffworks { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using DocumentFormat.OpenXml.Office2010.Drawing.Charts;
|
||||
using FamilyTreeAPI.Authorization;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.GraphQL.Query;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using FamilyTreeAPI.Repository;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
#region Services
|
||||
var services = builder.Services;
|
||||
services.AddCors();
|
||||
services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
services.AddEndpointsApiExplorer();
|
||||
services.AddSwaggerGen();
|
||||
|
||||
var appSettingsSection = builder.Configuration.GetSection("AppSettings");
|
||||
services.Configure<AppSettings>(appSettingsSection);
|
||||
var appSettings = appSettingsSection.Get<AppSettings>();
|
||||
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
|
||||
|
||||
services.AddAuthentication(x =>
|
||||
{
|
||||
|
||||
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.RequireHttpsMetadata = false;
|
||||
x.SaveToken = true;
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false
|
||||
};
|
||||
});
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IJwtUtils, JwtUtils>();
|
||||
services.AddScoped<ImportPersonRepository>();
|
||||
services.AddDbContext<FamilyTreeDBContext>(options =>
|
||||
{
|
||||
|
||||
// options.LogTo(s => Console.WriteLine(s));
|
||||
//options.UseNpgsql(appSettings.SQLConnectionString);
|
||||
string? conn = builder.Configuration.GetValue<string>("AppSettings:SQLConnectionString");
|
||||
// options.LogTo(s => Console.WriteLine(s));
|
||||
if (conn != null)
|
||||
options.UseNpgsql(conn);
|
||||
});
|
||||
|
||||
services.AddGraphQLServer()
|
||||
.AddFiltering()
|
||||
.AddSorting()
|
||||
.AddProjections()
|
||||
.RegisterDbContextFactory<FamilyTreeDBContext>()
|
||||
|
||||
.AddQueryType<QueryFamilyTree>();
|
||||
|
||||
|
||||
services.AddScoped<ILookup, LookupRepository>();
|
||||
services.AddScoped<IStaff, StaffRepository>();
|
||||
|
||||
services.AddScoped<IRelationShipd,RelationShipRepository>();
|
||||
services.AddScoped<IPerson, PersonRepository>();
|
||||
services.AddScoped<IUserService, UserServiceRepository>();
|
||||
services.AddScoped<IReport, ReportRepository>();
|
||||
services.AddScoped<Seed>();
|
||||
/*
|
||||
|
||||
public FamilyTreeDBContext(DbContextOptions<FamilyTreeDBContext> options)
|
||||
: base(options)
|
||||
{
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
}
|
||||
services.AddScoped<IAdminUser, AdminUserRepository>();
|
||||
|
||||
services.AddScoped<IMotorVehicles, MotorVehiclesRepository>();
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region app
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = scope.ServiceProvider.GetRequiredService<FamilyTreeDBContext>();
|
||||
var db = context.Database;
|
||||
if (db != null)
|
||||
{
|
||||
|
||||
db.Migrate();
|
||||
db.EnsureCreated();
|
||||
var staff = context.GetService<Seed>();
|
||||
int id = staff.InsertOneUser();
|
||||
if (id < 0)
|
||||
{
|
||||
var databaseCreator = (context.GetService<IDatabaseCreator>() as RelationalDatabaseCreator);
|
||||
if (databaseCreator != null)
|
||||
{
|
||||
//if (!databaseCreator.Exists())
|
||||
databaseCreator.CreateTables();
|
||||
}
|
||||
id = staff.InsertOneUser();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
app.UseMiddleware<JwtMiddleware>();
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())//
|
||||
//{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
//}
|
||||
// global cors policy
|
||||
app.UseCors(x => x
|
||||
.SetIsOriginAllowed(origin => true)
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.WithExposedHeaders("Content-Disposition")
|
||||
.AllowCredentials());
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.MapGraphQL();
|
||||
app.MapControllers();
|
||||
app.Run();
|
||||
|
||||
#endregion
|
||||
|
||||
/*
|
||||
https://www.youtube.com/watch?v=WQFx2m5Ub9M
|
||||
|
||||
|
||||
https://csharptotypescript.azurewebsites.net/
|
||||
\ services.AddDbContext<BloggingContext>(options =>
|
||||
options.UseNpgsql(Configuration.GetConnectionString("BloggingContext")));
|
||||
dotnet ef dbcontext scaffold "host=postgresdb;port=5432;Database=FamilyTreeDB;Username=postgres;password=Positive~1" Npgsql.EntityFrameworkCore.PostgreSQL -Schemas schema1 --output-dir Models
|
||||
PM> Scaffold-DbContext "Host=postgresdb;Port=5432;database=FamilyTreeDB;user id=postgres;Password=Positive~1" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Models
|
||||
*/
|
||||
|
||||
/*
|
||||
graphql
|
||||
https://www.youtube.com/watch?v=HnXA8RI7Tvc
|
||||
|
||||
query {
|
||||
family {
|
||||
nodes{
|
||||
id
|
||||
firstname
|
||||
lastname
|
||||
email
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"profiles": {
|
||||
"FamilyTreeAPI": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5016",
|
||||
"applicationUrl1": "http://192.168.8.188:5015"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": false
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:4559",
|
||||
"sslPort": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace FamilyTreeAPI.Repository
|
||||
{
|
||||
public class Email
|
||||
{
|
||||
public static void SendMail(string smtpServer, string sender, string sendTo,
|
||||
string header, string message, bool mailPriority = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(smtpServer) && string.IsNullOrEmpty(sender) && string.IsNullOrEmpty(sendTo))
|
||||
return;
|
||||
|
||||
System.Net.Mail.SmtpClient client = new SmtpClient(smtpServer);
|
||||
MailMessage mailMessage = new MailMessage();
|
||||
mailMessage.From = new MailAddress(sender);
|
||||
|
||||
var splitEmails = sendTo.Split(';');
|
||||
foreach (string item in splitEmails)
|
||||
{
|
||||
mailMessage.To.Add(item);
|
||||
}
|
||||
if (mailPriority)
|
||||
mailMessage.Priority = MailPriority.High;
|
||||
|
||||
// mailMessage.To.Add(sendTo);
|
||||
mailMessage.Subject = header;
|
||||
string text = message;
|
||||
string signature = "";
|
||||
try
|
||||
{
|
||||
text += "<br/>" + signature;
|
||||
mailMessage.Body = text;
|
||||
mailMessage.IsBodyHtml = true;
|
||||
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
// ErrorLogger.logError(ex, "CPA");
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string userState = "send to fleet manager";
|
||||
client.SendAsync(mailMessage, userState);
|
||||
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using SpreadsheetLight;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public class ImportPersonRepository
|
||||
{
|
||||
enum enumIdx
|
||||
{
|
||||
Id = 1,
|
||||
FirstName,
|
||||
LastName,
|
||||
Email,
|
||||
Phone,
|
||||
Image,
|
||||
Sex,
|
||||
Address,
|
||||
Alive,
|
||||
Dob,
|
||||
FatherId,
|
||||
MotherId,
|
||||
|
||||
}
|
||||
private FamilyTreeDBContext _context;
|
||||
private IConfiguration _config;
|
||||
private IHttpContextAccessor _httpContext;
|
||||
private IRelationShipd _relationship;
|
||||
public ImportPersonRepository(IConfiguration config, FamilyTreeDBContext context,
|
||||
IRelationShipd relationship,
|
||||
IHttpContextAccessor httpContext)
|
||||
{
|
||||
_context = context;
|
||||
_relationship = relationship;
|
||||
_config = config;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
public async Task<string> GetImageAsBase64String(string imagePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read the image file into a byte array
|
||||
//byte[] imageBytes = File.ReadAllBytes(imagePath);
|
||||
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
|
||||
// Convert the byte array to a Base64 string
|
||||
string base64String = Convert.ToBase64String(imageBytes);
|
||||
|
||||
return base64String;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Console.WriteLine($"Error: The file at '{imagePath}' was not found.");
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultModel<string>> DownloadFile(DownloadFileCriteria criteria)
|
||||
{
|
||||
int statusCode = -1;
|
||||
string result = "";
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
string imagePath = _config.GetValue<string>("AppSettings:ImageFolder");
|
||||
string imageUrl = System.IO.Path.Combine(imagePath, criteria.FileName);
|
||||
if (System.IO.File.Exists(imageUrl))
|
||||
{
|
||||
result = await this.GetImageAsBase64String(imageUrl);
|
||||
statusCode = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "error filename " + criteria.FileName + " can not find in this folder:" + imagePath;
|
||||
statusCode = -1;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusCode = -1;
|
||||
error = ex.Message;
|
||||
}
|
||||
|
||||
return new ResultModel<string>
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
public async Task<List<CodeDto<string>>> ImportPerson(MemoryStream fileStream, string sheetName)
|
||||
{
|
||||
Dictionary<string, ImportRelation> relationDic = new();
|
||||
int id;
|
||||
string keypair = ""; //father and mother key
|
||||
List<CodeDto<string>> output = new();
|
||||
Dictionary<int,MappingFatherMother> updateperson = new();
|
||||
CodeDto<string> colums;
|
||||
List<int> ids = new();
|
||||
Person person;
|
||||
Person? uperson;
|
||||
ImportRelation iRelation;
|
||||
MappingFatherMother mitem,faitem,moitem;
|
||||
int fid, mid;
|
||||
// MemoryStream msFirstPass = new MemoryStream();
|
||||
SLDocument sl = new SLDocument(fileStream, sheetName);
|
||||
// There is no way that I can see to get the Rows
|
||||
SLWorksheetStatistics stats = sl.GetWorksheetStatistics();
|
||||
for (int row = 2; row <= stats.NumberOfRows; row++)
|
||||
{
|
||||
person = new Person();
|
||||
mitem = new();
|
||||
id = sl.GetCellValueAsInt32(row, (int) enumIdx.Id);
|
||||
mitem.IId = id;
|
||||
person.FirstName = sl.GetCellValueAsString(row, (int) enumIdx.FirstName);
|
||||
person.LastName = sl.GetCellValueAsString(row, (int) enumIdx.LastName);
|
||||
person.Email = sl.GetCellValueAsString(row, (int) enumIdx.Email);
|
||||
person.Phone = sl.GetCellValueAsString(row, (int) enumIdx.Phone);
|
||||
person.Image = sl.GetCellValueAsString(row, (int) enumIdx.Image);
|
||||
person.Alive = sl.GetCellValueAsBoolean(row, (int) enumIdx.Alive);
|
||||
person.dob = sl.GetCellValueAsDateTime(row, (int) enumIdx.Dob);
|
||||
person.Sex = sl.GetCellValueAsString(row, (int) enumIdx.Sex);
|
||||
person.Address = sl.GetCellValueAsString(row, (int) enumIdx.Address);
|
||||
mitem.IFatherId = sl.GetCellValueAsInt32(row, (int) enumIdx.FatherId);
|
||||
mitem.IMotherId = sl.GetCellValueAsInt32(row, (int) enumIdx.MotherId);
|
||||
_context.Persons.Add(person);
|
||||
_context.SaveChanges();
|
||||
mitem.TId = person.Id;
|
||||
ids.Add(id);
|
||||
updateperson.Add(mitem.IId, mitem);
|
||||
colums = new();
|
||||
colums.Code = $"{mitem.IId} {mitem.IFatherId} {mitem.IMotherId}";
|
||||
colums.Description = $"{mitem.TId} {mitem.TFatherId} {mitem.TMotherId}";
|
||||
output.Add(colums);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < ids.Count; i++)
|
||||
{
|
||||
id = ids[i];
|
||||
mitem = updateperson[id];
|
||||
fid = mitem.IFatherId;
|
||||
if (updateperson.ContainsKey(fid))
|
||||
{
|
||||
faitem = updateperson[fid];
|
||||
mitem.TFatherId = faitem.TId;
|
||||
}
|
||||
mid = mitem.IMotherId;
|
||||
if (updateperson.ContainsKey(mid))
|
||||
{
|
||||
moitem = updateperson[mid];
|
||||
mitem.TMotherId = moitem.TId;
|
||||
}
|
||||
|
||||
uperson = await _context.Persons.FindAsync(mitem.TId);
|
||||
if (uperson == null) continue;
|
||||
uperson.FatherId = mitem.TFatherId;
|
||||
uperson.MotherId = mitem.TMotherId;
|
||||
if (mitem.TFatherId > 0 && mitem.TMotherId > 0)
|
||||
{
|
||||
keypair = mitem.TFatherId + "," + mitem.TMotherId;
|
||||
if (!relationDic.ContainsKey(keypair))
|
||||
{
|
||||
iRelation = new();
|
||||
iRelation.FatherId = mitem.TFatherId;
|
||||
iRelation.MotherId = mitem.TMotherId;
|
||||
relationDic.Add(keypair, iRelation);
|
||||
}
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
}
|
||||
//finally add relation ship table
|
||||
RelationShip model;
|
||||
foreach (KeyValuePair<string, ImportRelation> item in relationDic)
|
||||
{
|
||||
model = new RelationShip();
|
||||
model.PersonId = item.Value.FatherId;
|
||||
model.RelatePersonId = item.Value.MotherId;
|
||||
_context.RelationShips.Add(model);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FamilyTreeAPI.Repository
|
||||
{
|
||||
public class LookupRepository: ILookup
|
||||
{
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
public LookupRepository(FamilyTreeDBContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private bool checkDescription(string desc, string type, int id)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
{
|
||||
string ldesc = desc.ToLower();
|
||||
int count = _context.Lookups.Where(x => (x.Code ?? "").ToLower() == ldesc
|
||||
&& id != x.Id
|
||||
).ToList().Count();
|
||||
result = count > 0;
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public async Task<ResultModel<int>> SaveLookupAsync(LookupEditDto lookup)
|
||||
{
|
||||
int result = -1;
|
||||
int statusCode = 0;
|
||||
string desc = lookup.Description.Trim();
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
Lookup model = null!;
|
||||
bool ok = !checkDescription(desc, lookup.Type, lookup.Id);
|
||||
if (ok)
|
||||
{
|
||||
if (lookup.Id < 1)
|
||||
{
|
||||
model = new();
|
||||
model.Code = lookup.CodeId;
|
||||
model.Description = desc;
|
||||
model.Type = lookup.Type;
|
||||
model.Lactive = lookup.Active;
|
||||
|
||||
_context.Lookups.Add(model);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id);
|
||||
if (model1 != null)
|
||||
{
|
||||
model1.Description = desc;
|
||||
model1.Code = lookup.CodeId;
|
||||
model1.Lactive = lookup.Active;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
if (model != null)
|
||||
result = model.Id;
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
statusCode = 0;
|
||||
error = "description is already in database";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
public async Task<ResultModel<LookupEditDto>> GetLookupEditByIdAsync(int id, string type)
|
||||
{
|
||||
List<LookupEditDto> resultList;
|
||||
LookupEditDto item = null!;
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.Lookups.Where(x => x.Type == type
|
||||
&& (x.Id == id))
|
||||
.Select(item => new LookupEditDto
|
||||
{
|
||||
CodeId = string.IsNullOrEmpty(item.Code) ? "0": item.Code,
|
||||
Active = item.Lactive ?? false,
|
||||
Id = item.Id,
|
||||
Description = item.Description ?? ""
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
if (resultList.Count > 0)
|
||||
item = resultList[0];
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<LookupEditDto>()
|
||||
{
|
||||
Data = item,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
public async Task<ResultModel<List<LookupDto>>> GetLookupAsync(string type)
|
||||
{
|
||||
List<LookupDto> resultList = new();
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.Lookups.Where(x => x.Type == type && ((x.Lactive ?? false) == true))
|
||||
.Select(item => new LookupDto { Id = item.Id, CodeId = item.Code ?? "", Description = item.Description ?? "" })
|
||||
.ToListAsync();
|
||||
|
||||
resultList.Sort((x, y) => x.Description.CompareTo(y.Description));
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<LookupDto>>()
|
||||
{
|
||||
Data = resultList,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<Dictionary<string, LookupDto>>> GetLookupDicAsync(string type)
|
||||
{
|
||||
Dictionary<string, LookupDto> resultList = new();
|
||||
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.Lookups.Where(x => x.Type == type && ((x.Lactive ?? false) == true))
|
||||
.Select(item => new LookupDto { Id = item.Id, CodeId = item.Code ?? "", Description = item.Description ?? "" })
|
||||
.ToDictionaryAsync(x => x.CodeId);
|
||||
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<Dictionary<string, LookupDto>>()
|
||||
{
|
||||
Data = resultList,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
public async Task<ResultModel<List<LookupEditDto>>> GetLookupEditAsync(string type)
|
||||
{
|
||||
List<LookupEditDto> resultList = new();
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.Lookups.Where(x => x.Type == type)
|
||||
.Select(item => new LookupEditDto
|
||||
{
|
||||
Id = item.Id,
|
||||
Active = item.Lactive ?? false,
|
||||
CodeId = item.Code ?? "",
|
||||
Type = item.Type ?? "",
|
||||
Description = item.Description ?? ""
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
resultList.Sort((x, y) => x.Description.CompareTo(y.Description));
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<LookupEditDto>>()
|
||||
{
|
||||
Data = resultList,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
public async Task<ResultModel<List<LookupDto>>> GetPersonsAsync()
|
||||
{
|
||||
List<LookupDto> resultList = new();
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.Persons.Where(x => x.Alive == true)
|
||||
.Select(item => new LookupDto { Id = item.Id, CodeId = item.Id.ToString(), Description = item.FirstName ?? "" })
|
||||
.ToListAsync();
|
||||
|
||||
resultList.Sort((x, y) => x.Description.CompareTo(y.Description));
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<LookupDto>>()
|
||||
{
|
||||
Data = resultList,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<List<LookupDto>>> GetStaffAsync()
|
||||
{
|
||||
List<LookupDto> resultList = new();
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
resultList = await _context.staff.Where(x => x.Sactive == true)
|
||||
.Select(item => new LookupDto { Id = item.Id, CodeId = item.Firstname ?? "", Description = item.Lastname ?? "" })
|
||||
.ToListAsync();
|
||||
|
||||
resultList.Sort((x, y) => x.Description.CompareTo(y.Description));
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<LookupDto>>()
|
||||
{
|
||||
Data = resultList,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using DocumentFormat.OpenXml.Office.CustomUI;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Helper;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
//using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public partial class PersonRepository : IPerson
|
||||
{
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
private readonly IRelationShipd _relationship;
|
||||
private readonly IHttpContextAccessor _httpContext;
|
||||
private readonly IConfiguration _config;
|
||||
const string dateFormat = "yyyy-MM-dd";
|
||||
public PersonRepository(IConfiguration config, FamilyTreeDBContext context,
|
||||
IRelationShipd relationship,
|
||||
IHttpContextAccessor httpContext)
|
||||
{
|
||||
_context = context;
|
||||
_relationship = relationship;
|
||||
_config = config;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
private PersonDto FillDto(Person model)
|
||||
{
|
||||
PersonDto dto = new();
|
||||
dto.Id = model.Id;
|
||||
dto.FirstName = model.FirstName;
|
||||
dto.LastName = model.LastName;
|
||||
dto.Email = model.Email;
|
||||
dto.Address = model.Address;
|
||||
dto.Phone = model.Phone;
|
||||
dto.Alive = model.Alive;
|
||||
dto.Image = model.Image;
|
||||
dto.FatherId = model.FatherId;
|
||||
dto.MotherId = model.MotherId;
|
||||
dto.Sex = model.Sex;
|
||||
dto.dob = Helpers.DateToStr(model.dob);
|
||||
/*
|
||||
dto.AddedOn = model.AddedOn;
|
||||
|
||||
dto.RoleType = model.RoleType;
|
||||
|
||||
*/
|
||||
|
||||
return dto;
|
||||
}
|
||||
private Person FillModel(Person model, PersonDto dto)
|
||||
{
|
||||
|
||||
model.FirstName = dto.FirstName;
|
||||
model.LastName = dto.LastName;
|
||||
model.Email = dto.Email;
|
||||
model.Address = dto.Address;
|
||||
model.Phone = dto.Phone;
|
||||
model.Alive = dto.Alive;
|
||||
model.Image = dto.Image;
|
||||
model.FatherId = dto.FatherId;
|
||||
model.MotherId = dto.MotherId;
|
||||
model.Sex = dto.Sex;
|
||||
model.dob = Helpers.DateToDateTime(dto.dob);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public async Task<ResultModel<Dictionary<int, PersonDto>>> GetDicFamily()
|
||||
{
|
||||
|
||||
Dictionary<int, PersonDto> list = new();
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
PersonDto item;
|
||||
Person jitem;
|
||||
try
|
||||
{
|
||||
//list = await _context.LoadStaffAsync(criteria);
|
||||
var jlist = await _context.Persons.ToListAsync();
|
||||
for (int i = 0; i < jlist.Count; i++)
|
||||
{
|
||||
jitem = jlist[i];
|
||||
item = FillDto(jitem);
|
||||
list.Add(item.Id,item);
|
||||
}
|
||||
statuscode = 1;
|
||||
//list.Sort((x, y) => x.Code.CompareTo(y.Code));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel< Dictionary<int, PersonDto> > ()
|
||||
{
|
||||
Data = list,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<List<PersonDto>> GetChildrens(int FatherId, int MotherId)
|
||||
{
|
||||
List<PersonDto> list = new();
|
||||
PersonDto item;
|
||||
Person jitem;
|
||||
var jlist = await _context.Persons.Where(x =>
|
||||
(x.MotherId == FatherId && x.FatherId == MotherId) ||
|
||||
(x.FatherId == FatherId && x.MotherId == MotherId)).ToListAsync();
|
||||
for (int i = 0; i < jlist.Count; i++)
|
||||
{
|
||||
jitem = jlist[i];
|
||||
item = FillDto(jitem);
|
||||
list.Add(item);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
public async Task<ResultModel<List<PersonDto>>> GetChildren(ChildCriteria criteria)
|
||||
{
|
||||
List<PersonDto> list = new();
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
PersonDto item;
|
||||
Person jitem;
|
||||
try
|
||||
{
|
||||
//list = await _context.LoadStaffAsync(criteria);
|
||||
list = await GetChildrens(criteria.FatherId, criteria.MotherId);
|
||||
statuscode = 1;
|
||||
//list.Sort((x, y) => x.Code.CompareTo(y.Code));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<PersonDto>>()
|
||||
{
|
||||
Data = list,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<List<PersonDto>>> GetPerson(PersonCriteria criteria)
|
||||
{
|
||||
|
||||
List<PersonDto> list = new();
|
||||
List<Person>? jlist;
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
PersonDto item;
|
||||
Person jitem;
|
||||
string firstName = criteria.FirstName;
|
||||
string lastName = criteria.LastName;
|
||||
int Id = criteria.Id;
|
||||
try
|
||||
{
|
||||
//list = await _context.LoadStaffAsync(criteria);
|
||||
if (!string.IsNullOrEmpty(firstName))
|
||||
{
|
||||
firstName = firstName.ToLower();
|
||||
jlist = await _context.Persons.Where(x =>
|
||||
EF.Functions.Like(x.FirstName.ToLower(),firstName + "%")).ToListAsync();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName))
|
||||
{
|
||||
firstName = firstName.ToLower();
|
||||
lastName = lastName.ToLower();
|
||||
jlist = await _context.Persons.Where(x => EF.Functions.Like(x.FirstName.ToLower(),firstName + "%")
|
||||
&& EF.Functions.Like(x.LastName.ToLower(),lastName + "%")).ToListAsync();
|
||||
}
|
||||
else
|
||||
jlist = await _context.Persons.ToListAsync();
|
||||
|
||||
for (int i = 0; i < jlist.Count; i++)
|
||||
{
|
||||
jitem = jlist[i];
|
||||
item = FillDto(jitem);
|
||||
list.Add(item);
|
||||
}
|
||||
statuscode = 1;
|
||||
//list.Sort((x, y) => x.Code.CompareTo(y.Code));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<PersonDto>>()
|
||||
{
|
||||
Data = list,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<Person> GetPerson(int id)
|
||||
{
|
||||
Person rval = await _context.Persons.FindAsync(id);
|
||||
return rval;
|
||||
}
|
||||
|
||||
private string getStyleClass(string va)
|
||||
{
|
||||
string result = "";
|
||||
//result = "bg-indigo-500 text-black";
|
||||
|
||||
if (va == "T")
|
||||
{
|
||||
result = "bg-indigo-500 text-white";
|
||||
}
|
||||
else if (va == "P")
|
||||
{
|
||||
//result = "bg-purple-500 text-black";
|
||||
result = "bg-purple-500 text-black";
|
||||
}
|
||||
else if (va == "C")
|
||||
{
|
||||
result = "bg-teal-500 text-white";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public async Task<ResultModel<TreeNode<string>>> GetByFamilyAsync(int id)
|
||||
{
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
string key = "";
|
||||
string data = "";
|
||||
string type = "default";
|
||||
string pName = "";
|
||||
Person person;
|
||||
TreeNode<string> citem;
|
||||
TreeNode<string> child;
|
||||
TreeNode<string> node = new();
|
||||
node.Children = new();
|
||||
PersonDto dto = new();
|
||||
try
|
||||
{
|
||||
var item = await _context.Persons.FindAsync(id);
|
||||
if (item == null)
|
||||
{
|
||||
statuscode = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
node.Label = item.FirstName;
|
||||
node.Key = item.Id.ToString();
|
||||
node.Type = type;
|
||||
node.Icon = "T";
|
||||
node.StyleClass = getStyleClass(node.Icon);
|
||||
node.Expanded = true;
|
||||
node.Data = item.Sex;
|
||||
statuscode = 1;
|
||||
dto = FillDto(item);
|
||||
await GetPartnerChildrens(dto, node);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<TreeNode<string>> ()
|
||||
{
|
||||
Data = node,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
private async Task GetPartnerChildrens(PersonDto person, TreeNode<string> node)
|
||||
{
|
||||
/*****************************/
|
||||
ResultModel<List<RelationShipDto>> rlist = await _relationship.GetByPersonIdAsync(person.Id);
|
||||
List<RelationShipDto> relationShips = rlist.Data;
|
||||
RelationShipDto relate;
|
||||
string type = node.Type;
|
||||
TreeNode<string> child;
|
||||
PersonDto dto;
|
||||
Person pe;
|
||||
int fatherId, motherId;
|
||||
fatherId = motherId = 0;
|
||||
string data = person.Sex;
|
||||
TreeNode<string> citem;
|
||||
string pName = "";
|
||||
string key = "";
|
||||
for (int i = 0; i < relationShips.Count; i++)
|
||||
{
|
||||
relate = relationShips[i];
|
||||
if (relate.PersonId == person.Id)
|
||||
{
|
||||
if (data == "M")
|
||||
{
|
||||
fatherId = relate.PersonId;
|
||||
motherId = relate.RelatePersonId;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
fatherId = relate.RelatePersonId;
|
||||
motherId = relate.PersonId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data == "M")
|
||||
{
|
||||
fatherId = relate.RelatePersonId;
|
||||
motherId = relate.PersonId;
|
||||
}
|
||||
else
|
||||
{
|
||||
fatherId = relate.PersonId;
|
||||
motherId = relate.RelatePersonId;
|
||||
}
|
||||
}
|
||||
//get children
|
||||
List<PersonDto> children = await GetChildrens(fatherId, motherId);
|
||||
if (children.Count > 0)
|
||||
{
|
||||
if (person.Id != motherId)
|
||||
{
|
||||
//kham
|
||||
if (motherId > 0)
|
||||
{
|
||||
pe = await GetPerson(motherId);
|
||||
pName = pe.FirstName;
|
||||
key = motherId.ToString();
|
||||
data = pe.Sex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fatherId > 0)
|
||||
{
|
||||
pe = await GetPerson(fatherId);
|
||||
pName = pe.FirstName;
|
||||
key = fatherId.ToString();
|
||||
data = pe.Sex;
|
||||
}
|
||||
}
|
||||
|
||||
citem = new TreeNode<string>();
|
||||
citem.Label = pName;
|
||||
citem.Icon = "P";
|
||||
citem.StyleClass = getStyleClass(citem.Icon);
|
||||
citem.Expanded = true;
|
||||
citem.Data = data;
|
||||
citem.Type = type;
|
||||
citem.Key = key;
|
||||
citem.Children = new List<TreeNode<string>>();
|
||||
node.Children.Add(citem);
|
||||
for (int j = 0; j < children.Count; j++)
|
||||
{
|
||||
dto = children[j];
|
||||
child = new TreeNode<string>();
|
||||
child.Expanded = true;
|
||||
child.Type = type;
|
||||
child.Icon = "C";
|
||||
child.StyleClass = getStyleClass(child.Icon);
|
||||
child.Label = dto.FirstName;
|
||||
child.Key = dto.Id.ToString();
|
||||
child.Data = dto.Sex;
|
||||
child.Children = new();
|
||||
citem.Children.Add(child);
|
||||
//get child partner and repeat this.
|
||||
await GetPartnerChildrens(dto, child);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
}
|
||||
|
||||
public async Task<ResultModel<PersonDto>> GetByIdAsync(int id)
|
||||
{
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
PersonDto dto = new();
|
||||
try
|
||||
{
|
||||
var item = await _context.Persons.FindAsync(id);
|
||||
if (item == null)
|
||||
{
|
||||
statuscode = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dto = FillDto(item);
|
||||
statuscode = 1;
|
||||
ResultModel<List<RelationShipDto>> rlist = await _relationship.GetByPersonIdAsync(dto.Id);
|
||||
dto.RelationShips = rlist.Data;
|
||||
statuscode = rlist.StatusCode;
|
||||
error = rlist.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<PersonDto>()
|
||||
{
|
||||
Data = dto,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
private string GetDateTimeNow()
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// need filename to get extension file and family name for id combine to
|
||||
/// name to table.
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="familyId"></param>
|
||||
/// <param name="base64"></param>
|
||||
private string SaveFile(string fileName, int familyId, string base64)
|
||||
{
|
||||
string path = "";
|
||||
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
string sdate = GetCurrentDateTime();
|
||||
string filename, extention;
|
||||
filename = extention = "";
|
||||
int first = base64.IndexOf("base64,") +"base64,".Length;
|
||||
int last = base64.Length;
|
||||
string rbase = base64.Substring(first, last - first);
|
||||
|
||||
byte[] newBytes = Convert.FromBase64String(rbase);
|
||||
path = _config["AppSettings:ImageFolder"];
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
extention = System.IO.Path.GetExtension(fileName);
|
||||
filename = familyId + "_" + sdate + extention;
|
||||
string fullpath = System.IO.Path.Combine(path, filename);
|
||||
/*
|
||||
using (var fileStream = new FileStream(fullpath, FileMode.Create))
|
||||
{
|
||||
StreamWriter writer = new StreamWriter(fileStream);
|
||||
writer.Write(newBytes);
|
||||
//writer.BaseStream.Write(bytes, 0, bytes.Length);
|
||||
|
||||
}
|
||||
*/
|
||||
/*
|
||||
using (MemoryStream ms = new MemoryStream(newBytes))
|
||||
{
|
||||
Image image = Image.FromStream(ms);
|
||||
image.Save(fullpath);
|
||||
|
||||
}
|
||||
*/
|
||||
using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
fileStream.Write(newBytes, 0, newBytes.Length);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
public async Task<ResultModel<int>> SaveAsync(PersonForSave container)
|
||||
{
|
||||
int result = default(int);
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
|
||||
HttpContext? httpContext = _httpContext.HttpContext;
|
||||
string loginName = "";
|
||||
if (httpContext != null)
|
||||
{
|
||||
UserDto? user = (UserDto?)httpContext.Items["User"];
|
||||
if (user != null)
|
||||
loginName = user.FirstName + " " + user.LastName;
|
||||
}
|
||||
PersonDto item = container.Person;
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
Person model;
|
||||
if (item.Id < 1)
|
||||
{
|
||||
model = new();
|
||||
model = FillModel(model, item);
|
||||
|
||||
// model.Active = true;
|
||||
// model.AddedBy = loginName;
|
||||
// model.AddedOn = DateTime.Now;
|
||||
_context.Persons.Add(model);
|
||||
var successid = await _context.SaveChangesAsync();
|
||||
result = model.Id;
|
||||
if (!string.IsNullOrEmpty(container.FileName))
|
||||
{
|
||||
|
||||
string image = SaveFile(container.FileName, result, container.FormData);
|
||||
// update the image in table to new
|
||||
model.Image = image;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var model1 = await _context.Persons.FindAsync(item.Id);
|
||||
if (model1 != null)
|
||||
{
|
||||
model1 = FillModel(model1, item);
|
||||
|
||||
result = item.Id;
|
||||
if (!string.IsNullOrEmpty(container.FileName))
|
||||
{
|
||||
string image = SaveFile(container.FileName, result, container.FormData);
|
||||
// update the image in table to new
|
||||
model1.Image = image;
|
||||
}
|
||||
var successid = await _context.SaveChangesAsync();
|
||||
}
|
||||
// model.LastModified = DateTime.Now;
|
||||
// model.LastModifiedId = loginName;
|
||||
}
|
||||
|
||||
statuscode = 1;
|
||||
if (item.RelationShips != null)
|
||||
{
|
||||
ResultModel<int> rresult = await this._relationship.SaveAsync(item.RelationShips);
|
||||
statuscode = rresult.StatusCode;
|
||||
error += rresult.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
}
|
||||
//var dto = await Task.Run(() => result);
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<int>> DeleteAsync(int id)
|
||||
{
|
||||
int result = 1;
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
var model = await _context.Persons.FindAsync(id);
|
||||
if (model != null)
|
||||
_context.Persons.Remove(model);
|
||||
|
||||
var successCount = await _context.SaveChangesAsync();
|
||||
statuscode = 1;
|
||||
result = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//var dto = await Task.Run(() => result);
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public Task<ResultModel<string>> UploadImage(UploadCriteria criteria) {
|
||||
return UploadImagep(criteria);
|
||||
}
|
||||
|
||||
private string GetCurrentDateTime()
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
return now.ToString("yyyyMMdd");
|
||||
}
|
||||
|
||||
private async Task<ResultModel<string>> UploadImagep(UploadCriteria criteria)
|
||||
{
|
||||
string path = "";
|
||||
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
string sdate = GetCurrentDateTime();
|
||||
string filename, extention;
|
||||
filename = extention = "";
|
||||
try
|
||||
{
|
||||
if (criteria.File.Length > 0)
|
||||
{
|
||||
path = _config["AppSettings:ImageFolder"];
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
extention = System.IO.Path.GetExtension(criteria.FileName);
|
||||
filename = criteria.FamilyId + "_" + sdate + extention;
|
||||
|
||||
using (var fileStream = new FileStream(System.IO.Path.Combine(path, filename), FileMode.Create))
|
||||
{
|
||||
await criteria.File.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
statusCode = -1;
|
||||
error = "file contain is empty";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusCode = -1;
|
||||
error = ex.Message;
|
||||
|
||||
}
|
||||
|
||||
return new ResultModel<string>()
|
||||
{
|
||||
Data = filename,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria)
|
||||
{
|
||||
int result = -1;
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
var filePath = _config["AppSettings:ImageFolder"];
|
||||
var myfile = filePath + "\\" + criteria.Filename;
|
||||
try
|
||||
{
|
||||
File.Delete(myfile);
|
||||
result = 1;
|
||||
|
||||
statusCode = 1;
|
||||
Person? model = _context.Persons.Find(criteria.FamilyId);
|
||||
if (model != null)
|
||||
{
|
||||
model.Image = null;
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result = -1;
|
||||
statusCode = -1;
|
||||
error += e.Message;
|
||||
}
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Drawing.Text;
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public partial class PersonRepository
|
||||
{
|
||||
private TreeNode<string> PopulateItem(Person model)
|
||||
{
|
||||
TreeNode<string> treeNode = new();
|
||||
treeNode.Label = model.FirstName;
|
||||
treeNode.Data = model.Id.ToString();
|
||||
treeNode.Key = model.Id.ToString();
|
||||
treeNode.Expanded = false;
|
||||
treeNode.Children = new();
|
||||
return treeNode;
|
||||
}
|
||||
private bool UseFamilyCriteria(FamilyCriteria criteria, Person person)
|
||||
{
|
||||
bool result = false;
|
||||
if (criteria.UseFather && criteria.UseMother)
|
||||
{
|
||||
result = person.FatherId.GetValueOrDefault(0) == 0
|
||||
&& person.MotherId.GetValueOrDefault(0) == 0;
|
||||
}
|
||||
else if (criteria.UseFather)
|
||||
{
|
||||
result = person.FatherId.GetValueOrDefault(0) == 0;
|
||||
}
|
||||
else if (criteria.UseMother)
|
||||
{
|
||||
result = person.MotherId.GetValueOrDefault(0) == 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
private bool UseChildFamilyCriteria(FamilyCriteria criteria, int id, Person person)
|
||||
{
|
||||
bool result = false;
|
||||
int parentId;
|
||||
if (criteria.UseFather)
|
||||
{
|
||||
parentId = person.FatherId.GetValueOrDefault(0);
|
||||
result = parentId == id;
|
||||
}
|
||||
else if (criteria.UseMother)
|
||||
{
|
||||
parentId = person.MotherId.GetValueOrDefault(0);
|
||||
result = parentId == id;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
private List<Person> SplitListOfTopLevel(List<Person> list, FamilyCriteria criteria)
|
||||
{
|
||||
List<Person> result = new();
|
||||
Person item;
|
||||
for (int i = list.Count - 1; i > -1; i--)
|
||||
{
|
||||
item = list[i];
|
||||
if (UseFamilyCriteria(criteria,item))
|
||||
{
|
||||
result.Add(item);
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Person> GetParentId(List<Person> list,int id, FamilyCriteria criteria, Func<FamilyCriteria, int, Person, bool> conditionFn)
|
||||
{
|
||||
List<Person> result = new();
|
||||
Person item;
|
||||
for (int i = list.Count - 1; i > -1; i--)
|
||||
{
|
||||
item = list[i];
|
||||
if (conditionFn(criteria,id, item))
|
||||
{
|
||||
result.Add(item);
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TreeNode<string>> PopulateChild(FamilyCriteria criteria,int id, List<Person> childList, Func<FamilyCriteria, int ,Person,bool> conditionFn)
|
||||
{
|
||||
Person person;
|
||||
TreeNode<string> treeNode;
|
||||
List<TreeNode<string>> list = new();
|
||||
List<Person> children = GetParentId(childList,id, criteria, conditionFn);
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
person = children[i];
|
||||
treeNode = PopulateItem(person);
|
||||
list.Add(treeNode);
|
||||
// get children of this one too
|
||||
treeNode.Children = PopulateChild(criteria, person.Id, childList, UseChildFamilyCriteria);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
public async Task<ResultModel<List<TreeNode<string>>> > GetFamilyTreeBy(FamilyCriteria criteria)
|
||||
{
|
||||
int statusCode = -1;
|
||||
string error = "";
|
||||
Person person;
|
||||
TreeNode<string> treeNode;
|
||||
List<TreeNode<string>> data = new();
|
||||
try
|
||||
{
|
||||
var personList = await _context.Persons.ToListAsync();
|
||||
var topList = SplitListOfTopLevel(personList, criteria);
|
||||
for (int i = 0; i < topList.Count; i++)
|
||||
{
|
||||
person = topList[i];
|
||||
treeNode = PopulateItem(person);
|
||||
data.Add(treeNode);
|
||||
treeNode.Children = PopulateChild(criteria, person.Id, personList, UseChildFamilyCriteria);
|
||||
|
||||
}
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
}
|
||||
|
||||
return new ResultModel<List<TreeNode<string>>>
|
||||
{
|
||||
Data = data,
|
||||
StatusCode = statusCode,
|
||||
Message = error
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public class RelationShipRepository: IRelationShipd
|
||||
{
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
private readonly IHttpContextAccessor _httpContext;
|
||||
private readonly IConfiguration _config;
|
||||
public RelationShipRepository(IConfiguration config, FamilyTreeDBContext context, IHttpContextAccessor httpContext)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
private RelationShip FillModel(RelationShipDto dto, RelationShip model)
|
||||
{
|
||||
model.RelatePersonId = dto.RelatePersonId;
|
||||
model.PersonId = dto.PersonId;
|
||||
return model;
|
||||
}
|
||||
|
||||
private RelationShipDto FillDto(RelationShip model)
|
||||
{
|
||||
RelationShipDto dto = new();
|
||||
dto.Id = model.Id;
|
||||
dto.PersonId = model.PersonId;
|
||||
dto.RelatePersonId = model.RelatePersonId;
|
||||
return dto;
|
||||
}
|
||||
public async Task<ResultModel<int>> SaveAsync(List<RelationShipDto> list)
|
||||
{
|
||||
int statusCode = 0;
|
||||
string error = "";
|
||||
RelationShipDto item;
|
||||
RelationShip model;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
item = list[i];
|
||||
if (item.Id < 1)
|
||||
{
|
||||
model = new();
|
||||
model = FillModel(item, model);
|
||||
_context.RelationShips.Add(model);
|
||||
}
|
||||
else if (item.State == enumState.Modify)
|
||||
{
|
||||
RelationShip? RelationShip = await _context.RelationShips.FindAsync(item.Id);
|
||||
if (RelationShip != null)
|
||||
{
|
||||
RelationShip = FillModel(item, RelationShip);
|
||||
}
|
||||
}
|
||||
else if (item.State == enumState.Delete)
|
||||
{
|
||||
RelationShip? RelationShip = await _context.RelationShips.FindAsync(item.Id);
|
||||
if (RelationShip != null)
|
||||
{
|
||||
_context.RelationShips.Remove(RelationShip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statusCode = 1;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
}
|
||||
|
||||
return new ResultModel<int>
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Data = statusCode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<List<RelationShipDto>>> GetByPersonIdAsync(int personId)
|
||||
{
|
||||
string error = "";
|
||||
int statusCode = -1;
|
||||
List<RelationShipDto> list = new();
|
||||
RelationShipDto dto;
|
||||
RelationShip model;
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
var mlist = await _context.RelationShips.Where(x => x.PersonId == personId).ToListAsync();
|
||||
for (int i = 0; i < mlist.Count; i++)
|
||||
{
|
||||
model = mlist[i];
|
||||
dto = FillDto(model);
|
||||
list.Add(dto);
|
||||
}
|
||||
var rlist = await _context.RelationShips.Where(x => x.RelatePersonId == personId).ToListAsync();
|
||||
for (int i = 0; i < rlist.Count; i++)
|
||||
{
|
||||
model = rlist[i];
|
||||
dto = FillDto(model);
|
||||
list.Add(dto);
|
||||
}
|
||||
|
||||
statusCode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
}
|
||||
|
||||
return new ResultModel<List<RelationShipDto>>
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Data = list,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<RelationShipDto>> GetByIdAsync(int Id)
|
||||
{
|
||||
string error = "";
|
||||
int statusCode = -1;
|
||||
|
||||
RelationShipDto dto =new();
|
||||
RelationShip? model;
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
model = await _context.RelationShips.FindAsync(Id);
|
||||
if (model != null)
|
||||
dto = FillDto(model);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statusCode = -1;
|
||||
}
|
||||
|
||||
return new ResultModel<RelationShipDto>
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Data = dto,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<int>> DeleteAsync(int Id)
|
||||
{
|
||||
int result = -1;
|
||||
string error = "";
|
||||
int statusCode = -1;
|
||||
try
|
||||
{
|
||||
RelationShip? RelationShip = await _context.RelationShips.FindAsync(Id);
|
||||
if (RelationShip != null)
|
||||
{
|
||||
_context.RelationShips.Remove(RelationShip);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = e.ToString();
|
||||
statusCode = -1;
|
||||
}
|
||||
|
||||
return new ResultModel<int>
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Data = result,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Models;
|
||||
using SpreadsheetLight;
|
||||
using System.Linq;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public class ReportRepository : IReport
|
||||
{
|
||||
private enum EnumStaffWork
|
||||
{
|
||||
Id = 0,
|
||||
FirstName,
|
||||
LastName,
|
||||
StartDate,
|
||||
startTime,
|
||||
stopTime,
|
||||
Task,
|
||||
Job,
|
||||
Hour
|
||||
};
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
//private readonly ILookup _lookup;
|
||||
const string DateTimeFormat = "dd/MM/yyyy HH:mm";
|
||||
const string TimeFormat = "HH:mm";
|
||||
const string DateFormat = "dd/MM/yyyy";
|
||||
private readonly IStaff _staff;
|
||||
private Dictionary<int, StaffDto> _dstaff;
|
||||
private readonly ILookup _lookup;
|
||||
private string DisplayTime(DateTime date)
|
||||
{
|
||||
string result = "";
|
||||
if (date != DateTime.MinValue)
|
||||
result = date.ToString(TimeFormat);
|
||||
return result;
|
||||
}
|
||||
|
||||
private string DisplayDateTime(DateTime? date)
|
||||
{
|
||||
string result = "";
|
||||
if (date != DateTime.MinValue)
|
||||
result = date!.Value.ToString(DateFormat);
|
||||
return result;
|
||||
}
|
||||
private string FormatTime(int hour)
|
||||
{
|
||||
string result;
|
||||
if (hour < 10)
|
||||
result = "0" + hour.ToString() + ":00";
|
||||
else
|
||||
result = hour.ToString() + ":00";
|
||||
return result;
|
||||
}
|
||||
|
||||
public ReportRepository(FamilyTreeDBContext context, ILookup lookup,IStaff staff)
|
||||
{
|
||||
_context = context;
|
||||
_lookup = lookup;
|
||||
_staff = staff;
|
||||
}
|
||||
|
||||
// public async Task<ResultModel<FileContent>> GetStaffWorkReportAsync(StaffWorkCriteria criteria)
|
||||
// {
|
||||
// //var data = await _lookup.GetLookupDicAsync("TypeOfUse");
|
||||
// //if (data != null)
|
||||
// //{
|
||||
// // this._ServiceTypeDic = data.Data;
|
||||
// //}
|
||||
// var rstaff = await _staff.GetDicStaffs();
|
||||
// if (rstaff.StatusCode == 1)
|
||||
// _dstaff = rstaff.Data;
|
||||
// //data = await _lookup.GetLookupDicAsync("Status");
|
||||
// //if (data != null)
|
||||
// //{
|
||||
// // this._StatusDic = data.Data;
|
||||
// //}
|
||||
// int statusCode = 1;
|
||||
// int col = 1;
|
||||
// int row = 1;
|
||||
// SLDocument sl = new SLDocument();
|
||||
// SLStyle styleRed = sl.CreateStyle();
|
||||
// styleRed.SetFontColor(System.Drawing.Color.Red);
|
||||
|
||||
|
||||
|
||||
// //first sheetName
|
||||
// sl.RenameWorksheet(SLDocument.DefaultFirstSheetName, "Staff Work Report");
|
||||
// SLStyle style = sl.CreateStyle();
|
||||
// style.SetFontUnderline(DocumentFormat.OpenXml.Spreadsheet.UnderlineValues.Single);
|
||||
// style.SetFontBold(true);
|
||||
// style.SetFont("Calibri", 14);
|
||||
// sl.SetCellStyle(1, 3, style);
|
||||
// sl.SetRowHeight(1, 1, 25);
|
||||
// sl.SetCellValue(row++, 3, "Staff Work");
|
||||
// // sl.SetCellValue(row, 3, "From Date: " + fromDate.ToString(DateFormat) + " - " + toDate.ToString(DateFormat));
|
||||
// sl.SetCellValue(1, 6, " " + DateTime.Now.ToString("ddd dd/MM/yyyy HH:mm"));
|
||||
// // sl.SetCellValue(row, 6, "Hospital: " + facility);
|
||||
|
||||
|
||||
// int startTitleRow = 3;
|
||||
// row = startTitleRow;
|
||||
// //make the title bold
|
||||
// style = sl.CreateStyle();
|
||||
// style.Font.Bold = true;
|
||||
// sl.SetRowStyle(startTitleRow, style);
|
||||
// //set it height
|
||||
// sl.SetRowHeight(startTitleRow, startTitleRow, 25);
|
||||
// // add the title first
|
||||
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.Id, "Id");
|
||||
// sl.SetCellValue(row, (int) EnumStaffWork.FirstName, "FirstName");
|
||||
// sl.SetCellValue(row, (int) EnumStaffWork.LastName, "SurName");
|
||||
// sl.SetCellValue(row, (int) EnumStaffWork.StartDate, "StartDate");
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.startTime, "Start Time");
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.stopTime, "Stop Time");
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.Hour, "Hour");
|
||||
|
||||
// /*
|
||||
// style = sl.CreateStyle();
|
||||
// style.SetFontColor(System.Drawing.Color.Blue);
|
||||
// sl.SetRowStyle(row, style);
|
||||
// */
|
||||
// sl.FreezePanes(row, 0); //frozen the row.
|
||||
// //wrap text on is concession Expiry
|
||||
// style = sl.CreateStyle();
|
||||
// style.SetWrapText(true);
|
||||
// sl.SetColumnStyle(4, style);
|
||||
|
||||
// //format date
|
||||
|
||||
// // style = sl.CreateStyle();
|
||||
// //style.FormatCode = DateTimeFormat;
|
||||
// // sl.SetColumnStyle(1, style);
|
||||
// //make column width
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.Id, 10);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.FirstName, 30);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.LastName, 30);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.StartDate, 25);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.startTime, 10);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.stopTime, 10);
|
||||
// sl.SetColumnWidth((int)EnumStaffWork.Hour, 20);
|
||||
|
||||
|
||||
// style = sl.CreateStyle();
|
||||
// style.FormatCode = DateFormat;
|
||||
// sl.SetColumnStyle(5, style);
|
||||
// //style = sl.CreateStyle();
|
||||
// //style.FormatCode = DateTimeFormat;
|
||||
// //sl.SetColumnStyle(1, style);
|
||||
// sl.SetColumnWidth(10, 20);
|
||||
|
||||
// int startRowFrom = startTitleRow + 1;
|
||||
|
||||
//// DateTime ttdate = toDate.ToLocalTime(); //add 1 days so if to inclusive todate 02/09
|
||||
// var container = await _context.LoadStaffWorkAsync(criteria); //fromDate.ToLocalTime(), ttdate.AddDays(1), typeOfCall);
|
||||
// var list = container;
|
||||
// list.Sort((x, y) => x.StartDate.Value.CompareTo(y.StartDate.Value));
|
||||
// StaffWorkViewDto item;
|
||||
// DateTime startTime, stopTime;
|
||||
// double hour =0;
|
||||
// string fname, lname;
|
||||
// StaffWorkDetailDto detail;
|
||||
// for (int i = 0; i < list.Count; i++)
|
||||
// {
|
||||
|
||||
// item = list[i];
|
||||
// row = i + startRowFrom;
|
||||
// (startTime, stopTime ) = GetSSTime(item);
|
||||
// if (item.Details != null)
|
||||
// for (int j = 0; j < item.Details.Count; j++)
|
||||
// {
|
||||
// detail = item.Details[j];
|
||||
// hour += detail.TotalMinuts();
|
||||
// }
|
||||
// (fname, lname) = GetStaffName(item.StaffId.Value);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.StartDate, item.StartDate.Value);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.FirstName, fname);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.LastName, lname);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.startTime, startTime);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.stopTime, stopTime);
|
||||
// sl.SetCellValue(row, (int)EnumStaffWork.Hour, hour/60);
|
||||
|
||||
// }
|
||||
|
||||
// byte[] array;
|
||||
// // sl.SaveAs("C:\\Temp\\Report\\ConcessionValidationDate" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xlsx");
|
||||
// using (MemoryStream ws = new MemoryStream())
|
||||
// {
|
||||
// sl.SaveAs(ws);
|
||||
// array = ws.ToArray();
|
||||
// /*
|
||||
// using (FileStream fs = new FileStream("c:\\temp\\Report\\conReport.xlsx",FileMode.Create,FileAccess.Write))
|
||||
// {
|
||||
// ws.WriteTo(fs);
|
||||
// fs.Close();
|
||||
// }
|
||||
// */
|
||||
// ws.Close();
|
||||
// }
|
||||
|
||||
// var result = new FileContent
|
||||
// {
|
||||
// Content = array,
|
||||
// FileName = "StaffWork_Report" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm") + ".xlsx",
|
||||
// };
|
||||
|
||||
// return new ResultModel<FileContent>
|
||||
// {
|
||||
// Data = result,
|
||||
// StatusCode = statusCode
|
||||
// };
|
||||
|
||||
// }
|
||||
private (string, string) GetStaffName(int staffId)
|
||||
{
|
||||
|
||||
StaffDto staff;
|
||||
if (_dstaff.ContainsKey(staffId))
|
||||
{
|
||||
staff = _dstaff[staffId];
|
||||
return ( staff.Firstname ?? "" , staff.Lastname ?? "");
|
||||
}
|
||||
return ("","");
|
||||
}
|
||||
//private (DateTime firstTime, DateTime lastTime) GetSSTime(StaffWorkViewDto item)
|
||||
//{
|
||||
// DateTime sTime, lTime;
|
||||
// DateTime? value;
|
||||
// int count = 0;
|
||||
// sTime = lTime = DateTime.Now;
|
||||
// List<StaffWorkDetailDto> detail = item.Details ?? new();
|
||||
// count = detail.Count;
|
||||
// if (count > 0)
|
||||
// {
|
||||
|
||||
// value = detail[0].StartTime;
|
||||
// if (value.HasValue)
|
||||
// {
|
||||
// sTime = value.Value;
|
||||
// }
|
||||
// value = detail[count - 1].StopTime;
|
||||
// if (value.HasValue)
|
||||
// {
|
||||
// lTime = value.Value;
|
||||
// }
|
||||
// }
|
||||
// return (sTime, lTime);
|
||||
//}
|
||||
private string GetValue(string item, string text)
|
||||
{
|
||||
string result = item;
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
result = text;
|
||||
return result;
|
||||
}
|
||||
//private string GetPools(List<string> poolIds)
|
||||
//{
|
||||
// string result = "";
|
||||
// string id;
|
||||
// for (int i = 0; i < poolIds.Count; i++)
|
||||
// {
|
||||
// id = poolIds[i];
|
||||
// if (this._PoolDic.ContainsKey(id))
|
||||
// result += _PoolDic[id].Description + ",";
|
||||
// }
|
||||
// result = result.Remove(result.Length-1, 1);
|
||||
// return result;
|
||||
//}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
public class Seed
|
||||
{
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
public Seed(FamilyTreeDBContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public int InsertOneUser()
|
||||
{
|
||||
string sptext = "CREATE OR REPLACE FUNCTION public.usp_search_user( " +
|
||||
" iemail character varying,ifirstname character varying,ilastname character varying) " +
|
||||
" RETURNS TABLE(id integer, fistname character varying, lastname character varying, email character varying," +
|
||||
" stype integer, sactive boolean, srole integer, spassword character varying) " +
|
||||
" AS $$ BEGIN " +
|
||||
" return query SELECT e.id, e.firstname, e.lastname, e.email, e.stype," +
|
||||
" e.sactive, e.srole, e.spassword " +
|
||||
" FROM public.staff e " +
|
||||
" WHERE (iemail = '' or e.email ilike iemail || '%') " +
|
||||
" and (ilastname = '' or e.lastname ilike ilastname || '%') " +
|
||||
" and (ifirstname = '' or e.firstname ilike ifirstname || '%'); "+
|
||||
"END; " +
|
||||
" $$ " +
|
||||
" LANGUAGE 'plpgsql'; ";
|
||||
|
||||
|
||||
int id = -1;
|
||||
//password = password
|
||||
string txt = " INSERT INTO staff ( " +
|
||||
"firstname, lastname, email, phone, stype, srole, spassword, sactive) " +
|
||||
"VALUES" +
|
||||
" ( 'kham', 'vilaythong', 'kham.vilaythong@gmail.com', '009', 1, 2, 'cGFzc3dvcmQ=', true), " +
|
||||
" ( 'sy', 'vilaythong', 'sy.vilaythong@gmail.com', '007', 1, 2, 'cGFzc3dvcmQ=', true), " +
|
||||
" ( 'Hung', 'Nguyen', 'hung.gnuyen@gmail.com', '008', 1, 2, 'cGFzc3dvcmQ=', true); ";
|
||||
|
||||
string workertxt = "INSERT INTO person ( firstname, lastname,email,phone,address,dob ,alive, fatherId, image, sex) " +
|
||||
" VALUES " +
|
||||
" ('Ho 1','Tran', 'Ho.Tran@hotmail.com', '002', '1 Cabramatta','1960-09-01', true, 0,'', 'M'), " +
|
||||
" ('Jimmy 2','Tran', 'Ho.Tran@hotmail.com', '003', '34 Cabramatta','1980-09-01', true, 1,'','M'), " +
|
||||
" ('Joe 3','Tran', 'Joe.Tran@hotmail.com', '006', '32 Cabramatta','1980-10-01', true, 1,'','M'), " +
|
||||
" ('John 4','Tran', 'John.Tran@hotmail.com', '008', '30 Cabramatta','1990-12-01', true, 3,'','M')," +
|
||||
" ('Lee 5','Tran', 'Lee.Tran@hotmail.com', '030', '18 Cabramatta','2000-12-01', true, 2,'','M'), " +
|
||||
" ('Len 6','Nguyen', 'Len.nguyen@hotmail.com', '001', '1 Home','1950-01-01', true, 0,'','M'), " +
|
||||
" ('Son 7','Nguyen', 'son.Nguyen@hotmail.com', '001', '10 Home','1980-01-01', true,6,'','M'), " +
|
||||
" ('Loa 8','Tran', 'Loa.Tran@hotmail.com', '020', '11 Cabramatta','2002-12-01', true, 3,'','M'), " +
|
||||
" ('Du 9','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 4,'','M'), " +
|
||||
" ('Linh 10','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 5,'','M'), " +
|
||||
" ('COA 11','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','M'), " +
|
||||
" ('Hoa 12','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " +
|
||||
" ('Nhi 13','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " +
|
||||
" ('Mai 14','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " +
|
||||
" ('Trang 15','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 7,'','F'), " +
|
||||
" ('Tom 16','Tran', 'Ho.Tran@hotmail.com', '007', '34 Cabramatta','1999-01-01', true, 2,'','M');";
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
int count = _context.staff.Count();
|
||||
if (count < 1)
|
||||
{
|
||||
var conn = _context.Database.GetDbConnection();
|
||||
try
|
||||
{
|
||||
|
||||
conn.Open();
|
||||
var command = conn.CreateCommand();
|
||||
command.CommandText = sptext;
|
||||
command.CommandType = System.Data.CommandType.Text;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command = conn.CreateCommand();
|
||||
command.CommandText = txt;
|
||||
command.CommandType = System.Data.CommandType.Text;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = workertxt;
|
||||
command.CommandType = System.Data.CommandType.Text;
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
id = 9;
|
||||
}
|
||||
catch
|
||||
{
|
||||
id = -1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
id = 1;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
id = -10;
|
||||
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using FamilyTreeAPI.Models;
|
||||
using FamilyTreeAPI.Entities;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
|
||||
/* password = cGFzc3dvcmQ=
|
||||
*
|
||||
INSERT INTO public.staff(
|
||||
firstname, lastname, email, phone, stype, srole, spassword, sactive)
|
||||
VALUES ( 'kham', 'vilaythong', 'kham.vilaythong', '009', 1, 2, 'cGFzc3dvcmQ=', true);
|
||||
*/
|
||||
public class StaffRepository : IStaff
|
||||
{
|
||||
private readonly FamilyTreeDBContext _context;
|
||||
private readonly IHttpContextAccessor _httpContext;
|
||||
|
||||
|
||||
public StaffRepository(FamilyTreeDBContext context, IHttpContextAccessor httpContext)
|
||||
{
|
||||
_context = context;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
private StaffDto FillDto(staff model)
|
||||
{
|
||||
StaffDto dto = new StaffDto();
|
||||
dto.Firstname = model.Firstname;
|
||||
dto.Lastname = model.Lastname;
|
||||
dto.Type = model.Stype;
|
||||
dto.Phone = model.Phone;
|
||||
dto.Active = model.Sactive ?? false;
|
||||
dto.Email = model.Email;
|
||||
/*
|
||||
dto.AddedOn = model.AddedOn;
|
||||
|
||||
dto.RoleType = model.RoleType;
|
||||
|
||||
*/
|
||||
dto.RoleType = model.Srole;
|
||||
dto.Id = model.Id;
|
||||
return dto;
|
||||
}
|
||||
private staff FillModel(staff model, StaffDto dto)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dto.Firstname))
|
||||
model.Firstname = dto.Firstname.Trim();
|
||||
if (!string.IsNullOrEmpty(dto.Lastname))
|
||||
model.Lastname = dto.Lastname.Trim();
|
||||
if (!string.IsNullOrEmpty(dto.Phone))
|
||||
model.Phone = dto.Phone.Trim();
|
||||
|
||||
if (dto.Id > 0)
|
||||
model.Id = dto.Id;
|
||||
|
||||
if (!string.IsNullOrEmpty(dto.Password))
|
||||
{
|
||||
string password = dto.Password.Trim();
|
||||
model.Spassword = Ultils.Base64Encode(password);
|
||||
}
|
||||
|
||||
model.Email = dto.Email;
|
||||
model.Srole = dto.RoleType;
|
||||
model.Sactive = dto.Active;
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
public async Task<ResultModel<Dictionary<int,StaffDto>>> GetDicStaffs()
|
||||
{
|
||||
Dictionary<int,StaffDto> dlist = new();
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
StaffDto item;
|
||||
staff model;
|
||||
try
|
||||
{
|
||||
var list = await _context.staff.ToListAsync();
|
||||
for (int i = 0; i< list.Count; i++)
|
||||
{
|
||||
model = list[i];
|
||||
item = FillDto(model);
|
||||
dlist.Add(item.Id, item);
|
||||
}
|
||||
statuscode = 1;
|
||||
//list.Sort((x, y) => x.Code.CompareTo(y.Code));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<Dictionary<int,StaffDto>>()
|
||||
{
|
||||
Data = dlist,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<List<StaffDto> >> GetStaff(StaffCriteria criteria)
|
||||
{
|
||||
List<StaffDto> list = new();
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
list = await _context.LoadStaffAsync(criteria);
|
||||
statuscode = 1;
|
||||
//list.Sort((x, y) => x.Code.CompareTo(y.Code));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<List<StaffDto>>()
|
||||
{
|
||||
Data = list,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ResultModel<StaffDto>> GetStaffById(int id)
|
||||
{
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
StaffDto dto = new StaffDto();
|
||||
try
|
||||
{
|
||||
var item = await _context.staff.FindAsync(id);
|
||||
if (item == null)
|
||||
{
|
||||
statuscode = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dto = FillDto(item);
|
||||
statuscode = 1;
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
return new ResultModel<StaffDto>()
|
||||
{
|
||||
Data = dto,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
private string GetDateTimeNow()
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
public async Task<ResultModel<int>> ResetPassword(ResetPassDto item)
|
||||
{
|
||||
int result = -1;
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
staff? model1 = await _context.staff.FindAsync(item.Id);
|
||||
if (model1 != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.Password))
|
||||
{
|
||||
string password = item.Password.Trim();
|
||||
model1.Spassword = Ultils.Base64Encode(password);
|
||||
}
|
||||
|
||||
// model.LastModified = DateTime.Now;
|
||||
// model.LastModifiedId = loginName;
|
||||
var successid = await _context.SaveChangesAsync();
|
||||
result = item.Id;
|
||||
}
|
||||
statuscode = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statuscode = -1;
|
||||
error = ex.ToString();
|
||||
}
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
public async Task<ResultModel<int>> SaveStaff(StaffDto item)
|
||||
{
|
||||
int result = default(int);
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
|
||||
HttpContext? httpContext = _httpContext.HttpContext;
|
||||
string loginName = "";
|
||||
if (httpContext != null)
|
||||
{
|
||||
UserDto? user = (UserDto?)httpContext.Items["User"];
|
||||
if (user != null)
|
||||
loginName = user.FirstName + " " + user.LastName;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (item.Id < 1)
|
||||
{
|
||||
bool already = await CheckLoginAlready(item.Email.Trim());
|
||||
if (already)
|
||||
{
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = 0,
|
||||
StatusCode = 0,
|
||||
Message = "user name already exist in Database"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
staff model;
|
||||
if (item.Id < 1)
|
||||
{
|
||||
model = new();
|
||||
model = FillModel(model, item);
|
||||
|
||||
// model.Active = true;
|
||||
// model.AddedBy = loginName;
|
||||
// model.AddedOn = DateTime.Now;
|
||||
_context.staff.Add(model);
|
||||
await _context.SaveChangesAsync();
|
||||
result = model.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
staff? model1 = await _context.staff.FindAsync(item.Id);
|
||||
model1 = FillModel(model1, item);
|
||||
// model.LastModified = DateTime.Now;
|
||||
// model.LastModifiedId = loginName;
|
||||
var successid = await _context.SaveChangesAsync();
|
||||
result = item.Id;
|
||||
}
|
||||
|
||||
|
||||
statuscode = 1;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//var dto = await Task.Run(() => result);
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
private async Task<bool> CheckLoginAlready(string login)
|
||||
{
|
||||
bool result = false;
|
||||
var model = await _context.staff.Where(x => x.Email == login).ToListAsync();
|
||||
if (model.Count > 0)
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
public async Task<ResultModel<int>> SaveStaffNew(StaffDto item)
|
||||
{
|
||||
int result = default(int);
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
|
||||
try
|
||||
{
|
||||
if (item.Id < 1)
|
||||
{
|
||||
bool already = await CheckLoginAlready(item.Email.Trim());
|
||||
if (already)
|
||||
{
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = 0,
|
||||
StatusCode = 0,
|
||||
Message = "user name already exist in db"
|
||||
};
|
||||
}
|
||||
}
|
||||
staff model;
|
||||
if (item.Id < 1)
|
||||
{
|
||||
model = new();
|
||||
model = FillModel(model, item);
|
||||
// model.AddedOn = DateTime.Now;
|
||||
|
||||
// model.Active = true;
|
||||
_context.staff.Add(model);
|
||||
}
|
||||
else
|
||||
{
|
||||
model = await _context.staff.FindAsync(item.Id);
|
||||
model = FillModel(model, item);
|
||||
}
|
||||
var successid = await _context.SaveChangesAsync();
|
||||
|
||||
result = 1;
|
||||
statuscode = 1;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//var dto = await Task.Run(() => result);
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<ResultModel<int>> Delete(int id)
|
||||
{
|
||||
int result = 1;
|
||||
int statuscode = 0;
|
||||
string error = "";
|
||||
try
|
||||
{
|
||||
staff model;
|
||||
model = await _context.staff.FindAsync(id);
|
||||
// if (model != null)
|
||||
// model.Active = false;
|
||||
|
||||
var successCount = await _context.SaveChangesAsync();
|
||||
statuscode = 1;
|
||||
result = 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//var dto = await Task.Run(() => result);
|
||||
return new ResultModel<int>()
|
||||
{
|
||||
Data = result,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/**************************************/
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.client
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
clientname character(80) COLLATE pg_catalog."default",
|
||||
contact character(80) COLLATE pg_catalog."default",
|
||||
cactive boolean,
|
||||
address character(120),
|
||||
email character(80),
|
||||
phone character(30),
|
||||
CONSTRAINT client_pkey PRIMARY KEY (id)
|
||||
)
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.client
|
||||
OWNER to postgres;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.lookup
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
code character(10) COLLATE pg_catalog."default",
|
||||
description character(80) COLLATE pg_catalog."default",
|
||||
lactive boolean,
|
||||
ltype character(20) COLLATE pg_catalog."default",
|
||||
CONSTRAINT lookup_pkey PRIMARY KEY (id)
|
||||
)
|
||||
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.lookup
|
||||
OWNER to postgres;
|
||||
|
||||
|
||||
-- Table: public.job
|
||||
|
||||
-- DROP TABLE IF EXISTS public.job;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.job
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
code character(10) COLLATE pg_catalog."default",
|
||||
description character(80) COLLATE pg_catalog."default",
|
||||
jactive boolean,
|
||||
CONSTRAINT job_pkey PRIMARY KEY (id)
|
||||
)
|
||||
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.job
|
||||
OWNER to postgres;
|
||||
|
||||
-- Table: public.servicetask
|
||||
|
||||
-- DROP TABLE IF EXISTS public.servicetask;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.servicetask
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
assignto integer,
|
||||
clientid integer,
|
||||
code character(10) COLLATE pg_catalog."default",
|
||||
description character(80) COLLATE pg_catalog."default",
|
||||
sdate date,
|
||||
serviceno character(20) COLLATE pg_catalog."default",
|
||||
staffid integer,
|
||||
status integer,
|
||||
CONSTRAINT servicetask_pkey PRIMARY KEY (id)
|
||||
)
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.servicetask
|
||||
OWNER to postgres;
|
||||
|
||||
|
||||
-- Table: public.staffwork
|
||||
|
||||
-- DROP TABLE IF EXISTS public.staffwork;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.staffwork
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
description character(80) COLLATE pg_catalog."default",
|
||||
sactive boolean,
|
||||
staffid integer,
|
||||
startdate date,
|
||||
CONSTRAINT staffwork_pkey PRIMARY KEY (id)
|
||||
)
|
||||
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.staffwork
|
||||
OWNER to postgres;
|
||||
|
||||
-- Table: public.staffworkdetail
|
||||
|
||||
-- DROP TABLE IF EXISTS public.staffworkdetail;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.staffworkdetail
|
||||
(
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||
jobid integer,
|
||||
sactive boolean,
|
||||
starttime timestamp without time zone,
|
||||
stoptime timestamp without time zone,
|
||||
task character(80) COLLATE pg_catalog."default",
|
||||
swid integer,
|
||||
CONSTRAINT staffworkdetail_pkey PRIMARY KEY (id)
|
||||
)
|
||||
|
||||
TABLESPACE pg_default;
|
||||
|
||||
ALTER TABLE IF EXISTS public.staffworkdetail
|
||||
OWNER to postgres;
|
||||
|
||||
/******************************************/
|
||||
ALTER TABLE servicetask
|
||||
ADD FOREIGN KEY (clientid) REFERENCES client(id);
|
||||
|
||||
ALTER TABLE servicetask
|
||||
ADD FOREIGN KEY (staffid) REFERENCES staff(id);
|
||||
|
||||
ALTER TABLE servicetask
|
||||
ADD FOREIGN KEY (assignto) REFERENCES staff(id);
|
||||
|
||||
ALTER TABLE staffwork
|
||||
ADD FOREIGN KEY (staffid) REFERENCES staff(id);
|
||||
|
||||
ALTER TABLE staffworkdetail
|
||||
ADD FOREIGN KEY (swid) REFERENCES staffwork(id);
|
||||
|
||||
ALTER TABLE staffworkdetail
|
||||
ADD FOREIGN KEY (jobid) REFERENCES job(id);
|
||||
|
||||
/************************************/
|
||||
|
||||
CREATE OR REPLACE FUNCTION usp_search_user (
|
||||
iemail character varying,
|
||||
ifirstname character varying,
|
||||
ilastname character varying)
|
||||
RETURNS TABLE(id integer, fistname character varying, lastname character varying, email character varying, stype integer, sactive boolean, srole integer, spassword character varying)
|
||||
LANGUAGE 'plpgsql'
|
||||
COST 100
|
||||
VOLATILE PARALLEL UNSAFE
|
||||
ROWS 1000
|
||||
|
||||
AS $BODY$
|
||||
begin
|
||||
return query SELECT
|
||||
e.id,
|
||||
e.firstname,
|
||||
e.lastname,
|
||||
e.email,
|
||||
e.stype,
|
||||
e.sactive,
|
||||
e.srole,
|
||||
e.spassword
|
||||
|
||||
|
||||
|
||||
FROM staff e
|
||||
WHERE (iemail = '' or e.email ilike iemail || '%')
|
||||
and (ilastname = '' or e.lastname ilike ilastname || '%')
|
||||
and (ifirstname = '' or e.firstname ilike ifirstname || '%');
|
||||
|
||||
end;
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION usp_staffwork_detail(
|
||||
ifromdate date,
|
||||
itodate date,
|
||||
istaffid integer)
|
||||
RETURNS TABLE(id integer, staffid integer, description character varying(80),
|
||||
startdate date, sactive boolean, did integer, starttime timestamp, stoptime timestamp,
|
||||
task character varying(80))
|
||||
LANGUAGE 'plpgsql'
|
||||
COST 100
|
||||
VOLATILE PARALLEL UNSAFE
|
||||
ROWS 1000
|
||||
|
||||
AS $BODY$
|
||||
begin
|
||||
return query SELECT
|
||||
w.id,
|
||||
w.staffid,
|
||||
w.description,
|
||||
w.startdate,
|
||||
w.sactive,
|
||||
d.id as did,
|
||||
d.starttime,
|
||||
d.stoptime,
|
||||
d.task
|
||||
|
||||
FROM staffwork w,
|
||||
staffworkdetail d
|
||||
where w.id = d.swid
|
||||
and (istaffid = 0 or w.staffid = istaffid)
|
||||
and w.startdate >= ifromdate
|
||||
and w.startdate <= itodate
|
||||
order by id, did;
|
||||
|
||||
|
||||
end;
|
||||
$BODY$;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION usp_servicetask_sel(
|
||||
ifromdate date,
|
||||
itodate date,
|
||||
istaffid integer,
|
||||
iassignto integer,
|
||||
iclientid integer
|
||||
)
|
||||
RETURNS TABLE(id integer,
|
||||
staffid integer,
|
||||
sdate date,
|
||||
description character varying(80),
|
||||
code character varying(10),
|
||||
status integer,
|
||||
assignTo integer,
|
||||
clientid integer,
|
||||
serviceno character varying(20))
|
||||
LANGUAGE 'plpgsql'
|
||||
COST 100
|
||||
VOLATILE PARALLEL UNSAFE
|
||||
ROWS 1000
|
||||
|
||||
|
||||
AS $BODY$
|
||||
begin
|
||||
return query SELECT
|
||||
s.id,
|
||||
s.staffid,
|
||||
s.sdate,
|
||||
s.description,
|
||||
s.code,
|
||||
s.status,
|
||||
s.assignto,
|
||||
s.clientid,
|
||||
s."serviceNo"
|
||||
FROM servicetask s
|
||||
|
||||
where (istaffid < 1 or s.staffid = istaffid)
|
||||
and (iassignto < 1 or s.assignto = iassignto)
|
||||
and (iclientid < 1 or s.clientid = iclientid)
|
||||
and s.sdate >= ifromdate
|
||||
and s.sdate <= itodate
|
||||
|
||||
;
|
||||
end;
|
||||
$BODY$;
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace FamilyTreeAPI.Repository
|
||||
{
|
||||
public class Ultils
|
||||
{
|
||||
public static string Base64Encode(string plainText)
|
||||
{
|
||||
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
|
||||
return System.Convert.ToBase64String(plainTextBytes);
|
||||
}
|
||||
public static string Base64Decode(string base64EncodedData)
|
||||
{
|
||||
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
|
||||
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
//using BCryptNet = BCrypt.Net.BCrypt;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using FamilyTreeAPI.Interface;
|
||||
using FamilyTreeAPI.Entities;
|
||||
|
||||
using System;
|
||||
using FamilyTreeAPI.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
|
||||
namespace FamilyTreeAPI.Repository;
|
||||
public class UserServiceRepository : IUserService
|
||||
{
|
||||
private FamilyTreeDBContext _context;
|
||||
private IJwtUtils _jwtUtils;
|
||||
private readonly IHttpContextAccessor _httpcontext;
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public UserServiceRepository(
|
||||
FamilyTreeDBContext context,
|
||||
IJwtUtils jwtUtils,
|
||||
IHttpContextAccessor httpcontext,
|
||||
IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_context = context;
|
||||
_jwtUtils = jwtUtils;
|
||||
_appSettings = appSettings.Value;
|
||||
this._httpcontext = httpcontext;
|
||||
|
||||
}
|
||||
public Task<ResultModel<int>> Logout(string token, UserDto user, string remoteIpAddress)
|
||||
{
|
||||
/*
|
||||
int retval = 1;
|
||||
int statuscode = 1;
|
||||
string str255 = token;
|
||||
var histo = await _context.AdminLoginHistories.Where(x => x.UserId == user.Id
|
||||
&& x.RecordIntegrity == token
|
||||
&& x.LoginTyped == user.Username).ToListAsync();
|
||||
if (histo.Count > 0)
|
||||
{
|
||||
histo[0].LogoutDatetime = DateTime.Now;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
*/
|
||||
var result = new ResultModel<int>()
|
||||
{
|
||||
Data = 1,
|
||||
StatusCode = 1
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
|
||||
}
|
||||
private bool checkLogin(staff user, string email, string password)
|
||||
{
|
||||
bool result = false;
|
||||
// string dpassword = Ultils.Base64Encode(password);
|
||||
result = user.Spassword == password;
|
||||
return result;
|
||||
}
|
||||
public async Task<ResultModel<AuthenticateResponse>> Login(AuthenticateRequest model)
|
||||
{
|
||||
UserDto myUser = new();
|
||||
AuthenticateResponse retval = null;
|
||||
string error = "user name is not in DB or user Name profile is not generic";
|
||||
int statuscode = 0;
|
||||
|
||||
try
|
||||
{
|
||||
//now check the adminuser table
|
||||
myUser.Username = model.Username;
|
||||
var user = _context.staff.
|
||||
SingleOrDefault(x => x.Email == model.Username
|
||||
&& true == x.Sactive);
|
||||
if (user != null)
|
||||
{
|
||||
bool loginOK = checkLogin(user, model.Username, model.Password);
|
||||
if (loginOK)
|
||||
{
|
||||
|
||||
|
||||
// myUser.Role = user.RoleType;
|
||||
myUser.Id = user.Id;
|
||||
myUser.Role = user.Srole ?? 0;
|
||||
myUser.Email = user.Email;
|
||||
myUser.Phone = user.Phone;
|
||||
myUser.FirstName = user.Firstname;
|
||||
myUser.LastName = user.Lastname;
|
||||
//myUser.ValidationPointId = user.ValidationPointID ?? 0;
|
||||
statuscode = 1;
|
||||
}
|
||||
else //not allow
|
||||
{
|
||||
statuscode = -1;
|
||||
myUser.Role = 1;
|
||||
myUser.Id = -1;
|
||||
|
||||
error = "user name cannot login email or password";
|
||||
}
|
||||
// validate
|
||||
// if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash))
|
||||
|
||||
// authentication successful so generate jwt token
|
||||
if (statuscode == 1)
|
||||
{
|
||||
var jwtToken = _jwtUtils.GenerateJwtToken(myUser);
|
||||
retval = new AuthenticateResponse(myUser, jwtToken, myUser.Role);
|
||||
error = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "user name or password is not correct";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
retval = null;
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//writelog for login user.
|
||||
// if (myUser != null)
|
||||
// await AddToSession(myUser);
|
||||
return new ResultModel<AuthenticateResponse>()
|
||||
{
|
||||
Data = retval,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
|
||||
}
|
||||
public async Task<ResultModel<AuthenticateResponse>> LoginApiAD(AuthenticateRequest model, string remoteIpAddress)
|
||||
{
|
||||
UserDto myUser = new();
|
||||
AuthenticateResponse retval = null;
|
||||
string error = "user name is not in DB or user Name profile is not generic";
|
||||
int statuscode = 0;
|
||||
string webAPIUrl = _appSettings.LoginWebAPI;
|
||||
//KCO, D204KCO
|
||||
//now check the adminuser table
|
||||
//ward clerk
|
||||
try
|
||||
{
|
||||
//now check the adminuser table
|
||||
myUser.Username = model.Username;
|
||||
var user = _context.staff.
|
||||
SingleOrDefault(x => x.Email == model.Username
|
||||
&& true == x.Sactive);
|
||||
if (user != null)
|
||||
{
|
||||
bool loginOK = checkLogin(user,model.Username, model.Password);
|
||||
if (loginOK)
|
||||
{
|
||||
// myUser.Role = user.RoleType;
|
||||
myUser.Id = user.Id;
|
||||
myUser.Role = user.Srole ?? 0;
|
||||
myUser.Email = user.Email;
|
||||
myUser.Phone = user.Phone;
|
||||
myUser.FirstName = user.Firstname;
|
||||
myUser.LastName = user.Lastname;
|
||||
|
||||
//myUser.ValidationPointId = user.ValidationPointID ?? 0;
|
||||
statuscode = 1;
|
||||
}
|
||||
else // allow
|
||||
{
|
||||
statuscode = 1;
|
||||
myUser.Role = 1;
|
||||
myUser.Id = -1;
|
||||
|
||||
//error = "user name does not exist in adminUser";
|
||||
}
|
||||
// validate
|
||||
// if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash))
|
||||
|
||||
// authentication successful so generate jwt token
|
||||
if (statuscode == 1)
|
||||
{
|
||||
var jwtToken = _jwtUtils.GenerateJwtToken(myUser);
|
||||
retval = new AuthenticateResponse(myUser, jwtToken, myUser.Role);
|
||||
error = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "user name or password is not correct";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
retval = null;
|
||||
error = ex.ToString();
|
||||
statuscode = -1;
|
||||
|
||||
}
|
||||
//writelog for login user.
|
||||
// if (myUser != null)
|
||||
// await AddToSession(myUser);
|
||||
return new ResultModel<AuthenticateResponse>()
|
||||
{
|
||||
Data = retval,
|
||||
StatusCode = statuscode,
|
||||
Message = error
|
||||
};
|
||||
|
||||
}
|
||||
//get like this
|
||||
private async Task GetCurrentUser()
|
||||
{
|
||||
await _httpcontext.HttpContext.Session.LoadAsync();
|
||||
|
||||
string userString = _httpcontext.HttpContext.Session.GetString("user");
|
||||
|
||||
if (userString != null && userString != "")
|
||||
{
|
||||
var user = JsonSerializer.Deserialize<UserDto>(userString);
|
||||
if (user != null)
|
||||
{
|
||||
// return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
private User LoginADStaff(ADConfig adConfig, string username, string password)
|
||||
{
|
||||
|
||||
ADStaffLink staffLink = new ADStaffLink(adConfig);
|
||||
MyADObject myADObj = staffLink.CheckADCredentials(username,password);
|
||||
User user = new()
|
||||
{
|
||||
Username = myADObj.StafflinkNo,
|
||||
Email = myADObj.Email,
|
||||
FirstName = myADObj.FirstName,
|
||||
LastName = myADObj.LastName
|
||||
};
|
||||
// myADObj.JobTitle;
|
||||
return user;
|
||||
|
||||
// return null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
declare @id int
|
||||
set @id = 7
|
||||
delete from MotorAccess where mv_id = @id
|
||||
delete from [dbo].[MotorVehicle]
|
||||
where mv_id = @id
|
||||
*/
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usp_search_user(
|
||||
iemail character varying,
|
||||
ifirstname character varying,
|
||||
ilastname character varying)
|
||||
RETURNS TABLE(id integer, fistname character varying, lastname character varying, email character varying,
|
||||
stype integer, sactive boolean, srole integer, spassword character varying)
|
||||
|
||||
AS $BODY$
|
||||
begin
|
||||
return query SELECT
|
||||
e.id,
|
||||
e.firstname,
|
||||
e.lastname,
|
||||
e.email,
|
||||
e.stype,
|
||||
e.sactive,
|
||||
e.srole,
|
||||
e.spassword
|
||||
|
||||
|
||||
|
||||
FROM staff e
|
||||
WHERE (iemail = '' or e.email ilike iemail || '%')
|
||||
and (ilastname = '' or e.lastname ilike ilastname || '%')
|
||||
and (ifirstname = '' or e.firstname ilike ifirstname || '%');
|
||||
|
||||
end;
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION get_staff (
|
||||
iemail character varying,
|
||||
ifirstname character varying,
|
||||
ilastname character varying
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id int,
|
||||
firstname varchar,
|
||||
lastname varchar,
|
||||
email varchar,
|
||||
stype INT,
|
||||
sactive boolean,
|
||||
srole INT,
|
||||
spassword varchar )
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
e.id,
|
||||
e.firstname,
|
||||
e.lastname,
|
||||
e.email,
|
||||
e.stype,
|
||||
e.sactive,
|
||||
e.srole,
|
||||
e.spassword
|
||||
FROM staff e
|
||||
WHERE (iemail = '' or e.email ilike iemail || '%')
|
||||
and (ilastname = '' or e.lastname ilike ilastname || '%')
|
||||
and (ifirstname = '' or e.firstname ilike ifirstname || '%');
|
||||
END; $$
|
||||
|
||||
LANGUAGE 'plpgsql';
|
||||
|
||||
|
||||
how to host under .net 8 web api under linux
|
||||
|
||||
1) create [webAPI_name].service
|
||||
******************************************
|
||||
# Example: /etc/systemd/system/yourwebapp.service
|
||||
[Unit]
|
||||
Description=Your .NET 8 Web API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/www/yourwebapp
|
||||
ExecStart=/usr/bin/dotnet /var/www/yourwebapp/YourWebApi.dll
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
SyslogIdentifier=yourwebapp
|
||||
User=www-data # or a dedicated user
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
************************************************
|
||||
2) start /stop the service under linux
|
||||
***********************************************
|
||||
sudo systemctl enable yourwebapp.service
|
||||
sudo systemctl start yourwebapp.service
|
||||
**********************************************
|
||||
|
||||
3) host under nginx or apache
|
||||
************************************************
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5000; # Kestrel's listening port
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
***********************************************************************
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
|
||||
"AppSettings": {
|
||||
"Secret": "Nepean Blue Mountain Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.",
|
||||
"SQLConnectionString": "host=localhost;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;",
|
||||
"SQLConnectionString_25": "host=192.168.1.100;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;",
|
||||
|
||||
"ImageFolder": "c:\\temp\\Family",
|
||||
"ImportFolder": "c:\\temp"
|
||||
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.AspNetCore.DataProtection": "None"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel_not_use": {
|
||||
"Endpoints": {
|
||||
"Http": {
|
||||
"Url": "http://*:5015"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=ASP.NET Core Web App running on Ubuntu
|
||||
[Service]
|
||||
WorkingDirectory=/var/www/familytree/api
|
||||
ExecStart=/usr/bin/dotnet /var/www/fadmilytree/api/FamilyTreeAPI.dll
|
||||
Restart=always
|
||||
# Restart service after 10 seconds if the dotnet service crashes:
|
||||
RestartSec=10
|
||||
KillSignal=SIGINT
|
||||
SyslogIdentifier=dotnet-web-familytree-api
|
||||
# This user should exist on the server and have ownership of the deployment directory
|
||||
User=www-data
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectVersion>2.1</ProjectVersion>
|
||||
<DockerTargetOS>Linux</DockerTargetOS>
|
||||
<DockerPublishLocally>False</DockerPublishLocally>
|
||||
<ProjectGuid>81dded9d-158b-e303-5f62-77a2896d2a5a</ProjectGuid>
|
||||
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
|
||||
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}/swagger</DockerServiceUrl>
|
||||
<DockerServiceName>workapi</DockerServiceName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="docker-compose.override.yml">
|
||||
<DependentUpon>docker-compose.yml</DependentUpon>
|
||||
</None>
|
||||
<None Include="docker-compose.yml" />
|
||||
<None Include=".dockerignore" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
services:
|
||||
workapi:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_HTTP_PORTS=8080
|
||||
ports:
|
||||
- "8080"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
networks:
|
||||
dev:
|
||||
driver: bridge
|
||||
services:
|
||||
postgresdb:
|
||||
image: postgres
|
||||
container_name: ${DOCKER_REGISTRY-}postgresql-db
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_DB=FamilyTreeDB
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=Positive~1
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
restart: always
|
||||
volumes:
|
||||
- postgresdata:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dev
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4
|
||||
container_name: pgadmin4_container
|
||||
restart: always
|
||||
depends_on:
|
||||
- "postgresdb"
|
||||
volumes:
|
||||
- pgadmin-data:/var/lib/pgadmin
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: postgres@domain.com
|
||||
PGADMIN_DEFAULT_PASSWORD: Positive~1
|
||||
ports:
|
||||
- "5050:5050"
|
||||
networks:
|
||||
- dev
|
||||
familytreeui:
|
||||
image: ${DOCKER_REGISTRY-}familytreeui
|
||||
depends_on:
|
||||
- "familytreeapi"
|
||||
ports:
|
||||
- "4200:80"
|
||||
build:
|
||||
context: UI
|
||||
dockerfile: Dockerfile
|
||||
container_name: familytreeui
|
||||
volumes:
|
||||
- ./:/app
|
||||
networks:
|
||||
- dev
|
||||
familytreeapi:
|
||||
image: ${DOCKER_REGISTRY-}familytreeapi
|
||||
container_name: familytreeapi-services
|
||||
depends_on:
|
||||
- "postgresdb"
|
||||
environment:
|
||||
- "Logging:Microsoft.AspNetCore.DataProtection:None"
|
||||
- "AppSettings:SQLConnectionString=host=postgresdb;database=FamilyTreeDB;username=postgres;password=Positive~1"
|
||||
volumes:
|
||||
- app_import:/home/kham/dvolumes/app_import
|
||||
- app_images:/home/kham/dvolumes/app_images
|
||||
ports:
|
||||
- "8080:8080"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: FamilyTreeAPI/Dockerfile
|
||||
networks:
|
||||
- dev
|
||||
volumes:
|
||||
myapikey:
|
||||
external: true
|
||||
pgadmin-data:
|
||||
external: true
|
||||
postgresdata:
|
||||
external: true
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Docker Compose": {
|
||||
"commandName": "DockerCompose",
|
||||
"commandVersion": "1.0",
|
||||
"serviceActions": {
|
||||
"workapi": "StartDebugging"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": {
|
||||
"@tailwindcss/postcss": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
FROM node:24.0.0 AS build
|
||||
RUN mkdir -p /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY ["./*.json", "./"]
|
||||
|
||||
RUN npm install -g @angular/cli
|
||||
|
||||
COPY . .
|
||||
# Run command in Virtual directory
|
||||
RUN npm cache clean --force
|
||||
|
||||
RUN npm install
|
||||
|
||||
#RUN ng build --configuration=production
|
||||
RUN ng build -c=production
|
||||
|
||||
FROM nginx:latest
|
||||
#### copy nginx conf
|
||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
#COPY --from=build app/dist/Familytree/browser /usr/share/nginx/html
|
||||
#### copy artifact build from the 'build environment' old is not in browser folder
|
||||
COPY --from=build /app/dist/Familytree/browser /usr/share/nginx/html
|
||||
|
||||
#CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
#docker build -t my_angular_app:latest .
|
||||
#docker run --name carval -d -p 4200:80 varlidate_ui
|
||||
#npm error code ENOENT
|
||||
@@ -0,0 +1,59 @@
|
||||
# FamilyTree
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.1.1.
|
||||
|
||||
## Development server
|
||||
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"FamilyTree": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"allowedCommonJsDependencies": [
|
||||
"moment", "file-saver", "xlsx"
|
||||
],
|
||||
"browser": "src/main.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "5MB",
|
||||
"maximumError": "6MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "FamilyTree:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "FamilyTree:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular/build:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
server {
|
||||
listen 80;
|
||||
sendfile on;
|
||||
default_type application/octet-stream;
|
||||
|
||||
gzip on;
|
||||
gzip_http_version 1.1;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
gzip_min_length 256;
|
||||
gzip_vary on;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
gzip_comp_level 9;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "family-tree",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"prettier": {
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^20.1.7",
|
||||
"@angular/compiler": "^20.1.7",
|
||||
"@angular/core": "^20.1.7",
|
||||
"@angular/forms": "^20.1.7",
|
||||
"@angular/platform-browser": "^20.1.7",
|
||||
"@angular/router": "^20.1.7",
|
||||
"@primeuix/themes": "^1.2.1",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"file-saver": "^2.0.5",
|
||||
"moment": "^2.30.1",
|
||||
"postcss": "^8.5.6",
|
||||
"primeicons": "^7.0.0",
|
||||
"primeng": "^20.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tailwindcss-primeui": "^0.6.1",
|
||||
"tslib": "^2.3.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.1.6",
|
||||
"@angular/cli": "^20.1.6",
|
||||
"@angular/compiler-cli": "^20.1.7",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.8.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.8.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"baseUrl1": "http://192.168.8.188:5015",
|
||||
"baseUrl": "http://localhost:5016",
|
||||
"baseUrl_docker": "http://localhost:8080",
|
||||
"attachment": "http://localhost/document/family"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Angular Routes" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="./index.html" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ApplicationConfig, provideAppInitializer, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { providePrimeNG } from 'primeng/config';
|
||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
import { routes } from './app.routes';
|
||||
import MyPreset from './mythem';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
MessageService, ConfirmationService,
|
||||
provideAppInitializer(initializeApp()),
|
||||
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
|
||||
provideZonelessChangeDetection(),
|
||||
provideAnimationsAsync(),
|
||||
providePrimeNG({
|
||||
theme: {
|
||||
preset: MyPreset,
|
||||
options: {
|
||||
cssLayer: {
|
||||
name: 'primeng',
|
||||
order: 'theme, base, primeng'
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
provideRouter(routes)
|
||||
]
|
||||
};
|
||||
/*
|
||||
ng build --base-href "/familytreeui/" -c production
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
<p-toast position="bottom-center" />
|
||||
<app-toolbar> </app-toolbar>
|
||||
<router-outlet />
|
||||
<p-confirmDialog [style]="{width: '50vw'}"
|
||||
rejectButtonStyleClass="p-button-text"> </p-confirmDialog>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { Login } from './login';
|
||||
import { StaffComponent, StaffEditComponent } from './staff';
|
||||
import { AuthGuard } from './route-guard';
|
||||
import { FamilyTree, FamilyList} from './person';
|
||||
import { ImportCom } from './import.com/import.com';
|
||||
import { ImageDisplayComponent } from './pickperson/image.display';
|
||||
export const routes: Routes = [
|
||||
{ path: '', redirectTo: 'login', pathMatch: 'full' },
|
||||
// { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},},
|
||||
{ path: 'login', component: Login},
|
||||
{ path: 'staff', component: StaffComponent},
|
||||
{ path: 'person', component: FamilyList},
|
||||
{ path: 'imagedisplay/:id', component: ImageDisplayComponent},
|
||||
{ path: 'import', component: ImportCom},
|
||||
{ path: 'familytree', component: FamilyTree},
|
||||
{ path: 'staff/:id', component: StaffEditComponent},
|
||||
|
||||
];
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { ToolbarComponent } from './toolbar/toolbar.component';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.css'
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('FamilyTree');
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div>
|
||||
<label class="flex mb-2"> Import Excel File</label>
|
||||
<p-fileupload #fu mode="basic" chooseLabel="Choose"
|
||||
chooseIcon="pi pi-upload" name="file"
|
||||
url='{{baseUrl}}'
|
||||
maxFileSize="10000000000" (onUpload)="onUpload($event)" />
|
||||
<p-button styleClass="mt-4" label="Upload" (onClick)="fu.upload()" severity="primary" />
|
||||
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { FileUpload } from 'primeng/fileupload';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { AppSettingService } from '../shares';
|
||||
/*
|
||||
interface UploadEvent {
|
||||
originalEvent: Event;
|
||||
files: File[];
|
||||
}*/
|
||||
|
||||
@Component({
|
||||
selector: 'app-import.com',
|
||||
imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
|
||||
templateUrl: './import.com.html',
|
||||
styleUrl: './import.com.css'
|
||||
})
|
||||
export class ImportCom implements OnInit {
|
||||
uploadedFiles: any[] = [];
|
||||
baseUrl = "";
|
||||
private appSetting = inject(AppSettingService);
|
||||
constructor(private messageService: MessageService) {}
|
||||
ngOnInit(): void
|
||||
{
|
||||
const cbaseUrl = this.appSetting.appSetting.baseUrl;
|
||||
//http://localhost:5015/api/FileUpload/UploadFile"
|
||||
this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
|
||||
}
|
||||
onUpload(event:FileUploadEvent) {
|
||||
for(let file of event.files) {
|
||||
this.uploadedFiles.push(file);
|
||||
}
|
||||
|
||||
this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user