Initial Commit
This commit is contained in:
commit
16c9aae729
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@ -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/**
|
||||
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@ -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
|
||||
366
.gitignore
vendored
Normal file
366
.gitignore
vendored
Normal file
@ -0,0 +1,366 @@
|
||||
## 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
|
||||
/qtc-net-server/qtcdev.db
|
||||
/qtc-net-server/qtcdev.db-shm
|
||||
/qtc-net-server/qtcdev.db-wal
|
||||
19
docker-compose.dcproj
Normal file
19
docker-compose.dcproj
Normal file
@ -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>b1b3d2c9-92e6-4711-842e-c940a40a9a2e</ProjectGuid>
|
||||
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
|
||||
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}/swagger</DockerServiceUrl>
|
||||
<DockerServiceName>qtc-net-server</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>
|
||||
9
docker-compose.override.yml
Normal file
9
docker-compose.override.yml
Normal file
@ -0,0 +1,9 @@
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
qtc-net-server:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_HTTP_PORTS=8080
|
||||
ports:
|
||||
- "8080"
|
||||
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
qtc-net-server:
|
||||
image: ${DOCKER_REGISTRY-}qtcnetserver
|
||||
build:
|
||||
context: .
|
||||
dockerfile: qtc-net-server/Dockerfile
|
||||
11
launchSettings.json
Normal file
11
launchSettings.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Docker Compose": {
|
||||
"commandName": "DockerCompose",
|
||||
"commandVersion": "1.0",
|
||||
"serviceActions": {
|
||||
"qtc-net-server": "StartDebugging"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
qtc-net-server.sln
Normal file
31
qtc-net-server.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33414.496
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qtc-net-server", "qtc-net-server\qtc-net-server.csproj", "{AE9BEB1A-340C-4EE4-90D1-0B16456DDE6A}"
|
||||
EndProject
|
||||
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{B1B3D2C9-92E6-4711-842E-C940A40A9A2E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AE9BEB1A-340C-4EE4-90D1-0B16456DDE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AE9BEB1A-340C-4EE4-90D1-0B16456DDE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AE9BEB1A-340C-4EE4-90D1-0B16456DDE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AE9BEB1A-340C-4EE4-90D1-0B16456DDE6A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B1B3D2C9-92E6-4711-842E-C940A40A9A2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B1B3D2C9-92E6-4711-842E-C940A40A9A2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B1B3D2C9-92E6-4711-842E-C940A40A9A2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1B3D2C9-92E6-4711-842E-C940A40A9A2E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1B6C7F89-981F-4871-860E-D961CB6A204A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
96
qtc-net-server/Controllers/AuthController.cs
Normal file
96
qtc-net-server/Controllers/AuthController.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using qtc_api.Dtos.User;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/auth")]
|
||||
[ApiController]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly ITokenService _tokenService;
|
||||
|
||||
private readonly ServerConfig serverConfig;
|
||||
private readonly DataContext dataContext;
|
||||
|
||||
public AuthController(IUserService userService, ITokenService tokenService, DataContext dataContext)
|
||||
{
|
||||
_userService = userService;
|
||||
_tokenService = tokenService;
|
||||
|
||||
serverConfig = JsonSerializer.Deserialize<ServerConfig>(JsonDocument.Parse(System.IO.File.ReadAllText("./ServerConfig.json")));
|
||||
this.dataContext = dataContext;
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<ActionResult<ServiceResponse<User>>> Register(UserDto userDto)
|
||||
{
|
||||
if(userDto != null)
|
||||
{
|
||||
var response = await _userService.AddUser(userDto);
|
||||
if(response.Success != false)
|
||||
{
|
||||
return Ok(response);
|
||||
} else
|
||||
{
|
||||
return StatusCode(500, response.Message);
|
||||
}
|
||||
} else
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<ActionResult<ServiceResponse<string>>> Login(UserLoginDto request)
|
||||
{
|
||||
var dbUser = await _userService.GetUserByEmail(request.Email);
|
||||
|
||||
if (dbUser.Data == null)
|
||||
{
|
||||
return Ok(new ServiceResponse<string>
|
||||
{
|
||||
Message = "User not found.",
|
||||
Success = false
|
||||
});
|
||||
} else if(!BCrypt.Net.BCrypt.Verify(request.Password, dbUser.Data.PasswordHash))
|
||||
{
|
||||
return Ok(new ServiceResponse<string>
|
||||
{
|
||||
Message = "Incorrect password.",
|
||||
Success = false
|
||||
});
|
||||
}
|
||||
|
||||
if (dbUser.Data.Id == serverConfig.AdminUserId && dbUser.Data.Role != "Admin")
|
||||
{
|
||||
dbUser.Data.Role = "Admin";
|
||||
dataContext.SaveChanges();
|
||||
}
|
||||
|
||||
if (dbUser.Data.Status == 1)
|
||||
{
|
||||
return Ok(new ServiceResponse<string>
|
||||
{
|
||||
Message = "User is already signed in.",
|
||||
Success = false
|
||||
});
|
||||
}
|
||||
|
||||
var token = await _tokenService.GenerateAccessTokenAndRefreshToken(dbUser.Data, true, request.RememberMe);
|
||||
|
||||
return Ok(token);
|
||||
}
|
||||
|
||||
[HttpPost("refresh")]
|
||||
public async Task<ActionResult<ServiceResponse<string>>> RefreshLogin(string token)
|
||||
{
|
||||
var response = await _tokenService.ValidateRefreshToken(token);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
qtc-net-server/Controllers/ContactController.cs
Normal file
83
qtc-net-server/Controllers/ContactController.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using qtc_api.Models;
|
||||
using qtc_api.Services.ContactService;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/contacts")]
|
||||
[ApiController]
|
||||
public class ContactController : ControllerBase
|
||||
{
|
||||
private IContactService _contactService;
|
||||
|
||||
public ContactController(IContactService contactService)
|
||||
{
|
||||
_contactService = contactService;
|
||||
}
|
||||
|
||||
[HttpPost("add-contact")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<Contact>>> AddContact(string userId)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var ownerId = claims.First().Value;
|
||||
|
||||
var result = await _contactService.CreateContact(ownerId, userId);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
[HttpPost("approve-contact")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<bool>>> ApproveContact(string userId)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var ownerId = claims.First().Value;
|
||||
|
||||
var result = await _contactService.UpdateContactStatus(ownerId, userId, Contact.ContactStatus.Accepted, Contact.ContactStatus.Accepted);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
[HttpDelete("remove-contact")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<Contact>>> RemoveContact(string userId)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var ownerId = claims.First().Value;
|
||||
|
||||
var response = await _contactService.DeleteContact(ownerId, userId);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
[HttpGet("get-user-contacts")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<List<Contact>>>> GetUserContacts(User user)
|
||||
{
|
||||
var result = await _contactService.GetUserContacts(user);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
qtc-net-server/Controllers/GeneralController.cs
Normal file
20
qtc-net-server/Controllers/GeneralController.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/general")]
|
||||
[ApiController]
|
||||
public class GeneralController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<GeneralController> logger;
|
||||
public GeneralController(ILogger<GeneralController> logger) => this.logger = logger;
|
||||
|
||||
[HttpGet("ping")]
|
||||
public ActionResult PingAsync()
|
||||
{
|
||||
logger.LogInformation($"Ping Received From Client");
|
||||
return Ok("Pong!");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
qtc-net-server/Controllers/RoomsController.cs
Normal file
46
qtc-net-server/Controllers/RoomsController.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using qtc_api.Services.RoomService;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/rooms")]
|
||||
[ApiController]
|
||||
public class RoomsController : ControllerBase
|
||||
{
|
||||
public IRoomService _roomService;
|
||||
public IHubContext<ChatHub> _hubContext;
|
||||
|
||||
public RoomsController(IRoomService roomService, IHubContext<ChatHub> hubContext)
|
||||
{
|
||||
_roomService = roomService;
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
[HttpPost("create-room")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public async Task<ActionResult<ServiceResponse<Room>>> CreateRoom(string userId, RoomDto request)
|
||||
{
|
||||
var response = await _roomService.AddRoom(userId, request);
|
||||
await _hubContext.Clients.All.SendAsync("cf", "rul");
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpDelete("delete-room")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public async Task<ActionResult<ServiceResponse<Room>>> DeleteRoom(string roomId)
|
||||
{
|
||||
var response = await _roomService.DeleteRoom(roomId);
|
||||
await _hubContext.Clients.All.SendAsync("cf", "rul");
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpGet("get-all-rooms")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<List<Room>>>> GetAllRooms()
|
||||
{
|
||||
var rooms = await _roomService.GetAllRooms();
|
||||
return Ok(rooms);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
qtc-net-server/Controllers/UsersController.cs
Normal file
156
qtc-net-server/Controllers/UsersController.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using qtc_api.Dtos.User;
|
||||
using System.Net.Mime;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace qtc_api.Controllers
|
||||
{
|
||||
[Route("api/users")]
|
||||
[ApiController]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public UsersController(IUserService userService, IConfiguration configuration)
|
||||
{
|
||||
_userService = userService;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
[HttpGet("all")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<List<UserInformationDto>>>> GetAllUsers()
|
||||
{
|
||||
var users = await _userService.GetAllUsers();
|
||||
return Ok(users);
|
||||
}
|
||||
|
||||
[HttpGet("user-info")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<UserInformationDto>>> GetUserInformation(string id)
|
||||
{
|
||||
var user = await _userService.GetUserInformationById(id);
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
[HttpGet("user-authorized")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<User>>> UserFromAuthorizeHead()
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if(identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var id = claims.First().Value;
|
||||
|
||||
if(id != null)
|
||||
{
|
||||
var user = await _userService.GetUserById(id);
|
||||
return Ok(user);
|
||||
} else
|
||||
{
|
||||
return BadRequest("Token did not contain an ID.");
|
||||
}
|
||||
} else
|
||||
{
|
||||
return BadRequest("Header not found.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("users-online")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<List<UserInformationDto>>>> GetAllOnlineUsers()
|
||||
{
|
||||
var users = await _userService.GetAllOnlineUsers();
|
||||
return Ok(users);
|
||||
}
|
||||
|
||||
[HttpPut("update")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<UserInformationDto>>> UpdateUserInformation(UserUpdateInformationDto user)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if(identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var id = claims.First().Value;
|
||||
|
||||
if(id != null && id == user.Id)
|
||||
{
|
||||
var updatedUser = await _userService.UpdateUserInfo(user);
|
||||
return Ok(updatedUser);
|
||||
} else
|
||||
{
|
||||
return Unauthorized("You are not authorized to edit that user.");
|
||||
}
|
||||
} else
|
||||
{
|
||||
return BadRequest("Session Expired.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("upload-profile-pic")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ServiceResponse<string>>> UploadOrUpdateProfilePic(string userId, IFormFile file)
|
||||
{
|
||||
var identity = HttpContext.User.Identity as ClaimsIdentity;
|
||||
|
||||
if(identity != null)
|
||||
{
|
||||
IEnumerable<Claim> claims = identity.Claims;
|
||||
var id = claims.First().Value;
|
||||
|
||||
if(id != null && id == userId)
|
||||
{
|
||||
if (file.Length > 3000000)
|
||||
{
|
||||
return BadRequest("File Is Above Limit.");
|
||||
}
|
||||
|
||||
var response = await _userService.UpdateUserPic(userId, file);
|
||||
|
||||
return Ok(response);
|
||||
} else
|
||||
{
|
||||
return BadRequest("You are not permitted to edit that user.");
|
||||
}
|
||||
} else
|
||||
{
|
||||
return BadRequest("No Identity.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("profile-pic/{userId}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult> GetUserProfilePicture(string userId)
|
||||
{
|
||||
var result = await _userService.GetUserPic(userId);
|
||||
|
||||
if (result != null && result.Success != false)
|
||||
{
|
||||
return result.Data!;
|
||||
} else if (result!.Message == "User Does Not Have A Profile Picture." || result!.Message == "User Content Folder Does Not Exist Yet.")
|
||||
{
|
||||
return BadRequest("User has no profile picture.");
|
||||
} else
|
||||
{
|
||||
return BadRequest("Failed To Get Profile Picture.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("delete-user")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public async Task<ActionResult<ServiceResponse<User>>> DeleteUserById(string id)
|
||||
{
|
||||
var result = await _userService.DeleteUser(id);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
qtc-net-server/Data/DataContext.cs
Normal file
42
qtc-net-server/Data/DataContext.cs
Normal file
@ -0,0 +1,42 @@
|
||||
namespace qtc_api.Data
|
||||
{
|
||||
public class DataContext : DbContext
|
||||
{
|
||||
public DataContext(DbContextOptions<DataContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Room> Rooms { get; set; }
|
||||
public DbSet<RefreshToken> ValidRefreshTokens { get; set; }
|
||||
public DbSet<Contact> Contacts { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
// Users
|
||||
|
||||
builder.Entity<User>().HasMany(e => e.ContactsList);
|
||||
builder.Entity<User>().HasMany(e => e.ContactsMade);
|
||||
|
||||
// Rooms (no relations)
|
||||
|
||||
builder.Entity<Room>();
|
||||
|
||||
// Refresh Tokens
|
||||
|
||||
builder.Entity<RefreshToken>().HasOne(e => e.User)
|
||||
.WithMany(e => e.RefreshTokens)
|
||||
.HasForeignKey(e => e.UserID);
|
||||
|
||||
// Contacts
|
||||
|
||||
builder.Entity<Contact>().HasOne(e => e.Owner)
|
||||
.WithMany(e => e.ContactsMade)
|
||||
.HasForeignKey(e => e.OwnerId);
|
||||
|
||||
builder.Entity<Contact>().HasOne(e => e.User)
|
||||
.WithMany(e => e.ContactsList)
|
||||
.HasForeignKey(e => e.UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
qtc-net-server/Dockerfile
Normal file
21
qtc-net-server/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
#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.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["qtc-net-server/qtc-net-server.csproj", "qtc-net-server/"]
|
||||
RUN dotnet restore "qtc-net-server/qtc-net-server.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/qtc-net-server"
|
||||
RUN dotnet build "qtc-net-server.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "qtc-net-server.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "qtc-net-server.dll"]
|
||||
8
qtc-net-server/Dtos/Room/RoomDto.cs
Normal file
8
qtc-net-server/Dtos/Room/RoomDto.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace qtc_api.Dtos.Room
|
||||
{
|
||||
public class RoomDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; } = new DateTime();
|
||||
}
|
||||
}
|
||||
8
qtc-net-server/Dtos/User/UserConnectionDto.cs
Normal file
8
qtc-net-server/Dtos/User/UserConnectionDto.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserConnectionDto
|
||||
{
|
||||
public Models.User? ConnectedUser { get; set; }
|
||||
public string? ConnectionId { get; set; }
|
||||
}
|
||||
}
|
||||
10
qtc-net-server/Dtos/User/UserDto.cs
Normal file
10
qtc-net-server/Dtos/User/UserDto.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserDto
|
||||
{
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public DateTime DateOfBirth { get; set; } = new DateTime();
|
||||
}
|
||||
}
|
||||
14
qtc-net-server/Dtos/User/UserInformationDto.cs
Normal file
14
qtc-net-server/Dtos/User/UserInformationDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserInformationDto
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string ProfilePicture { get; set; } = string.Empty;
|
||||
public string Bio { get; set; } = string.Empty;
|
||||
public string Role { get; set; } = string.Empty;
|
||||
public DateTime DateOfBirth { get; set; } = new DateTime();
|
||||
public DateTime CreatedAt { get; set; } = new DateTime();
|
||||
public int Status { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
9
qtc-net-server/Dtos/User/UserLoginDto.cs
Normal file
9
qtc-net-server/Dtos/User/UserLoginDto.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserLoginDto
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public bool RememberMe { get; set; } = false;
|
||||
}
|
||||
}
|
||||
8
qtc-net-server/Dtos/User/UserStatusDto.cs
Normal file
8
qtc-net-server/Dtos/User/UserStatusDto.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserStatusDto
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public int Status { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
10
qtc-net-server/Dtos/User/UserUpdateInformationDto.cs
Normal file
10
qtc-net-server/Dtos/User/UserUpdateInformationDto.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace qtc_api.Dtos.User
|
||||
{
|
||||
public class UserUpdateInformationDto
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string Bio { get; set; } = string.Empty;
|
||||
public DateTime DateOfBirth { get; set; } = new DateTime();
|
||||
}
|
||||
}
|
||||
167
qtc-net-server/Hubs/ChatHub.cs
Normal file
167
qtc-net-server/Hubs/ChatHub.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace qtc_gateway.Hubs
|
||||
{
|
||||
[Authorize]
|
||||
public class ChatHub : Hub
|
||||
{
|
||||
private IUserService _userService;
|
||||
private ILogger<ChatHub> _logger;
|
||||
private static List<UserConnectionDto> ConnectedUsers = new List<UserConnectionDto>();
|
||||
private static List<User> OnlineUsers = new List<User>();
|
||||
|
||||
public ChatHub(IUserService userService, ILogger<ChatHub> logger)
|
||||
{
|
||||
_userService = userService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? ex)
|
||||
{
|
||||
Log("Client Disconnected.");
|
||||
|
||||
var connection = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
var user = OnlineUsers.FirstOrDefault(x => x.Id == connection!.ConnectedUser!.Id);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
await LogoutAsync(user!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("l")]
|
||||
public async Task LoginAsync(User user)
|
||||
{
|
||||
await Clients.All.SendAsync("rm", $"[SERVER] User {user.Username} Is Now Online");
|
||||
Log($"User {user.Username} Has Logged In");
|
||||
|
||||
var statusDto = new UserStatusDto { Id = user.Id, Status = 1 };
|
||||
|
||||
await _userService.UpdateStatus(statusDto);
|
||||
|
||||
ConnectedUsers.Add(new UserConnectionDto() { ConnectedUser = user, ConnectionId = Context.ConnectionId });
|
||||
OnlineUsers.Add(user);
|
||||
|
||||
ServerConfig serverConfig = JsonSerializer.Deserialize<ServerConfig>(JsonDocument.Parse(File.ReadAllText("./ServerConfig.json")));
|
||||
|
||||
await Clients.Client(ConnectedUsers.FirstOrDefault(e => e.ConnectionId == Context.ConnectionId)!.ConnectionId!).SendAsync("rc", serverConfig);
|
||||
await Clients.All.SendAsync("cf", "rul");
|
||||
}
|
||||
|
||||
[HubMethodName("us")]
|
||||
public async Task UpdateStatusAsync(User user, int status)
|
||||
{
|
||||
var statusDto = new UserStatusDto { Id = user.Id, Status = status };
|
||||
|
||||
await _userService.UpdateStatus(statusDto);
|
||||
|
||||
await Clients.All.SendAsync("cf", "rul");
|
||||
}
|
||||
|
||||
[HubMethodName("jl")]
|
||||
public async Task JoinHubAsync(User user)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, "LOBBY");
|
||||
|
||||
await Clients.Group("LOBBY").SendAsync("rm", $"[SERVER] User {user.Username} Has Joined The Lobby");
|
||||
Log($"User {user.Username} Has Joined The Lobby");
|
||||
}
|
||||
|
||||
[HubMethodName("ll")]
|
||||
public async Task LeaveLobbyAsync(User user)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "LOBBY");
|
||||
|
||||
await Clients.Group("LOBBY").SendAsync("rm", $"[SERVER] User {user.Username} Has Left The Lobby");
|
||||
Log($"User {user.Username} Has Left The Lobby");
|
||||
}
|
||||
|
||||
[HubMethodName("jr")]
|
||||
public async Task JoinRoomAsync(User user, Room room)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, room.Id);
|
||||
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] User {user.Username} Has Joined The Room");
|
||||
Log($"User {user.Username} Has Joined {room.Name}");
|
||||
}
|
||||
|
||||
[HubMethodName("lr")]
|
||||
public async Task LeaveRoomAsync(User user, Room room)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, room.Id);
|
||||
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] User {user.Username} Has Left The Room");
|
||||
Log($"User {user.Username} Has Left {room.Name}");
|
||||
}
|
||||
|
||||
[HubMethodName("hdr")]
|
||||
public async Task HandleDeletedRoomAsync(Room room)
|
||||
{
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[SERVER] This Room Has Been Deleted By An Administrator.");
|
||||
await Clients.Group(room.Id).SendAsync("cf", "rtl");
|
||||
|
||||
await Clients.All.SendAsync("cf", "rr");
|
||||
}
|
||||
|
||||
[HubMethodName("rcl")]
|
||||
public async Task RefreshContactsListForUser(UserInformationDto user, User execUser)
|
||||
{
|
||||
var connection = ConnectedUsers.FirstOrDefault(e => e.ConnectedUser.Id == user.Id);
|
||||
var connection2 = ConnectedUsers.FirstOrDefault(e => e.ConnectedUser.Id == execUser.Id);
|
||||
if (connection != null && connection2 != null)
|
||||
{
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("cf", "rcl");
|
||||
await Clients.Client(connection2.ConnectionId).SendAsync("cf", "rcl");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("s")]
|
||||
public async Task SendMessageAsync(User user, Message message, bool IsLobbyMsg, Room room = null!)
|
||||
{
|
||||
if(IsLobbyMsg == true) { await Clients.Group("LOBBY").SendAsync("rm", $"[{user.Username}] {message.Content}"); return; }
|
||||
await Clients.Group(room.Id).SendAsync("rm", $"[{user.Username}] {message.Content}");
|
||||
}
|
||||
|
||||
[HubMethodName("sdm")]
|
||||
public async Task SendDirectMessageAsync(User user, UserInformationDto userToMsg, Message message)
|
||||
{
|
||||
// send direct message directly to connected user
|
||||
var connection = ConnectedUsers.FirstOrDefault(e => e.ConnectedUser.Id == userToMsg.Id);
|
||||
if (connection != null)
|
||||
{
|
||||
UserInformationDto userInformationDto = new UserInformationDto { Id = user.Id, Username = user.Username, Bio = user.Bio, Role = user.Role, Status = user.Status, CreatedAt = user.CreatedAt, DateOfBirth = user.DateOfBirth, ProfilePicture = user.ProfilePicture };
|
||||
await Clients.Client(connection.ConnectionId).SendAsync("rdm", message, userInformationDto);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LogoutAsync(User user)
|
||||
{
|
||||
await Clients.All.SendAsync("rm", $"[SERVER] User {user.Username} Is Now Offline");
|
||||
Log($"User {user.Username} Has Logged Out");
|
||||
|
||||
var statusDto = new UserStatusDto { Id = user.Id, Status = 0 };
|
||||
|
||||
await _userService.UpdateStatus(statusDto);
|
||||
|
||||
Log($"Set User {user.Username} To Offline");
|
||||
|
||||
await Clients.All.SendAsync("cf", "rul");
|
||||
|
||||
OnlineUsers.Remove(user);
|
||||
|
||||
var connection = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
|
||||
ConnectedUsers.Remove(connection!);
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
_logger.LogInformation(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
qtc-net-server/Migrations/20250605232504_InitialData.Designer.cs
generated
Normal file
133
qtc-net-server/Migrations/20250605232504_InitialData.Designer.cs
generated
Normal file
@ -0,0 +1,133 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250605232504_InitialData")]
|
||||
partial class InitialData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Contacts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("ValidRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Room", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("CreatorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Rooms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
100
qtc-net-server/Migrations/20250605232504_InitialData.cs
Normal file
100
qtc-net-server/Migrations/20250605232504_InitialData.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialData : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Contacts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
OwnerId = table.Column<string>(type: "longtext", nullable: false),
|
||||
UserId = table.Column<string>(type: "longtext", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Contacts", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Rooms",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false),
|
||||
CreatorId = table.Column<string>(type: "longtext", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Rooms", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
Username = table.Column<string>(type: "longtext", nullable: false),
|
||||
ProfilePicture = table.Column<string>(type: "longtext", nullable: false),
|
||||
Bio = table.Column<string>(type: "longtext", nullable: false),
|
||||
Role = table.Column<string>(type: "longtext", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "longtext", nullable: false),
|
||||
Email = table.Column<string>(type: "longtext", nullable: false),
|
||||
DateOfBirth = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ValidRefreshTokens",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
UserID = table.Column<string>(type: "longtext", nullable: false),
|
||||
Token = table.Column<string>(type: "longtext", nullable: false),
|
||||
Expires = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ValidRefreshTokens", x => x.ID);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Contacts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Rooms");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ValidRefreshTokens");
|
||||
}
|
||||
}
|
||||
}
|
||||
136
qtc-net-server/Migrations/20250606000415_AddExtraStatus.Designer.cs
generated
Normal file
136
qtc-net-server/Migrations/20250606000415_AddExtraStatus.Designer.cs
generated
Normal file
@ -0,0 +1,136 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250606000415_AddExtraStatus")]
|
||||
partial class AddExtraStatus
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("OwnerStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("UserStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Contacts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("ValidRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Room", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("CreatorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Rooms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
39
qtc-net-server/Migrations/20250606000415_AddExtraStatus.cs
Normal file
39
qtc-net-server/Migrations/20250606000415_AddExtraStatus.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddExtraStatus : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Status",
|
||||
table: "Contacts",
|
||||
newName: "UserStatus");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "OwnerStatus",
|
||||
table: "Contacts",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OwnerStatus",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "UserStatus",
|
||||
table: "Contacts",
|
||||
newName: "Status");
|
||||
}
|
||||
}
|
||||
}
|
||||
160
qtc-net-server/Migrations/20250608203855_RefreshTokensForeignKey.Designer.cs
generated
Normal file
160
qtc-net-server/Migrations/20250608203855_RefreshTokensForeignKey.Designer.cs
generated
Normal file
@ -0,0 +1,160 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250608203855_RefreshTokensForeignKey")]
|
||||
partial class RefreshTokensForeignKey
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("OwnerStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("UserStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Contact");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("UserID");
|
||||
|
||||
b.ToTable("RefreshToken");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "Owner")
|
||||
.WithMany("ContactsMade")
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("ContactsList")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("RefreshTokens")
|
||||
.HasForeignKey("UserID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ContactsList");
|
||||
|
||||
b.Navigation("ContactsMade");
|
||||
|
||||
b.Navigation("RefreshTokens");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RefreshTokensForeignKey : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Rooms");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_ValidRefreshTokens",
|
||||
table: "ValidRefreshTokens");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Users",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Contacts",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "ValidRefreshTokens",
|
||||
newName: "RefreshToken");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Users",
|
||||
newName: "User");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Contacts",
|
||||
newName: "Contact");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserID",
|
||||
table: "RefreshToken",
|
||||
type: "varchar(255)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserId",
|
||||
table: "Contact",
|
||||
type: "varchar(255)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "OwnerId",
|
||||
table: "Contact",
|
||||
type: "varchar(255)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_RefreshToken",
|
||||
table: "RefreshToken",
|
||||
column: "ID");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_User",
|
||||
table: "User",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Contact",
|
||||
table: "Contact",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RefreshToken_UserID",
|
||||
table: "RefreshToken",
|
||||
column: "UserID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contact_OwnerId",
|
||||
table: "Contact",
|
||||
column: "OwnerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contact_UserId",
|
||||
table: "Contact",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contact_User_OwnerId",
|
||||
table: "Contact",
|
||||
column: "OwnerId",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contact_User_UserId",
|
||||
table: "Contact",
|
||||
column: "UserId",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_RefreshToken_User_UserID",
|
||||
table: "RefreshToken",
|
||||
column: "UserID",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contact_User_OwnerId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contact_User_UserId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_RefreshToken_User_UserID",
|
||||
table: "RefreshToken");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_User",
|
||||
table: "User");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_RefreshToken",
|
||||
table: "RefreshToken");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_RefreshToken_UserID",
|
||||
table: "RefreshToken");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Contact",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Contact_OwnerId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Contact_UserId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "User",
|
||||
newName: "Users");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "RefreshToken",
|
||||
newName: "ValidRefreshTokens");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Contact",
|
||||
newName: "Contacts");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserID",
|
||||
table: "ValidRefreshTokens",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(255)");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserId",
|
||||
table: "Contacts",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(255)");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "OwnerId",
|
||||
table: "Contacts",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(255)");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Users",
|
||||
table: "Users",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_ValidRefreshTokens",
|
||||
table: "ValidRefreshTokens",
|
||||
column: "ID");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Contacts",
|
||||
table: "Contacts",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Rooms",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
CreatorId = table.Column<string>(type: "longtext", nullable: false),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Rooms", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
||||
181
qtc-net-server/Migrations/20250608204114_AddRemainingRelations.Designer.cs
generated
Normal file
181
qtc-net-server/Migrations/20250608204114_AddRemainingRelations.Designer.cs
generated
Normal file
@ -0,0 +1,181 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250608204114_AddRemainingRelations")]
|
||||
partial class AddRemainingRelations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("OwnerStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("UserStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Contact");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("UserID");
|
||||
|
||||
b.ToTable("RefreshToken");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Room", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("CreatorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Room");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "Owner")
|
||||
.WithMany("ContactsMade")
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("ContactsList")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("RefreshTokens")
|
||||
.HasForeignKey("UserID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ContactsList");
|
||||
|
||||
b.Navigation("ContactsMade");
|
||||
|
||||
b.Navigation("RefreshTokens");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddRemainingRelations : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Room",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false),
|
||||
CreatorId = table.Column<string>(type: "longtext", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Room", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Room");
|
||||
}
|
||||
}
|
||||
}
|
||||
181
qtc-net-server/Migrations/20250608204625_ValidRefreshTokens.Designer.cs
generated
Normal file
181
qtc-net-server/Migrations/20250608204625_ValidRefreshTokens.Designer.cs
generated
Normal file
@ -0,0 +1,181 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250608204625_ValidRefreshTokens")]
|
||||
partial class ValidRefreshTokens
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("OwnerStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("UserStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Contacts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("UserID");
|
||||
|
||||
b.ToTable("ValidRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Room", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("CreatorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Rooms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "Owner")
|
||||
.WithMany("ContactsMade")
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("ContactsList")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("RefreshTokens")
|
||||
.HasForeignKey("UserID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ContactsList");
|
||||
|
||||
b.Navigation("ContactsMade");
|
||||
|
||||
b.Navigation("RefreshTokens");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
224
qtc-net-server/Migrations/20250608204625_ValidRefreshTokens.cs
Normal file
224
qtc-net-server/Migrations/20250608204625_ValidRefreshTokens.cs
Normal file
@ -0,0 +1,224 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ValidRefreshTokens : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contact_User_OwnerId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contact_User_UserId",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_RefreshToken_User_UserID",
|
||||
table: "RefreshToken");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_User",
|
||||
table: "User");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Room",
|
||||
table: "Room");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_RefreshToken",
|
||||
table: "RefreshToken");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Contact",
|
||||
table: "Contact");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "User",
|
||||
newName: "Users");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Room",
|
||||
newName: "Rooms");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "RefreshToken",
|
||||
newName: "ValidRefreshTokens");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Contact",
|
||||
newName: "Contacts");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_RefreshToken_UserID",
|
||||
table: "ValidRefreshTokens",
|
||||
newName: "IX_ValidRefreshTokens_UserID");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Contact_UserId",
|
||||
table: "Contacts",
|
||||
newName: "IX_Contacts_UserId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Contact_OwnerId",
|
||||
table: "Contacts",
|
||||
newName: "IX_Contacts_OwnerId");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Users",
|
||||
table: "Users",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Rooms",
|
||||
table: "Rooms",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_ValidRefreshTokens",
|
||||
table: "ValidRefreshTokens",
|
||||
column: "ID");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Contacts",
|
||||
table: "Contacts",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contacts_Users_OwnerId",
|
||||
table: "Contacts",
|
||||
column: "OwnerId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contacts_Users_UserId",
|
||||
table: "Contacts",
|
||||
column: "UserId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ValidRefreshTokens_Users_UserID",
|
||||
table: "ValidRefreshTokens",
|
||||
column: "UserID",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contacts_Users_OwnerId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contacts_Users_UserId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ValidRefreshTokens_Users_UserID",
|
||||
table: "ValidRefreshTokens");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_ValidRefreshTokens",
|
||||
table: "ValidRefreshTokens");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Users",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Rooms",
|
||||
table: "Rooms");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_Contacts",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "ValidRefreshTokens",
|
||||
newName: "RefreshToken");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Users",
|
||||
newName: "User");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Rooms",
|
||||
newName: "Room");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "Contacts",
|
||||
newName: "Contact");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_ValidRefreshTokens_UserID",
|
||||
table: "RefreshToken",
|
||||
newName: "IX_RefreshToken_UserID");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Contacts_UserId",
|
||||
table: "Contact",
|
||||
newName: "IX_Contact_UserId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Contacts_OwnerId",
|
||||
table: "Contact",
|
||||
newName: "IX_Contact_OwnerId");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_RefreshToken",
|
||||
table: "RefreshToken",
|
||||
column: "ID");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_User",
|
||||
table: "User",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Room",
|
||||
table: "Room",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_Contact",
|
||||
table: "Contact",
|
||||
column: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contact_User_OwnerId",
|
||||
table: "Contact",
|
||||
column: "OwnerId",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contact_User_UserId",
|
||||
table: "Contact",
|
||||
column: "UserId",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_RefreshToken_User_UserID",
|
||||
table: "RefreshToken",
|
||||
column: "UserID",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
qtc-net-server/Migrations/DataContextModelSnapshot.cs
Normal file
178
qtc-net-server/Migrations/DataContextModelSnapshot.cs
Normal file
@ -0,0 +1,178 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using qtc_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace qtc_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
partial class DataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("OwnerStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("UserStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Contacts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.Property<string>("ID")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserID")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("UserID");
|
||||
|
||||
b.ToTable("ValidRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Room", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("CreatorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Rooms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProfilePicture")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.Contact", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "Owner")
|
||||
.WithMany("ContactsMade")
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("ContactsList")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("qtc_api.Models.User", "User")
|
||||
.WithMany("RefreshTokens")
|
||||
.HasForeignKey("UserID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("qtc_api.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ContactsList");
|
||||
|
||||
b.Navigation("ContactsMade");
|
||||
|
||||
b.Navigation("RefreshTokens");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
16
qtc-net-server/Models/Contact.cs
Normal file
16
qtc-net-server/Models/Contact.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class Contact
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string OwnerId { get; set; } = string.Empty;
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
public ContactStatus OwnerStatus { get; set; } = ContactStatus.AwaitingApprovalFromOther;
|
||||
public ContactStatus UserStatus { get; set; } = ContactStatus.AwaitingApprovalFromSelf;
|
||||
|
||||
public virtual User? Owner { get; }
|
||||
public virtual User? User { get; }
|
||||
|
||||
public enum ContactStatus { AwaitingApprovalFromOther = 0, AwaitingApprovalFromSelf = 1, Accepted = 2 }
|
||||
}
|
||||
}
|
||||
10
qtc-net-server/Models/Message.cs
Normal file
10
qtc-net-server/Models/Message.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class Message
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string AuthorId { get; set; } = string.Empty;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; } = new DateTime();
|
||||
}
|
||||
}
|
||||
12
qtc-net-server/Models/RefreshToken.cs
Normal file
12
qtc-net-server/Models/RefreshToken.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class RefreshToken
|
||||
{
|
||||
public string ID { get; set; } = string.Empty;
|
||||
public string UserID { get; set; } = string.Empty;
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public virtual User User { get; } = null!;
|
||||
}
|
||||
}
|
||||
10
qtc-net-server/Models/Room.cs
Normal file
10
qtc-net-server/Models/Room.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class Room
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string CreatorId { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; } = new DateTime();
|
||||
}
|
||||
}
|
||||
11
qtc-net-server/Models/ServerConfig.cs
Normal file
11
qtc-net-server/Models/ServerConfig.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class ServerConfig
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? AdminUserId { get; set; }
|
||||
public bool IsDown { get; set; }
|
||||
public string? IsDownMessage { get; set; }
|
||||
}
|
||||
}
|
||||
9
qtc-net-server/Models/ServiceResponse.cs
Normal file
9
qtc-net-server/Models/ServiceResponse.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class ServiceResponse<T>
|
||||
{
|
||||
public T? Data { get; set; }
|
||||
public bool Success { get; set; } = false;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
20
qtc-net-server/Models/User.cs
Normal file
20
qtc-net-server/Models/User.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace qtc_api.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string ProfilePicture { get; set; } = string.Empty;
|
||||
public string Bio { get; set; } = string.Empty;
|
||||
public string Role { get; set; } = string.Empty;
|
||||
public string PasswordHash { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public DateTime DateOfBirth { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int Status { get; set; } = 0;
|
||||
|
||||
public virtual IEnumerable<RefreshToken>? RefreshTokens { get; }
|
||||
public virtual IEnumerable<Contact>? ContactsMade { get; }
|
||||
public virtual IEnumerable<Contact>? ContactsList { get; }
|
||||
}
|
||||
}
|
||||
63
qtc-net-server/Program.cs
Normal file
63
qtc-net-server/Program.cs
Normal file
@ -0,0 +1,63 @@
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.AspNetCore.SignalR;
|
||||
global using Microsoft.AspNetCore.Authorization;
|
||||
global using qtc_api.Models;
|
||||
global using qtc_api.Data;
|
||||
global using qtc_api.Dtos.User;
|
||||
global using qtc_api.Dtos.Room;
|
||||
global using qtc_api.Services.UserService;
|
||||
global using Microsoft.IdentityModel.Tokens;
|
||||
global using System.Text;
|
||||
global using qtc_api.Services.TokenService;
|
||||
global using qtc_gateway.Hubs;
|
||||
using qtc_api.Services.RoomService;
|
||||
using qtc_api.Services.ContactService;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddDbContext<DataContext>(options =>
|
||||
{
|
||||
if (builder.Environment.IsProduction()) options.UseMySQL(builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||
else options.UseSqlite(builder.Configuration.GetConnectionString("DevelopmentConnection"));
|
||||
|
||||
// ignore pending model changes warning
|
||||
options.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||||
});
|
||||
builder.Services.AddSignalR();
|
||||
|
||||
builder.Services.AddAuthentication().AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = builder.Configuration["Jwt:Issuer"],
|
||||
ValidAudience = builder.Configuration["Jwt:Audience"],
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
|
||||
};
|
||||
});
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<ITokenService, TokenService>();
|
||||
builder.Services.AddScoped<IRoomService, RoomService>();
|
||||
builder.Services.AddScoped<IContactService, ContactService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using var scope = app.Services.CreateScope();
|
||||
|
||||
await scope.ServiceProvider.GetRequiredService<DataContext>().Database.EnsureCreatedAsync();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.MapHub<ChatHub>("/chat");
|
||||
|
||||
app.Run();
|
||||
37
qtc-net-server/Properties/launchSettings.json
Normal file
37
qtc-net-server/Properties/launchSettings.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5268"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"publishAllPorts": true
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:26136",
|
||||
"sslPort": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
7
qtc-net-server/ServerConfig.json
Normal file
7
qtc-net-server/ServerConfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"Name": "QtC.NET Server",
|
||||
"Description": "This is a QtC.NET Server.",
|
||||
"AdminUserId": "523736357658921388",
|
||||
"IsDown": false,
|
||||
"IsDownMessage": "This server is currently down. Please try again later."
|
||||
}
|
||||
118
qtc-net-server/Services/ContactService/ContactService.cs
Normal file
118
qtc-net-server/Services/ContactService/ContactService.cs
Normal file
@ -0,0 +1,118 @@
|
||||
namespace qtc_api.Services.ContactService
|
||||
{
|
||||
public class ContactService : IContactService
|
||||
{
|
||||
private readonly DataContext _dataContext;
|
||||
|
||||
public ContactService(DataContext dataContext)
|
||||
{
|
||||
_dataContext = dataContext;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Contact>> CreateContact(string ownerId, string userId)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Contact>();
|
||||
var rnd = LongRandom(1, 900000000000000000, new Random());
|
||||
|
||||
var contact = new Contact()
|
||||
{
|
||||
Id = rnd.ToString(),
|
||||
OwnerId = ownerId,
|
||||
UserId = userId,
|
||||
OwnerStatus = Contact.ContactStatus.AwaitingApprovalFromOther,
|
||||
UserStatus = Contact.ContactStatus.AwaitingApprovalFromSelf,
|
||||
};
|
||||
|
||||
await _dataContext.Contacts.AddAsync(contact);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = contact;
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<bool>> UpdateContactStatus(string ownerId, string userId, Contact.ContactStatus ownerStatus, Contact.ContactStatus userStatus)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<bool>();
|
||||
var contact = await _dataContext.Contacts.FirstOrDefaultAsync(e => (e.OwnerId == ownerId || e.UserId == userId) || (e.OwnerId == userId || e.UserId == ownerId));
|
||||
|
||||
if(contact != null)
|
||||
{
|
||||
contact.OwnerStatus = ownerStatus;
|
||||
contact.UserStatus = userStatus;
|
||||
serviceResponse.Success = true;
|
||||
}
|
||||
else serviceResponse.Success = false;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Contact>> DeleteContact(string ownerId, string userId)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Contact>();
|
||||
var contact = await _dataContext.Contacts.FirstOrDefaultAsync(x => (x.OwnerId == ownerId || x.UserId == userId) || (x.OwnerId == userId || x.UserId == ownerId));
|
||||
|
||||
if (contact != null)
|
||||
{
|
||||
var result = _dataContext.Contacts.Remove(contact);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = result.Entity;
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Data = null;
|
||||
serviceResponse.Message = "Contact Not Found";
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Contact>> GetContactById(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Contact>();
|
||||
|
||||
var contact = await _dataContext.Contacts.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if (contact == null) { serviceResponse.Success = false; serviceResponse.Message = "Contact Does Not Exist."; }
|
||||
else { serviceResponse.Success = true; serviceResponse.Data = contact; }
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<List<Contact>>> GetUserContacts(User user)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<List<Contact>>();
|
||||
|
||||
var contactsMade = await _dataContext.Contacts.Where(x => x.OwnerId == user.Id).ToListAsync();
|
||||
var contactsList = await _dataContext.Contacts.Where(x => x.UserId == user.Id).ToListAsync();
|
||||
|
||||
var contactsCombined = new List<Contact>();
|
||||
|
||||
foreach (var contact in contactsMade)
|
||||
contactsCombined.Add(contact); // all contacts the user has made
|
||||
foreach (var contact in contactsList)
|
||||
contactsCombined.Add(contact); // all contacts the user has received
|
||||
|
||||
if (contactsCombined.Count == 0) { serviceResponse.Success = true; serviceResponse.Message = "User Has No Contacts."; }
|
||||
else { serviceResponse.Success = true; serviceResponse.Data = contactsCombined; }
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
private long LongRandom(long min, long max, Random rnd)
|
||||
{
|
||||
long result = rnd.Next((int)(min >> 32), (int)(max >> 32));
|
||||
result = result << 32;
|
||||
result = result | (long)rnd.Next((int)min, (int)max);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
qtc-net-server/Services/ContactService/IContactService.cs
Normal file
11
qtc-net-server/Services/ContactService/IContactService.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace qtc_api.Services.ContactService
|
||||
{
|
||||
public interface IContactService
|
||||
{
|
||||
public Task<ServiceResponse<Contact>> CreateContact(string ownerId, string userId);
|
||||
public Task<ServiceResponse<bool>> UpdateContactStatus(string ownerId, string userId, Contact.ContactStatus ownerStatus, Contact.ContactStatus userStatus);
|
||||
public Task<ServiceResponse<List<Contact>>> GetUserContacts(User user);
|
||||
public Task<ServiceResponse<Contact>> GetContactById(string id);
|
||||
public Task<ServiceResponse<Contact>> DeleteContact(string ownerId, string userId);
|
||||
}
|
||||
}
|
||||
11
qtc-net-server/Services/RoomService/IRoomService.cs
Normal file
11
qtc-net-server/Services/RoomService/IRoomService.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace qtc_api.Services.RoomService
|
||||
{
|
||||
public interface IRoomService
|
||||
{
|
||||
public Task<ServiceResponse<Room>> GetRoom(string id);
|
||||
public Task<ServiceResponse<List<Room>>> GetAllRooms();
|
||||
public Task<ServiceResponse<Room>> AddRoom(string userId, RoomDto room);
|
||||
public Task<ServiceResponse<Room>> UpdateRoom(Room room);
|
||||
public Task<ServiceResponse<Room>> DeleteRoom(string id);
|
||||
}
|
||||
}
|
||||
107
qtc-net-server/Services/RoomService/RoomService.cs
Normal file
107
qtc-net-server/Services/RoomService/RoomService.cs
Normal file
@ -0,0 +1,107 @@
|
||||
namespace qtc_api.Services.RoomService
|
||||
{
|
||||
public class RoomService : IRoomService
|
||||
{
|
||||
private readonly DataContext _dataContext;
|
||||
|
||||
private long idMax = 900000000000000000;
|
||||
|
||||
public RoomService(DataContext dataContext)
|
||||
{
|
||||
_dataContext = dataContext;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Room>> AddRoom(string userId, RoomDto room)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Room>();
|
||||
var roomList = await _dataContext.Rooms.ToListAsync();
|
||||
|
||||
var roomToAdd = new Room();
|
||||
Random rnd = new Random();
|
||||
|
||||
roomToAdd.Id = LongRandom(1, idMax, rnd).ToString();
|
||||
roomToAdd.Name = room.Name;
|
||||
roomToAdd.CreatorId = userId;
|
||||
roomToAdd.CreatedAt = DateTime.UtcNow;
|
||||
|
||||
var cRoom = await _dataContext.Rooms.FirstOrDefaultAsync(x => x.Name == roomToAdd.Name);
|
||||
|
||||
if (cRoom == null)
|
||||
{
|
||||
await _dataContext.Rooms.AddAsync(roomToAdd);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = roomToAdd;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Room already exists.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Room>> DeleteRoom(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Room>();
|
||||
var room = await _dataContext.Rooms.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if (room != null)
|
||||
{
|
||||
_dataContext.Rooms.Remove(room);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = room;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Room not found.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<List<Room>>> GetAllRooms()
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<List<Room>>();
|
||||
var rooms = await _dataContext.Rooms.ToListAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = rooms;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<Room>> GetRoom(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<Room>();
|
||||
var room = await _dataContext.Rooms.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if (room != null)
|
||||
{
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = room;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Room not found.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public Task<ServiceResponse<Room>> UpdateRoom(Room room)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private long LongRandom(long min, long max, Random rnd)
|
||||
{
|
||||
long result = rnd.Next((int)(min >> 32), (int)(max >> 32));
|
||||
result = result << 32;
|
||||
result = result | (long)rnd.Next((int)min, (int)max);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
qtc-net-server/Services/TokenService/ITokenService.cs
Normal file
10
qtc-net-server/Services/TokenService/ITokenService.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace qtc_api.Services.TokenService
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
public Task<ServiceResponse<string>> GenerateAccessTokenAndRefreshToken(User user, bool generateRefToken = true, bool remember = false);
|
||||
public Task<ServiceResponse<bool>> ValidateAccessToken(string accessToken);
|
||||
public Task<ServiceResponse<string>> ValidateRefreshToken(string refreshToken);
|
||||
public ServiceResponse<TokenValidationParameters> GetValidationParams();
|
||||
}
|
||||
}
|
||||
176
qtc-net-server/Services/TokenService/TokenService.cs
Normal file
176
qtc-net-server/Services/TokenService/TokenService.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace qtc_api.Services.TokenService
|
||||
{
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly DataContext _dataContext;
|
||||
|
||||
public TokenService(IConfiguration configuration, DataContext dataContext)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_dataContext = dataContext;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<string>> GenerateAccessTokenAndRefreshToken(User user, bool generateRefToken, bool remember)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<string>();
|
||||
|
||||
// Generate JWT Access Token
|
||||
|
||||
List<Claim> claims = new List<Claim>()
|
||||
{
|
||||
new Claim(ClaimTypes.Hash, user.Id),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Email, user.Email),
|
||||
new Claim(ClaimTypes.Role, user.Role)
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("Jwt:Key").Value!));
|
||||
var issuer = _configuration["Jwt:Issuer"];
|
||||
var audience = _configuration["Jwt:Audience"];
|
||||
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.AddHours(1),
|
||||
signingCredentials: creds
|
||||
);
|
||||
|
||||
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
|
||||
serviceResponse.Data = jwt;
|
||||
|
||||
// Generate and Store Refresh Token
|
||||
|
||||
if (generateRefToken)
|
||||
{
|
||||
var random = new byte[32];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
rng.GetBytes(random);
|
||||
}
|
||||
|
||||
RefreshToken refToken = new RefreshToken()
|
||||
{
|
||||
ID = LongRandom(1, 900000000000000000, new Random()).ToString(),
|
||||
UserID = user.Id,
|
||||
Token = Convert.ToBase64String(random)
|
||||
};
|
||||
|
||||
if (remember) refToken.Expires = DateTime.UtcNow.AddDays(7);
|
||||
else refToken.Expires = DateTime.UtcNow.AddDays(1);
|
||||
|
||||
_dataContext.ValidRefreshTokens.Add(refToken);
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Message = refToken.Token;
|
||||
}
|
||||
|
||||
serviceResponse.Success = true;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<string>> ValidateRefreshToken(string refreshToken)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<string>();
|
||||
|
||||
var dbRefresh = await _dataContext.ValidRefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken);
|
||||
|
||||
if (dbRefresh != null)
|
||||
{
|
||||
if (dbRefresh.Expires < DateTime.UtcNow)
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Refresh Token Expired.";
|
||||
|
||||
// Handle Expired Refresh Token
|
||||
|
||||
_dataContext.ValidRefreshTokens.Remove(dbRefresh);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == dbRefresh.UserID);
|
||||
|
||||
if (user != null && dbRefresh.UserID == user.Id)
|
||||
{
|
||||
var token = await GenerateAccessTokenAndRefreshToken(user, false, false);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = token.Data;
|
||||
}
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Requesting User ID and the associated Refresh Token's User ID does not match.";
|
||||
}
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Invalid Refresh Token.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<bool>> ValidateAccessToken(string accessToken)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<bool>();
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var validationParams = GetValidationParams();
|
||||
|
||||
TokenValidationResult result = await tokenHandler.ValidateTokenAsync(accessToken, validationParams.Data);
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = true;
|
||||
return serviceResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = false;
|
||||
return serviceResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceResponse<TokenValidationParameters> GetValidationParams()
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<TokenValidationParameters>();
|
||||
|
||||
serviceResponse.Data = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = _configuration["Jwt:Issuer"],
|
||||
ValidAudience = _configuration["Jwt:Audience"],
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!))
|
||||
};
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
private long LongRandom(long min, long max, Random rnd)
|
||||
{
|
||||
long result = rnd.Next((int)(min >> 32), (int)(max >> 32));
|
||||
result = result << 32;
|
||||
result = result | (long)rnd.Next((int)min, (int)max);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
qtc-net-server/Services/UserService/IUserService.cs
Normal file
19
qtc-net-server/Services/UserService/IUserService.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using qtc_api.Dtos.User;
|
||||
|
||||
namespace qtc_api.Services.UserService
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
public Task<ServiceResponse<User>> GetUserById(string id);
|
||||
public Task<ServiceResponse<UserInformationDto>> GetUserInformationById(string id);
|
||||
public Task<ServiceResponse<User>> GetUserByEmail(string email);
|
||||
public Task<ServiceResponse<List<UserInformationDto>>> GetAllUsers();
|
||||
public Task<ServiceResponse<List<UserInformationDto>>> GetAllOnlineUsers();
|
||||
public Task<ServiceResponse<User>> AddUser(UserDto userReq);
|
||||
public Task<ServiceResponse<UserInformationDto>> UpdateUserInfo(UserUpdateInformationDto request);
|
||||
public Task<ServiceResponse<string>> UpdateUserPic(string userId, IFormFile file);
|
||||
public Task<ServiceResponse<FileContentResult>> GetUserPic(string userId);
|
||||
public Task<ServiceResponse<UserStatusDto>> UpdateStatus(UserStatusDto request);
|
||||
public Task<ServiceResponse<User>> DeleteUser(string id);
|
||||
}
|
||||
}
|
||||
340
qtc-net-server/Services/UserService/UserService.cs
Normal file
340
qtc-net-server/Services/UserService/UserService.cs
Normal file
@ -0,0 +1,340 @@
|
||||
namespace qtc_api.Services.UserService
|
||||
{
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly DataContext _dataContext;
|
||||
|
||||
private long idMax = 900000000000000000;
|
||||
|
||||
public UserService(IConfiguration configuration, DataContext dataContext)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_dataContext = dataContext;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<User>> AddUser(UserDto userReq)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<User>();
|
||||
var userList = await _dataContext.Users.ToListAsync();
|
||||
|
||||
var user = new User();
|
||||
Random rnd = new Random();
|
||||
var id = LongRandom(1, idMax, rnd);
|
||||
|
||||
user.Id = id.ToString();
|
||||
user.Username = userReq.Username;
|
||||
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(userReq.Password);
|
||||
user.Email = userReq.Email;
|
||||
user.DateOfBirth = userReq.DateOfBirth;
|
||||
user.Role = _configuration["Jwt:DefaultUserRole"]!;
|
||||
user.CreatedAt = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
var cUser = userList.FirstOrDefault(x => x.Email == user.Email);
|
||||
|
||||
if (cUser != null)
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User with that email already exists.";
|
||||
return serviceResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _dataContext.Users.AddAsync(user);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
userList = await _dataContext.Users.ToListAsync();
|
||||
|
||||
if (userList.Contains(user))
|
||||
{
|
||||
var createdUser = await _dataContext.Users.FindAsync(user.Id);
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = createdUser;
|
||||
return serviceResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User not created.";
|
||||
return serviceResponse;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = ex.Message;
|
||||
return serviceResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<User>> DeleteUser(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<User>();
|
||||
var user = _dataContext.Users.FirstOrDefault(x => x.Id == id)!;
|
||||
|
||||
if(user != null)
|
||||
{
|
||||
_dataContext.Users.Remove(user);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = user;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User not found.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<List<UserInformationDto>>> GetAllUsers()
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<List<UserInformationDto>>();
|
||||
var userList = await _dataContext.Users.ToListAsync();
|
||||
|
||||
var userInfoList = new List<UserInformationDto>();
|
||||
foreach(var user in userList)
|
||||
{
|
||||
var x = new UserInformationDto();
|
||||
|
||||
x.Id = user.Id;
|
||||
x.Username = user.Username;
|
||||
x.ProfilePicture = user.ProfilePicture;
|
||||
x.Role = user.Role;
|
||||
x.Bio = user.Bio;
|
||||
x.DateOfBirth = user.DateOfBirth;
|
||||
|
||||
userInfoList.Add(x);
|
||||
}
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = userInfoList;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<List<UserInformationDto>>> GetAllOnlineUsers()
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<List<UserInformationDto>>();
|
||||
var onlineUsers = new List<UserInformationDto>();
|
||||
|
||||
await _dataContext.Users.ForEachAsync(user =>
|
||||
{
|
||||
if (user.Status == 1 || user.Status == 2 || user.Status == 3)
|
||||
{
|
||||
var x = new UserInformationDto();
|
||||
|
||||
x.Id = user.Id;
|
||||
x.Username = user.Username;
|
||||
x.ProfilePicture = user.ProfilePicture;
|
||||
x.Role = user.Role;
|
||||
x.Bio = user.Bio;
|
||||
x.Status = user.Status;
|
||||
x.DateOfBirth = user.DateOfBirth;
|
||||
x.CreatedAt = user.CreatedAt;
|
||||
|
||||
onlineUsers.Add(x);
|
||||
}
|
||||
});
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = onlineUsers;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<User>> GetUserById(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<User>();
|
||||
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == id);
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = user;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<UserInformationDto>> GetUserInformationById(string id)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<UserInformationDto>();
|
||||
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if(user != null)
|
||||
{
|
||||
var dto = new UserInformationDto();
|
||||
|
||||
dto.Id = user.Id;
|
||||
dto.Username = user.Username;
|
||||
dto.ProfilePicture = user.ProfilePicture;
|
||||
dto.Role = user.Role;
|
||||
dto.Bio = user.Bio;
|
||||
dto.DateOfBirth = user.DateOfBirth;
|
||||
dto.CreatedAt = user.CreatedAt;
|
||||
dto.Status = user.Status;
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = dto;
|
||||
return serviceResponse;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User not found.";
|
||||
return serviceResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<User>> GetUserByEmail(string email)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<User>();
|
||||
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Email == email);
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = user;
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<UserInformationDto>> UpdateUserInfo(UserUpdateInformationDto request)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<UserInformationDto>();
|
||||
var dbUser = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == request.Id);
|
||||
|
||||
if(dbUser != null)
|
||||
{
|
||||
dbUser.Username = request.Username;
|
||||
dbUser.Bio = request.Bio;
|
||||
dbUser.DateOfBirth = request.DateOfBirth;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
var infoDto = new UserInformationDto();
|
||||
|
||||
infoDto.Id = dbUser.Id;
|
||||
infoDto.Username = request.Username;
|
||||
infoDto.ProfilePicture = dbUser.ProfilePicture;
|
||||
infoDto.Bio = request.Bio;
|
||||
infoDto.Role = dbUser.Role;
|
||||
infoDto.DateOfBirth = request.DateOfBirth;
|
||||
infoDto.CreatedAt = dbUser.CreatedAt;
|
||||
infoDto.Status = dbUser.Status;
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = infoDto;
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User not found.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<string>> UpdateUserPic(string userId, IFormFile file)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<string>();
|
||||
|
||||
var userToUpdate = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == userId);
|
||||
var cdnPath = _configuration["GeneralConfig:CDNPath"];
|
||||
|
||||
if (userToUpdate == null)
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User Not Found.";
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
if (file != null && file.Length > 0)
|
||||
{
|
||||
if (!Directory.Exists(cdnPath)) Directory.CreateDirectory(cdnPath!);
|
||||
if (!Directory.Exists($"{cdnPath}/{userId}")) Directory.CreateDirectory($"{cdnPath}/{userId}");
|
||||
|
||||
var fileName = $"{userId}.{file.FileName.Split('.')[1]}";
|
||||
var filePath = Path.Combine(cdnPath!, userId, fileName);
|
||||
|
||||
using (var stream = File.Create(filePath))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
userToUpdate.ProfilePicture = fileName;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "Empty File.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<FileContentResult>> GetUserPic(string userId)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<FileContentResult>();
|
||||
|
||||
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == userId);
|
||||
var cdnPath = _configuration["GeneralConfig:CDNPath"];
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (user.ProfilePicture != null)
|
||||
{
|
||||
if (!Directory.Exists(cdnPath))
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User Content Folder Does Not Exist Yet.";
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
var pic = Path.Combine(cdnPath, userId, user.ProfilePicture);
|
||||
|
||||
if (!File.Exists(pic))
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User Does Not Have A Profile Picture.";
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Message = user.ProfilePicture;
|
||||
|
||||
serviceResponse.Data = new FileContentResult(File.ReadAllBytes(pic), $"image/{Path.GetExtension(pic)}");
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User Does Not Have A Profile Picture.";
|
||||
}
|
||||
} else
|
||||
{
|
||||
serviceResponse.Success = false;
|
||||
serviceResponse.Message = "User Not Found.";
|
||||
}
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<UserStatusDto>> UpdateStatus(UserStatusDto request)
|
||||
{
|
||||
var serviceResponse = new ServiceResponse<UserStatusDto>();
|
||||
var user = _dataContext.Users.FirstOrDefault(x => x.Id == request.Id)!;
|
||||
|
||||
user.Status = request.Status;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
serviceResponse.Success = true;
|
||||
serviceResponse.Data = request;
|
||||
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
private long LongRandom(long min, long max, Random rnd)
|
||||
{
|
||||
long result = rnd.Next((int)(min >> 32), (int)(max >> 32));
|
||||
result = result << 32;
|
||||
result = result | (long)rnd.Next((int)min, (int)max);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
qtc-net-server/appsettings.Development.json
Normal file
8
qtc-net-server/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft.AspNetCore": "Debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
22
qtc-net-server/appsettings.json
Normal file
22
qtc-net-server/appsettings.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"Jwt": {
|
||||
"Key": "bgpLLhY2L2UeZN3sj6WwSzScFmY3JgWfs33ZEJNcaPzC2TEnfZz",
|
||||
"Issuer": "http://localhost",
|
||||
"Audience": "http://localhost",
|
||||
"DefaultUserRole": "User"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=db;Database=qtcdb;Uid=root;Pwd=EuK3pXkaPCR9cW",
|
||||
"DevelopmentConnection": "Data Source=qtcdev.db"
|
||||
},
|
||||
"GeneralConfig": {
|
||||
"CDNPath": "./user-content"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
5
qtc-net-server/libman.json
Normal file
5
qtc-net-server/libman.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "cdnjs",
|
||||
"libraries": []
|
||||
}
|
||||
37
qtc-net-server/qtc-net-server.csproj
Normal file
37
qtc-net-server/qtc-net-server.csproj
Normal file
@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>qtc_api</RootNamespace>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.10.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="ServerConfig.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
2
qtc-net-server/run.Development.bat
Normal file
2
qtc-net-server/run.Development.bat
Normal file
@ -0,0 +1,2 @@
|
||||
@ECHO OFF
|
||||
dotnet run --project qtc-net-server.csproj -lp http -e ASPNETCORE_ENVIRONMENT="Development"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
Loading…
x
Reference in New Issue
Block a user