Merge branch 'backend-rewrite' into 'main'
Rewrote this piece of shit backend See merge request root/fileserver!7
This commit is contained in:
		
							
								
								
									
										29
									
								
								.eslintrc.js
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.eslintrc.js
									
									
									
									
									
								
							@@ -1,29 +0,0 @@
 | 
				
			|||||||
module.exports = {
 | 
					 | 
				
			||||||
	parser: '@typescript-eslint/parser',
 | 
					 | 
				
			||||||
	parserOptions: {
 | 
					 | 
				
			||||||
		project: 'tsconfig.json',
 | 
					 | 
				
			||||||
		tsconfigRootDir: __dirname,
 | 
					 | 
				
			||||||
		sourceType: 'module',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	plugins: ['@typescript-eslint/eslint-plugin', 'no-relative-import-paths'],
 | 
					 | 
				
			||||||
	extends: [
 | 
					 | 
				
			||||||
		'plugin:@typescript-eslint/recommended',
 | 
					 | 
				
			||||||
		'plugin:prettier/recommended',
 | 
					 | 
				
			||||||
	],
 | 
					 | 
				
			||||||
	root: true,
 | 
					 | 
				
			||||||
	env: {
 | 
					 | 
				
			||||||
		node: true,
 | 
					 | 
				
			||||||
		jest: true,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	ignorePatterns: ['.eslintrc.js'],
 | 
					 | 
				
			||||||
	rules: {
 | 
					 | 
				
			||||||
		'@typescript-eslint/interface-name-prefix': 'off',
 | 
					 | 
				
			||||||
		'@typescript-eslint/explicit-function-return-type': 'off',
 | 
					 | 
				
			||||||
		'@typescript-eslint/explicit-module-boundary-types': 'off',
 | 
					 | 
				
			||||||
		'@typescript-eslint/no-explicit-any': 'off',
 | 
					 | 
				
			||||||
		'no-relative-import-paths/no-relative-import-paths': [
 | 
					 | 
				
			||||||
			'error',
 | 
					 | 
				
			||||||
			{ 'allowSameFolder': true, 'rootDir': 'src' }
 | 
					 | 
				
			||||||
		]
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										422
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										422
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,37 +1,58 @@
 | 
				
			|||||||
# Created by .ignore support plugin (hsz.mobi)
 | 
					# Created by https://www.toptal.com/developers/gitignore/api/clion
 | 
				
			||||||
### JetBrains template
 | 
					# Edit at https://www.toptal.com/developers/gitignore?templates=clion
 | 
				
			||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
 | 
					
 | 
				
			||||||
 | 
					### CLion ###
 | 
				
			||||||
 | 
					# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
 | 
				
			||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 | 
					# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# User-specific stuff:
 | 
					# User-specific stuff
 | 
				
			||||||
.idea/**/workspace.xml
 | 
					.idea/**/workspace.xml
 | 
				
			||||||
.idea/**/tasks.xml
 | 
					.idea/**/tasks.xml
 | 
				
			||||||
.idea/dictionaries
 | 
					.idea/**/usage.statistics.xml
 | 
				
			||||||
 | 
					.idea/**/dictionaries
 | 
				
			||||||
 | 
					.idea/**/shelf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Sensitive or high-churn files:
 | 
					# AWS User-specific
 | 
				
			||||||
 | 
					.idea/**/aws.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Generated files
 | 
				
			||||||
 | 
					.idea/**/contentModel.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sensitive or high-churn files
 | 
				
			||||||
.idea/**/dataSources/
 | 
					.idea/**/dataSources/
 | 
				
			||||||
.idea/**/dataSources.ids
 | 
					.idea/**/dataSources.ids
 | 
				
			||||||
.idea/**/dataSources.xml
 | 
					 | 
				
			||||||
.idea/**/dataSources.local.xml
 | 
					.idea/**/dataSources.local.xml
 | 
				
			||||||
.idea/**/sqlDataSources.xml
 | 
					.idea/**/sqlDataSources.xml
 | 
				
			||||||
.idea/**/dynamic.xml
 | 
					.idea/**/dynamic.xml
 | 
				
			||||||
.idea/**/uiDesigner.xml
 | 
					.idea/**/uiDesigner.xml
 | 
				
			||||||
 | 
					.idea/**/dbnavigator.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Gradle:
 | 
					# Gradle
 | 
				
			||||||
.idea/**/gradle.xml
 | 
					.idea/**/gradle.xml
 | 
				
			||||||
.idea/**/libraries
 | 
					.idea/**/libraries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CMake
 | 
					# Gradle and Maven with auto-import
 | 
				
			||||||
cmake-build-debug/
 | 
					# When using Gradle or Maven with auto-import, you should exclude module files,
 | 
				
			||||||
 | 
					# since they will be recreated, and may cause churn.  Uncomment if using
 | 
				
			||||||
 | 
					# auto-import.
 | 
				
			||||||
 | 
					# .idea/artifacts
 | 
				
			||||||
 | 
					# .idea/compiler.xml
 | 
				
			||||||
 | 
					# .idea/jarRepositories.xml
 | 
				
			||||||
 | 
					# .idea/modules.xml
 | 
				
			||||||
 | 
					# .idea/*.iml
 | 
				
			||||||
 | 
					# .idea/modules
 | 
				
			||||||
 | 
					# *.iml
 | 
				
			||||||
 | 
					# *.ipr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Mongo Explorer plugin:
 | 
					# CMake
 | 
				
			||||||
 | 
					cmake-build-*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Mongo Explorer plugin
 | 
				
			||||||
.idea/**/mongoSettings.xml
 | 
					.idea/**/mongoSettings.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## File-based project format:
 | 
					# File-based project format
 | 
				
			||||||
*.iws
 | 
					*.iws
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Plugin-specific files:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# IntelliJ
 | 
					# IntelliJ
 | 
				
			||||||
out/
 | 
					out/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,358 +65,55 @@ atlassian-ide-plugin.xml
 | 
				
			|||||||
# Cursive Clojure plugin
 | 
					# Cursive Clojure plugin
 | 
				
			||||||
.idea/replstate.xml
 | 
					.idea/replstate.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SonarLint plugin
 | 
				
			||||||
 | 
					.idea/sonarlint/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
 | 
					# Crashlytics plugin (for Android Studio and IntelliJ)
 | 
				
			||||||
com_crashlytics_export_strings.xml
 | 
					com_crashlytics_export_strings.xml
 | 
				
			||||||
crashlytics.properties
 | 
					crashlytics.properties
 | 
				
			||||||
crashlytics-build.properties
 | 
					crashlytics-build.properties
 | 
				
			||||||
fabric.properties
 | 
					fabric.properties
 | 
				
			||||||
### VisualStudio template
 | 
					 | 
				
			||||||
## 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
 | 
					# Editor-based Rest Client
 | 
				
			||||||
*.suo
 | 
					.idea/httpRequests
 | 
				
			||||||
*.user
 | 
					 | 
				
			||||||
*.userosscache
 | 
					 | 
				
			||||||
*.sln.docstates
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# User-specific files (MonoDevelop/Xamarin Studio)
 | 
					# Android studio 3.1+ serialized cache file
 | 
				
			||||||
*.userprefs
 | 
					.idea/caches/build_file_checksums.ser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build results
 | 
					### CLion Patch ###
 | 
				
			||||||
[Dd]ebug/
 | 
					# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
 | 
				
			||||||
[Dd]ebugPublic/
 | 
					 | 
				
			||||||
[Rr]elease/
 | 
					 | 
				
			||||||
[Rr]eleases/
 | 
					 | 
				
			||||||
x64/
 | 
					 | 
				
			||||||
x86/
 | 
					 | 
				
			||||||
bld/
 | 
					 | 
				
			||||||
[Bb]in/
 | 
					 | 
				
			||||||
[Oo]bj/
 | 
					 | 
				
			||||||
[Ll]og/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Visual Studio 2015 cache/options directory
 | 
					# *.iml
 | 
				
			||||||
.vs/
 | 
					# modules.xml
 | 
				
			||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
 | 
					# .idea/misc.xml
 | 
				
			||||||
#wwwroot/
 | 
					# *.ipr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# MSTest test Results
 | 
					# Sonarlint plugin
 | 
				
			||||||
[Tt]est[Rr]esult*/
 | 
					# https://plugins.jetbrains.com/plugin/7973-sonarlint
 | 
				
			||||||
[Bb]uild[Ll]og.*
 | 
					.idea/**/sonarlint/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# NUNIT
 | 
					# SonarQube Plugin
 | 
				
			||||||
*.VisualState.xml
 | 
					# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
 | 
				
			||||||
TestResult.xml
 | 
					.idea/**/sonarIssues.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build Results of an ATL Project
 | 
					# Markdown Navigator plugin
 | 
				
			||||||
[Dd]ebugPS/
 | 
					# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
 | 
				
			||||||
[Rr]eleasePS/
 | 
					.idea/**/markdown-navigator.xml
 | 
				
			||||||
dlldata.c
 | 
					.idea/**/markdown-navigator-enh.xml
 | 
				
			||||||
 | 
					.idea/**/markdown-navigator/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Benchmark Results
 | 
					# Cache file creation bug
 | 
				
			||||||
BenchmarkDotNet.Artifacts/
 | 
					# See https://youtrack.jetbrains.com/issue/JBR-2257
 | 
				
			||||||
 | 
					.idea/$CACHE_FILE$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# .NET Core
 | 
					# CodeStream plugin
 | 
				
			||||||
project.lock.json
 | 
					# https://plugins.jetbrains.com/plugin/12206-codestream
 | 
				
			||||||
project.fragment.lock.json
 | 
					.idea/codestream.xml
 | 
				
			||||||
artifacts/
 | 
					 | 
				
			||||||
**/Properties/launchSettings.json
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*_i.c
 | 
					# Azure Toolkit for IntelliJ plugin
 | 
				
			||||||
*_p.c
 | 
					# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
 | 
				
			||||||
*_i.h
 | 
					.idea/**/azureSettings.xml
 | 
				
			||||||
*.ilk
 | 
					 | 
				
			||||||
*.meta
 | 
					 | 
				
			||||||
*.obj
 | 
					 | 
				
			||||||
*.pch
 | 
					 | 
				
			||||||
*.pdb
 | 
					 | 
				
			||||||
*.pgc
 | 
					 | 
				
			||||||
*.pgd
 | 
					 | 
				
			||||||
*.rsp
 | 
					 | 
				
			||||||
*.sbr
 | 
					 | 
				
			||||||
*.tlb
 | 
					 | 
				
			||||||
*.tli
 | 
					 | 
				
			||||||
*.tlh
 | 
					 | 
				
			||||||
*.tmp
 | 
					 | 
				
			||||||
*.tmp_proj
 | 
					 | 
				
			||||||
*.log
 | 
					 | 
				
			||||||
*.vspscc
 | 
					 | 
				
			||||||
*.vssscc
 | 
					 | 
				
			||||||
.builds
 | 
					 | 
				
			||||||
*.pidb
 | 
					 | 
				
			||||||
*.svclog
 | 
					 | 
				
			||||||
*.scc
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Chutzpah Test files
 | 
					# End of https://www.toptal.com/developers/gitignore/api/clion
 | 
				
			||||||
_Chutzpah*
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Visual C++ cache files
 | 
					run/
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# JustCode is a .NET coding add-in
 | 
					 | 
				
			||||||
.JustCode
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TeamCity is a build add-in
 | 
					 | 
				
			||||||
_TeamCity*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# DotCover is a Code Coverage Tool
 | 
					 | 
				
			||||||
*.dotCover
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AxoCover is a Code Coverage Tool
 | 
					 | 
				
			||||||
.axoCover/*
 | 
					 | 
				
			||||||
!.axoCover/settings.json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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
 | 
					 | 
				
			||||||
# 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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# SQL Server files
 | 
					 | 
				
			||||||
*.mdf
 | 
					 | 
				
			||||||
*.ldf
 | 
					 | 
				
			||||||
*.ndf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Business Intelligence projects
 | 
					 | 
				
			||||||
*.rdl.data
 | 
					 | 
				
			||||||
*.bim.layout
 | 
					 | 
				
			||||||
*.bim_*.settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Microsoft Fakes
 | 
					 | 
				
			||||||
FakesAssemblies/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# GhostDoc plugin setting file
 | 
					 | 
				
			||||||
*.GhostDoc.xml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Node.js Tools for Visual Studio
 | 
					 | 
				
			||||||
.ntvs_analysis.dat
 | 
					 | 
				
			||||||
node_modules/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Typescript v1 declaration files
 | 
					 | 
				
			||||||
typings/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# JetBrains Rider
 | 
					 | 
				
			||||||
.idea/
 | 
					 | 
				
			||||||
*.sln.iml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# IDE - VSCode
 | 
					 | 
				
			||||||
.vscode/*
 | 
					 | 
				
			||||||
!.vscode/settings.json
 | 
					 | 
				
			||||||
!.vscode/tasks.json
 | 
					 | 
				
			||||||
!.vscode/launch.json
 | 
					 | 
				
			||||||
!.vscode/extensions.json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# CodeRush
 | 
					 | 
				
			||||||
.cr/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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/
 | 
					 | 
				
			||||||
coverage/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### macOS template
 | 
					 | 
				
			||||||
# General
 | 
					 | 
				
			||||||
.DS_Store
 | 
					 | 
				
			||||||
.AppleDouble
 | 
					 | 
				
			||||||
.LSOverride
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Icon must end with two \r
 | 
					 | 
				
			||||||
Icon
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Thumbnails
 | 
					 | 
				
			||||||
._*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Files that might appear in the root of a volume
 | 
					 | 
				
			||||||
.DocumentRevisions-V100
 | 
					 | 
				
			||||||
.fseventsd
 | 
					 | 
				
			||||||
.Spotlight-V100
 | 
					 | 
				
			||||||
.TemporaryItems
 | 
					 | 
				
			||||||
.Trashes
 | 
					 | 
				
			||||||
.VolumeIcon.icns
 | 
					 | 
				
			||||||
.com.apple.timemachine.donotpresent
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Directories potentially created on remote AFP share
 | 
					 | 
				
			||||||
.AppleDB
 | 
					 | 
				
			||||||
.AppleDesktop
 | 
					 | 
				
			||||||
Network Trash Folder
 | 
					 | 
				
			||||||
Temporary Items
 | 
					 | 
				
			||||||
.apdisk
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=======
 | 
					 | 
				
			||||||
# Local
 | 
					 | 
				
			||||||
.env
 | 
					 | 
				
			||||||
dist
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
files
 | 
					 | 
				
			||||||
sqlite.db
 | 
					 | 
				
			||||||
							
								
								
									
										112
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							@@ -1,89 +1,42 @@
 | 
				
			|||||||
image: node:latest
 | 
					image: ubuntu:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
stages:
 | 
					stages:
 | 
				
			||||||
    - setup
 | 
					 | 
				
			||||||
    - test
 | 
					 | 
				
			||||||
    - build
 | 
					    - build
 | 
				
			||||||
    - package
 | 
					    - package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cache: &global_cache
 | 
					 | 
				
			||||||
    paths:
 | 
					 | 
				
			||||||
        - .yarn
 | 
					 | 
				
			||||||
        - node_modules
 | 
					 | 
				
			||||||
        - frontend/.yarn
 | 
					 | 
				
			||||||
        - frontend/node_modules
 | 
					 | 
				
			||||||
    policy: pull
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
before_script:
 | 
					 | 
				
			||||||
    - yarn install --cache-folder .yarn --frozen-lockfile
 | 
					 | 
				
			||||||
    - cd frontend
 | 
					 | 
				
			||||||
    - yarn install --cache-folder .yarn --frozen-lockfile
 | 
					 | 
				
			||||||
    - cd ..
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.dto_artifacts_need: &dto_artifacts_need
 | 
					 | 
				
			||||||
    job: test_build_dto
 | 
					 | 
				
			||||||
    artifacts: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test_build_dto:
 | 
					 | 
				
			||||||
    stage: setup
 | 
					 | 
				
			||||||
    cache:
 | 
					 | 
				
			||||||
        <<: *global_cache
 | 
					 | 
				
			||||||
        policy: pull-push
 | 
					 | 
				
			||||||
    before_script: []
 | 
					 | 
				
			||||||
    script:
 | 
					 | 
				
			||||||
        - cd dto
 | 
					 | 
				
			||||||
        - yarn install --frozen-lockfile
 | 
					 | 
				
			||||||
        - yarn lint
 | 
					 | 
				
			||||||
        - yarn build
 | 
					 | 
				
			||||||
        - cd ..
 | 
					 | 
				
			||||||
        - yarn install --cache-folder .yarn --frozen-lockfile
 | 
					 | 
				
			||||||
        - yarn add ./dto
 | 
					 | 
				
			||||||
        - cd frontend
 | 
					 | 
				
			||||||
        - yarn install --cache-folder .yarn --frozen-lockfile
 | 
					 | 
				
			||||||
        - yarn add ../dto
 | 
					 | 
				
			||||||
    artifacts:
 | 
					 | 
				
			||||||
        paths:
 | 
					 | 
				
			||||||
            - dto/lib/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test_backend:
 | 
					 | 
				
			||||||
    needs:
 | 
					 | 
				
			||||||
        - *dto_artifacts_need
 | 
					 | 
				
			||||||
    stage: test
 | 
					 | 
				
			||||||
    script:
 | 
					 | 
				
			||||||
        - yarn lint
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test_frontend:
 | 
					 | 
				
			||||||
    needs:
 | 
					 | 
				
			||||||
        - *dto_artifacts_need
 | 
					 | 
				
			||||||
    stage: test
 | 
					 | 
				
			||||||
    script:
 | 
					 | 
				
			||||||
        - cd frontend
 | 
					 | 
				
			||||||
        - yarn lint
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
build_backend:
 | 
					build_backend:
 | 
				
			||||||
    stage: build
 | 
					    stage: build
 | 
				
			||||||
    needs:
 | 
					    cache:
 | 
				
			||||||
        - *dto_artifacts_need
 | 
					        paths:
 | 
				
			||||||
        - job: test_backend
 | 
					            - /root/.cache/vcpkg
 | 
				
			||||||
          artifacts: false
 | 
					 | 
				
			||||||
    script:
 | 
					    script:
 | 
				
			||||||
        - echo This has to work till I rewrite the backend
 | 
					        - apt-get update
 | 
				
			||||||
        - false && echo
 | 
					        - apt-get install g++ gcc make cmake git curl zip unzip tar python3 pkg-config -y
 | 
				
			||||||
        - yarn webpack
 | 
					        - SRC="$PWD"
 | 
				
			||||||
 | 
					        - TMP=$(mktemp -d)
 | 
				
			||||||
 | 
					        - cd $TMP
 | 
				
			||||||
 | 
					        - git clone https://github.com/Microsoft/vcpkg.git .
 | 
				
			||||||
 | 
					        - ./bootstrap-vcpkg.sh -disableMetrics
 | 
				
			||||||
 | 
					        - cd $SRC
 | 
				
			||||||
 | 
					        - cmake -B build -S backend -DCMAKE_TOOLCHAIN_FILE=$TMP/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release
 | 
				
			||||||
 | 
					        - cmake --build build
 | 
				
			||||||
 | 
					        - cp build/backend server
 | 
				
			||||||
    artifacts:
 | 
					    artifacts:
 | 
				
			||||||
        paths:
 | 
					        paths:
 | 
				
			||||||
            - dist/
 | 
					            - server
 | 
				
			||||||
        expire_in: 1h
 | 
					        expire_in: 1h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build_frontend:
 | 
					test_and_build_frontend:
 | 
				
			||||||
 | 
					    image: node:latest
 | 
				
			||||||
    stage: build
 | 
					    stage: build
 | 
				
			||||||
    needs:
 | 
					    cache:
 | 
				
			||||||
        - *dto_artifacts_need
 | 
					        paths:
 | 
				
			||||||
        - job: test_frontend
 | 
					            - frontend/.yarn
 | 
				
			||||||
          artifacts: false
 | 
					            - frontend/node_modules
 | 
				
			||||||
    script:
 | 
					    script:
 | 
				
			||||||
        - cd frontend
 | 
					        - cd frontend
 | 
				
			||||||
 | 
					        - yarn install --cache-folder .yarn --frozen-lockfile
 | 
				
			||||||
 | 
					        - yarn lint
 | 
				
			||||||
        - yarn build
 | 
					        - yarn build
 | 
				
			||||||
    artifacts:
 | 
					    artifacts:
 | 
				
			||||||
        paths:
 | 
					        paths:
 | 
				
			||||||
@@ -92,23 +45,16 @@ build_frontend:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package_server:
 | 
					package_server:
 | 
				
			||||||
    stage: package
 | 
					    stage: package
 | 
				
			||||||
    cache: []
 | 
					 | 
				
			||||||
    before_script: []
 | 
					    before_script: []
 | 
				
			||||||
    needs:
 | 
					    needs:
 | 
				
			||||||
        - job: build_backend
 | 
					        - job: build_backend
 | 
				
			||||||
          artifacts: true
 | 
					          artifacts: true
 | 
				
			||||||
        - job: build_frontend
 | 
					        - job: test_and_build_frontend
 | 
				
			||||||
          artifacts: true
 | 
					          artifacts: true
 | 
				
			||||||
    script:
 | 
					    script:
 | 
				
			||||||
        - TMP=$(mktemp -d)
 | 
					        - mkdir static
 | 
				
			||||||
        - mv dist/* "$TMP"
 | 
					        - mv frontend/dist/* static/
 | 
				
			||||||
        - mkdir "$TMP/frontend"
 | 
					 | 
				
			||||||
        - mv frontend/dist/* "$TMP/frontend"
 | 
					 | 
				
			||||||
        - rm -r *
 | 
					 | 
				
			||||||
        - rm -r .* || true
 | 
					 | 
				
			||||||
        - mv "$TMP/"* .
 | 
					 | 
				
			||||||
    artifacts:
 | 
					    artifacts:
 | 
				
			||||||
        paths:
 | 
					        paths:
 | 
				
			||||||
            - package.json
 | 
					            - server
 | 
				
			||||||
            - server.js
 | 
					            - static/
 | 
				
			||||||
            - frontend/
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					# Default ignored files
 | 
				
			||||||
 | 
					/shelf/
 | 
				
			||||||
 | 
					/workspace.xml
 | 
				
			||||||
 | 
					# Editor-based HTTP Client requests
 | 
				
			||||||
 | 
					/httpRequests/
 | 
				
			||||||
 | 
					# Datasource local storage ignored files
 | 
				
			||||||
 | 
					/dataSources/
 | 
				
			||||||
 | 
					/dataSources.local.xml
 | 
				
			||||||
							
								
								
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					backend
 | 
				
			||||||
							
								
								
									
										19
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
				
			||||||
 | 
					    <data-source source="LOCAL" name="sqlite.db" uuid="6e8086dd-b853-422e-b48a-7c96a2403352">
 | 
				
			||||||
 | 
					      <driver-ref>sqlite.xerial</driver-ref>
 | 
				
			||||||
 | 
					      <synchronize>true</synchronize>
 | 
				
			||||||
 | 
					      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
				
			||||||
 | 
					      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/old_backend/sqlite.db</jdbc-url>
 | 
				
			||||||
 | 
					      <working-dir>$ProjectFileDir$</working-dir>
 | 
				
			||||||
 | 
					    </data-source>
 | 
				
			||||||
 | 
					    <data-source source="LOCAL" name="sqlite.db [2]" uuid="788293bd-abec-4b6b-a13e-26da21cb36dd">
 | 
				
			||||||
 | 
					      <driver-ref>sqlite.xerial</driver-ref>
 | 
				
			||||||
 | 
					      <synchronize>true</synchronize>
 | 
				
			||||||
 | 
					      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
				
			||||||
 | 
					      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/run/sqlite.db</jdbc-url>
 | 
				
			||||||
 | 
					      <working-dir>$ProjectFileDir$</working-dir>
 | 
				
			||||||
 | 
					    </data-source>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										2
									
								
								.idea/file_server.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.idea/file_server.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<module classpath="CMake" type="CPP_MODULE" version="4" />
 | 
				
			||||||
							
								
								
									
										11
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$/backend">
 | 
				
			||||||
 | 
					    <contentRoot DIR="$PROJECT_DIR$" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					  <component name="CidrRootsConfiguration">
 | 
				
			||||||
 | 
					    <libraryRoots>
 | 
				
			||||||
 | 
					      <file path="$PROJECT_DIR$/backend/lib" />
 | 
				
			||||||
 | 
					    </libraryRoots>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="ProjectModuleManager">
 | 
				
			||||||
 | 
					    <modules>
 | 
				
			||||||
 | 
					      <module fileurl="file://$PROJECT_DIR$/.idea/file_server.iml" filepath="$PROJECT_DIR$/.idea/file_server.iml" />
 | 
				
			||||||
 | 
					    </modules>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="VcsDirectoryMappings">
 | 
				
			||||||
 | 
					    <mapping directory="$PROJECT_DIR$" vcs="Git" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
	"tabWidth": 4,
 | 
					 | 
				
			||||||
	"useTabs": true,
 | 
					 | 
				
			||||||
	"singleQuote": true,
 | 
					 | 
				
			||||||
	"trailingComma": "none",
 | 
					 | 
				
			||||||
	"endOfLine": "lf"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							@@ -1,19 +0,0 @@
 | 
				
			|||||||
# Mutzi's fileserver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Description
 | 
					 | 
				
			||||||
The most crackhead fileserver you will find on the market
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Installation
 | 
					 | 
				
			||||||
```bash
 | 
					 | 
				
			||||||
npm install
 | 
					 | 
				
			||||||
cd frontend && npm install
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Running the app
 | 
					 | 
				
			||||||
```bash
 | 
					 | 
				
			||||||
npm run start:dev
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
Run in parallel for building the frontend:
 | 
					 | 
				
			||||||
````bash
 | 
					 | 
				
			||||||
cd frontend && npm run serve
 | 
					 | 
				
			||||||
````
 | 
					 | 
				
			||||||
							
								
								
									
										70
									
								
								backend/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								backend/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					cmake_minimum_required(VERSION 3.20)
 | 
				
			||||||
 | 
					project(backend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set(CMAKE_CXX_STANDARD 23)
 | 
				
			||||||
 | 
					set(CMAKE_CXX_STANDARD_REQUIRED YES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(backend
 | 
				
			||||||
 | 
					        src/main.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/dto/dto.h
 | 
				
			||||||
 | 
					        src/dto/responses.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/db/db.h
 | 
				
			||||||
 | 
					        src/db/db.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/db/model/Inode.cc
 | 
				
			||||||
 | 
					        src/db/model/Inode.h
 | 
				
			||||||
 | 
					        src/db/model/Tokens.cc
 | 
				
			||||||
 | 
					        src/db/model/Tokens.h
 | 
				
			||||||
 | 
					        src/db/model/User.cc
 | 
				
			||||||
 | 
					        src/db/model/User.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/controllers/controllers.h
 | 
				
			||||||
 | 
					        src/controllers/admin.cpp
 | 
				
			||||||
 | 
					        src/controllers/fs.cpp
 | 
				
			||||||
 | 
					        src/controllers/user.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/controllers/auth/auth_common.cpp
 | 
				
			||||||
 | 
					        src/controllers/auth/auth_basic.cpp
 | 
				
			||||||
 | 
					        src/controllers/auth/auth_2fa.cpp
 | 
				
			||||||
 | 
					        src/controllers/auth/auth_gitlab.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        src/filters/filters.h
 | 
				
			||||||
 | 
					        src/filters/filters.cpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					find_package(Drogon CONFIG REQUIRED)
 | 
				
			||||||
 | 
					find_package(CURL CONFIG REQUIRED)
 | 
				
			||||||
 | 
					find_package(PNG REQUIRED)
 | 
				
			||||||
 | 
					find_path(JWT_CPP_INCLUDE_DIRS "jwt-cpp/base.h")
 | 
				
			||||||
 | 
					find_path(BOTAN_INCLUDE_DIRS "botan/botan.h")
 | 
				
			||||||
 | 
					find_path(QR_INCLUDE_DIRS "qrcodegen.hpp")
 | 
				
			||||||
 | 
					find_path(PNGPP_INCLUDE_DIRS "png++/color.hpp")
 | 
				
			||||||
 | 
					find_library(BOTAN_LIBRARY botan-2)
 | 
				
			||||||
 | 
					find_library(QR_LIBRARY nayuki-qr-code-generator)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_include_directories(backend PRIVATE
 | 
				
			||||||
 | 
					        src
 | 
				
			||||||
 | 
					        ${JWT_CPP_INCLUDE_DIRS}
 | 
				
			||||||
 | 
					        ${BOTAN_INCLUDE_DIRS}
 | 
				
			||||||
 | 
					        ${QR_INCLUDE_DIRS}
 | 
				
			||||||
 | 
					        ${PNGPP_INCLUDE_DIRS}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(backend
 | 
				
			||||||
 | 
					        Drogon::Drogon
 | 
				
			||||||
 | 
					        CURL::libcurl
 | 
				
			||||||
 | 
					        PNG::PNG
 | 
				
			||||||
 | 
					        ${BOTAN_LIBRARY}
 | 
				
			||||||
 | 
					        ${QR_LIBRARY}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					install(TARGETS backend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_compile_options(backend PRIVATE
 | 
				
			||||||
 | 
					        $<$<CONFIG:Debug>:-g -Wall -Wno-unknown-pragmas>
 | 
				
			||||||
 | 
					        $<$<CONFIG:Release>:-O3>
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										88
									
								
								backend/src/controllers/admin.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								backend/src/controllers/admin.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "performance-unnecessary-value-param"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    void admin::users(req_type, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        std::vector<dto::Responses::GetUsersEntry> entries;
 | 
				
			||||||
 | 
					        auto users = user_mapper.findAll();
 | 
				
			||||||
 | 
					        for (const db::User& user : users)
 | 
				
			||||||
 | 
					            entries.emplace_back(
 | 
				
			||||||
 | 
					                    user.getValueOfId(),
 | 
				
			||||||
 | 
					                    user.getValueOfGitlab() != 0,
 | 
				
			||||||
 | 
					                    db::User_getEnumTfaType(user) != db::tfaTypes::NONE,
 | 
				
			||||||
 | 
					                    user.getValueOfName(),
 | 
				
			||||||
 | 
					                    db::User_getEnumRole(user)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_admin_users_res(entries));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void admin::set_role(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value& json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            uint64_t user_id = dto::json_get<uint64_t>(json, "user").value();
 | 
				
			||||||
 | 
					            db::UserRole role = (db::UserRole)dto::json_get<int>(json, "role").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            auto user = user_mapper.findByPrimaryKey(user_id);
 | 
				
			||||||
 | 
					            user.setRole(role);
 | 
				
			||||||
 | 
					            user_mapper.update(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void admin::logout(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value& json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            uint64_t user_id = dto::json_get<uint64_t>(json, "user").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            auto user = user_mapper.findByPrimaryKey(user_id);
 | 
				
			||||||
 | 
					            auth::revoke_all(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void admin::delete_user(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value& json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            uint64_t user_id = dto::json_get<uint64_t>(json, "user").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            auto user = user_mapper.findByPrimaryKey(user_id);
 | 
				
			||||||
 | 
					            auth::revoke_all(user);
 | 
				
			||||||
 | 
					             fs::delete_node(fs::get_node(user.getValueOfRootId()).value(), true);
 | 
				
			||||||
 | 
					             user_mapper.deleteOne(user);
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void admin::disable_2fa(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value& json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            uint64_t user_id = dto::json_get<uint64_t>(json, "user").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            auto user = user_mapper.findByPrimaryKey(user_id);
 | 
				
			||||||
 | 
					            user.setTfaType(db::tfaTypes::NONE);
 | 
				
			||||||
 | 
					            user_mapper.update(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										103
									
								
								backend/src/controllers/auth/auth_2fa.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								backend/src/controllers/auth/auth_2fa.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-make-member-function-const"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <botan/base32.h>
 | 
				
			||||||
 | 
					#include <botan/base64.h>
 | 
				
			||||||
 | 
					#include <qrcodegen.hpp>
 | 
				
			||||||
 | 
					#include <png++/png.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers/controllers.h"
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string create_totp_qrcode(const db::User& user, const std::string& b32_secret) {
 | 
				
			||||||
 | 
					    const int qrcode_pixel_size = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::stringstream code_ss;
 | 
				
			||||||
 | 
					    code_ss << "otpauth://totp/MFileserver:"
 | 
				
			||||||
 | 
					            << user.getValueOfName()
 | 
				
			||||||
 | 
					            << "?secret="
 | 
				
			||||||
 | 
					            << b32_secret
 | 
				
			||||||
 | 
					            << "&issuer=MFileserver";
 | 
				
			||||||
 | 
					    auto code = qrcodegen::QrCode::encodeText(code_ss.str().c_str(), qrcodegen::QrCode::Ecc::MEDIUM);
 | 
				
			||||||
 | 
					    const int mod_count = code.getSize();
 | 
				
			||||||
 | 
					    png::image<png::gray_pixel> image(mod_count*qrcode_pixel_size, mod_count*qrcode_pixel_size);
 | 
				
			||||||
 | 
					    for (int x = 0; x < mod_count; x++) for (int y = 0; y < mod_count; y++) {
 | 
				
			||||||
 | 
					        const bool mod = code.getModule(x, y);
 | 
				
			||||||
 | 
					        const int x_img_start = x * qrcode_pixel_size, y_img_start = y * qrcode_pixel_size;
 | 
				
			||||||
 | 
					        for (int x_img = x_img_start; x_img < x_img_start + qrcode_pixel_size; x_img++) for (int y_img = y_img_start; y_img < y_img_start + qrcode_pixel_size; y_img++)
 | 
				
			||||||
 | 
					            image[x_img][y_img] = mod ? 0 : 0xff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::stringstream image_ss;
 | 
				
			||||||
 | 
					    image.write_stream(image_ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string image_str = image_ss.str();
 | 
				
			||||||
 | 
					    std::vector<uint8_t> secret(image_str.data(), image_str.data()+image_str.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return "data:image/png;base64," + Botan::base64_encode(secret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    void auth::tfa_setup(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        Json::Value &json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            bool mail = dto::json_get<bool>(json, "mail").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto secret_uchar = rng->random_vec(32);
 | 
				
			||||||
 | 
					            std::vector<char> secret(secret_uchar.data(), secret_uchar.data()+32);
 | 
				
			||||||
 | 
					            user.setTfaSecret(secret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            user_mapper.update(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mail) {
 | 
				
			||||||
 | 
					                send_mail(user);
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                std::string b32_secret = Botan::base32_encode(secret_uchar);
 | 
				
			||||||
 | 
					                b32_secret.erase(std::remove(b32_secret.begin(), b32_secret.end(), '='), b32_secret.end());
 | 
				
			||||||
 | 
					                std::string code = create_totp_qrcode(user, b32_secret);
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_tfa_setup_res(b32_secret, code));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::tfa_complete(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        Json::Value &json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            bool mail = dto::json_get<bool>(json, "mail").value();
 | 
				
			||||||
 | 
					            uint32_t code = std::stoi(dto::json_get<std::string>(json, "code").value());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            user.setTfaType(mail ? db::tfaTypes::EMAIL : db::tfaTypes::TOTP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!verify2fa(user, code))
 | 
				
			||||||
 | 
					                return cbk(dto::Responses::get_unauth_res("Incorrect 2fa"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					            user_mapper.update(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            revoke_all(user);
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::tfa_disable(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        user.setTfaType(db::tfaTypes::NONE);
 | 
				
			||||||
 | 
					        user_mapper.update(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        revoke_all(user);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										135
									
								
								backend/src/controllers/auth/auth_basic.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								backend/src/controllers/auth/auth_basic.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-make-member-function-const"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <botan/argon2.h>
 | 
				
			||||||
 | 
					#include <botan/totp.h>
 | 
				
			||||||
 | 
					#include <jwt-cpp/traits/kazuho-picojson/traits.h>
 | 
				
			||||||
 | 
					#include <jwt-cpp/jwt.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers/controllers.h"
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    void auth::login(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value &json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            std::string username = dto::json_get<std::string>(json, "username").value();
 | 
				
			||||||
 | 
					            std::string password = dto::json_get<std::string>(json, "password").value();
 | 
				
			||||||
 | 
					            std::optional<std::string> otp = dto::json_get<std::string>(json, "otp");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto db = drogon::app().getDbClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(db);
 | 
				
			||||||
 | 
					            auto db_users = user_mapper.findBy(
 | 
				
			||||||
 | 
					                    db::Criteria(db::User::Cols::_name, db::CompareOps::EQ, username) &&
 | 
				
			||||||
 | 
					                    db::Criteria(db::User::Cols::_gitlab, db::CompareOps::EQ, 0)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (db_users.empty()) {
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_unauth_res("Invalid username or password"));
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            db::User &db_user = db_users.at(0);
 | 
				
			||||||
 | 
					            if (!Botan::argon2_check_pwhash(password.c_str(), password.size(), db_user.getValueOfPassword())) {
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_unauth_res("Invalid username or password"));
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (db::User_getEnumRole(db_user) == db::UserRole::DISABLED) {
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_unauth_res("Account is disabled"));
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const auto tfa = db::User_getEnumTfaType(db_user);
 | 
				
			||||||
 | 
					            if (tfa != db::tfaTypes::NONE) {
 | 
				
			||||||
 | 
					                if (!otp.has_value()) {
 | 
				
			||||||
 | 
					                    if (tfa == db::tfaTypes::EMAIL) send_mail(db_user);
 | 
				
			||||||
 | 
					                    return cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (!verify2fa(db_user, std::stoi(otp.value())))
 | 
				
			||||||
 | 
					                    return cbk(dto::Responses::get_unauth_res("Incorrect 2fa"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_login_res(get_token(db_user)));
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::signup(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        Json::Value &json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            std::string username = dto::json_get<std::string>(json, "username").value();
 | 
				
			||||||
 | 
					            std::string password = dto::json_get<std::string>(json, "password").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto existing_users = user_mapper.count(
 | 
				
			||||||
 | 
					                    db::Criteria(db::User::Cols::_name, db::CompareOps::EQ, username) &&
 | 
				
			||||||
 | 
					                    db::Criteria(db::User::Cols::_gitlab, db::CompareOps::EQ, 0)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (existing_users != 0) {
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_badreq_res("Username is already taken"));
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //std::string hash = Botan::argon2_generate_pwhash(password.c_str(), password.size(), *rng, 1, 256*1024, 2);
 | 
				
			||||||
 | 
					            std::string hash = Botan::argon2_generate_pwhash(password.c_str(), password.size(), *rng, 1, 16*1024, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db::User new_user;
 | 
				
			||||||
 | 
					            new_user.setName(username);
 | 
				
			||||||
 | 
					            new_user.setPassword(hash);
 | 
				
			||||||
 | 
					            new_user.setGitlab(0);
 | 
				
			||||||
 | 
					            new_user.setRole(db::UserRole::DISABLED);
 | 
				
			||||||
 | 
					            new_user.setRootId(0);
 | 
				
			||||||
 | 
					            new_user.setTfaType(db::tfaTypes::NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            user_mapper.insert(new_user);
 | 
				
			||||||
 | 
					            generate_root(new_user);
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception& e) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::refresh(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        db::Token token = dto::get_token(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::MapperToken token_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        token_mapper.deleteOne(token);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_login_res( get_token(user)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::logout_all(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        revoke_all(user);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::change_password(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        Json::Value &json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            std::string old_pw = dto::json_get<std::string>(json, "oldPassword").value();
 | 
				
			||||||
 | 
					            std::string new_pw = dto::json_get<std::string>(json, "newPassword").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto db = drogon::app().getDbClient();
 | 
				
			||||||
 | 
					            db::MapperUser user_mapper(db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!Botan::argon2_check_pwhash(old_pw.c_str(), old_pw.size(), user.getValueOfPassword()))
 | 
				
			||||||
 | 
					                return cbk(dto::Responses::get_unauth_res("Old password is wrong"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::string hash = Botan::argon2_generate_pwhash(new_pw.c_str(), new_pw.size(), *rng, 1, 256*1024, 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            user.setPassword(hash);
 | 
				
			||||||
 | 
					            user_mapper.update(user);
 | 
				
			||||||
 | 
					            revoke_all(user);
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										110
									
								
								backend/src/controllers/auth/auth_common.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								backend/src/controllers/auth/auth_common.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-make-member-function-const"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <botan/argon2.h>
 | 
				
			||||||
 | 
					#include <botan/uuid.h>
 | 
				
			||||||
 | 
					#include <botan/totp.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(BOTAN_HAS_SYSTEM_RNG)
 | 
				
			||||||
 | 
					#include <botan/system_rng.h>
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include <botan/auto_rng.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <jwt-cpp/traits/kazuho-picojson/traits.h>
 | 
				
			||||||
 | 
					#include <jwt-cpp/jwt.h>
 | 
				
			||||||
 | 
					#include <curl/curl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers/controllers.h"
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t payload_source(char* ptr, size_t size, size_t nmemb, void* userp) {
 | 
				
			||||||
 | 
					    auto* ss = (std::stringstream*)userp;
 | 
				
			||||||
 | 
					    return ss->readsome(ptr, (long)(size*nmemb));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					#if defined(BOTAN_HAS_SYSTEM_RNG)
 | 
				
			||||||
 | 
					    std::unique_ptr<Botan::RNG> auth::rng = std::make_unique<Botan::System_RNG>();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    std::unique_ptr<Botan::RNG> auth::rng = std::make_unique<Botan::AutoSeeded_RNG>();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool auth::verify2fa(const db::User& user, uint32_t totp) {
 | 
				
			||||||
 | 
					        size_t allowed_skew = db::User_getEnumTfaType(user) == db::tfaTypes::TOTP ? 0 : 10;
 | 
				
			||||||
 | 
					        const auto& totp_secret = (const std::vector<uint8_t>&) user.getValueOfTfaSecret();
 | 
				
			||||||
 | 
					        return Botan::TOTP(Botan::OctetString(totp_secret)).verify_totp(totp, std::chrono::system_clock::now(), allowed_skew);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::send_mail(const db::User& user) {
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        std::time_t t = std::time(nullptr);
 | 
				
			||||||
 | 
					        const auto& totp_secret = (const std::vector<uint8_t>&) user.getValueOfTfaSecret();
 | 
				
			||||||
 | 
					        char totp[16];
 | 
				
			||||||
 | 
					        std::snprintf(totp, 16, "%06d", Botan::TOTP(Botan::OctetString(totp_secret)).generate_totp(t));
 | 
				
			||||||
 | 
					        ss.imbue(std::locale("en_US.utf8"));
 | 
				
			||||||
 | 
					        ss << "Date: " << std::put_time(std::localtime(&t), "%a, %d %b %Y %T %z") << "\r\n";
 | 
				
			||||||
 | 
					        ss << "To: " << user.getValueOfName() << "\r\n";
 | 
				
			||||||
 | 
					        ss << "From: fileserver@mattv.de\r\n";
 | 
				
			||||||
 | 
					        ss << "Message-ID: " << Botan::UUID(*rng).to_string() << "@mattv.de>\r\n";
 | 
				
			||||||
 | 
					        ss << "Subject: Fileserver - EMail 2fa code\r\n";
 | 
				
			||||||
 | 
					        ss << "Your code is: " << totp << "\r\n";
 | 
				
			||||||
 | 
					        ss << "It is valid for 5 Minutes\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CURL* curl = curl_easy_init();
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_USERNAME, "no-reply@mattv.de");
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_PASSWORD, "noreplyLONGPASS123");
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.mattv.de:587");
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
 | 
				
			||||||
 | 
					        auto recp = curl_slist_append(nullptr, user.getValueOfName().c_str());
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recp);
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_READFUNCTION, &payload_source);
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_READDATA, &ss);
 | 
				
			||||||
 | 
					        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
 | 
				
			||||||
 | 
					        curl_easy_perform(curl);
 | 
				
			||||||
 | 
					        curl_slist_free_all(recp);
 | 
				
			||||||
 | 
					        curl_easy_cleanup(curl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string auth::get_token(const db::User& user) {
 | 
				
			||||||
 | 
					        auto db = drogon::app().getDbClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::MapperToken token_mapper(db);
 | 
				
			||||||
 | 
					        const auto iat = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
 | 
				
			||||||
 | 
					        const auto exp = iat + std::chrono::hours{24};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::Token new_token;
 | 
				
			||||||
 | 
					        new_token.setOwnerId(user.getValueOfId());
 | 
				
			||||||
 | 
					        new_token.setExp(exp.count());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        token_mapper.insert(new_token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return jwt::create<jwt::traits::kazuho_picojson>()
 | 
				
			||||||
 | 
					                .set_type("JWT")
 | 
				
			||||||
 | 
					                .set_payload_claim("sub", picojson::value((int64_t)user.getValueOfId()))
 | 
				
			||||||
 | 
					                .set_payload_claim("jti", picojson::value((int64_t)new_token.getValueOfId()))
 | 
				
			||||||
 | 
					                .set_issued_at(std::chrono::system_clock::from_time_t(iat.count()))
 | 
				
			||||||
 | 
					                .set_expires_at(std::chrono::system_clock::from_time_t(exp.count()))
 | 
				
			||||||
 | 
					                .sign(jwt::algorithm::hs256{jwt_secret});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::generate_root(db::User& user) {
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto node = fs::create_node("", user, false, std::nullopt, true);
 | 
				
			||||||
 | 
					        user.setRootId(std::get<db::INode>(node).getValueOfId());
 | 
				
			||||||
 | 
					        user_mapper.update(user);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::revoke_all(const db::User& user) {
 | 
				
			||||||
 | 
					        db::MapperToken token_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					         token_mapper.deleteBy(db::Criteria(db::Token::Cols::_owner_id, db::CompareOps::EQ, user.getValueOfId()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										118
									
								
								backend/src/controllers/auth/auth_gitlab.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								backend/src/controllers/auth/auth_gitlab.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "performance-unnecessary-value-param"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-make-member-function-const"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers/controllers.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string GITLAB_ID = "98bcbad78cb1f880d1d1de62291d70a791251a7bea077bfe7df111ef3c115760";
 | 
				
			||||||
 | 
					const std::string GITLAB_SECRET = "7ee01d2b204aff3a05f9d028f004d169b6d381ec873e195f314b3935fa150959";
 | 
				
			||||||
 | 
					const std::string GITLAB_URL = "https://gitlab.mattv.de";
 | 
				
			||||||
 | 
					const std::string GITLAB_API_URL = "https://ssh.gitlab.mattv.de";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string get_redirect_uri(req_type req) {
 | 
				
			||||||
 | 
					    auto host_header = req->headers().find("host");
 | 
				
			||||||
 | 
					    std::stringstream ss;
 | 
				
			||||||
 | 
					    ss << (req->isOnSecureConnection() ? "https" : "http")
 | 
				
			||||||
 | 
					       << "://"
 | 
				
			||||||
 | 
					       << (host_header != req->headers().end() ? host_header->second : "127.0.0.1:1234")
 | 
				
			||||||
 | 
					       << "/api/auth/gitlab_callback";
 | 
				
			||||||
 | 
					    return drogon::utils::urlEncode(ss.str());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const drogon::HttpClientPtr& get_gitlab_client() {
 | 
				
			||||||
 | 
					    static drogon::HttpClientPtr client = drogon::HttpClient::newHttpClient(GITLAB_API_URL, drogon::app().getLoop(), false, false);
 | 
				
			||||||
 | 
					    return client;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    std::optional<auth::gitlab_tokens> auth::get_gitlab_tokens(req_type req, const std::string& code_or_token, bool token) {
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << "/oauth/token"
 | 
				
			||||||
 | 
					           << "?redirect_uri=" << get_redirect_uri(req)
 | 
				
			||||||
 | 
					           << "&client_id=" << GITLAB_ID
 | 
				
			||||||
 | 
					           << "&client_secret=" << GITLAB_SECRET
 | 
				
			||||||
 | 
					           << (token ? "&refresh_token=" : "&code=") << code_or_token
 | 
				
			||||||
 | 
					           << "&grant_type=" << (token ? "refresh_token" : "authorization_code");
 | 
				
			||||||
 | 
					        auto gitlab_req = drogon::HttpRequest::newHttpRequest();
 | 
				
			||||||
 | 
					        gitlab_req->setPathEncode(false);
 | 
				
			||||||
 | 
					        gitlab_req->setPath(ss.str());
 | 
				
			||||||
 | 
					        gitlab_req->setMethod(drogon::HttpMethod::Post);
 | 
				
			||||||
 | 
					        auto res_tuple = get_gitlab_client()->sendRequest(gitlab_req);
 | 
				
			||||||
 | 
					        auto res = res_tuple.second;
 | 
				
			||||||
 | 
					        if ((res->statusCode() != drogon::HttpStatusCode::k200OK) && (res->statusCode() != drogon::HttpStatusCode::k201Created))
 | 
				
			||||||
 | 
					            return std::nullopt;
 | 
				
			||||||
 | 
					        auto json = *res->jsonObject();
 | 
				
			||||||
 | 
					        return std::make_optional<gitlab_tokens>(
 | 
				
			||||||
 | 
					                json["access_token"].as<std::string>(),
 | 
				
			||||||
 | 
					                json["refresh_token"].as<std::string>()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<auth::gitlab_user> auth::get_gitlab_user(const std::string& at) {
 | 
				
			||||||
 | 
					        auto gitlab_req = drogon::HttpRequest::newHttpRequest();
 | 
				
			||||||
 | 
					        gitlab_req->setPath("/api/v4/user");
 | 
				
			||||||
 | 
					        gitlab_req->addHeader("Authorization", "Bearer " + at);
 | 
				
			||||||
 | 
					        gitlab_req->setMethod(drogon::HttpMethod::Get);
 | 
				
			||||||
 | 
					        auto res_tuple = get_gitlab_client()->sendRequest(gitlab_req);
 | 
				
			||||||
 | 
					        auto res = res_tuple.second;
 | 
				
			||||||
 | 
					        if (res->statusCode() != drogon::HttpStatusCode::k200OK)
 | 
				
			||||||
 | 
					            return std::nullopt;
 | 
				
			||||||
 | 
					        auto json = *res->jsonObject();
 | 
				
			||||||
 | 
					        return std::make_optional<gitlab_user>(
 | 
				
			||||||
 | 
					                json["username"].as<std::string>(),
 | 
				
			||||||
 | 
					                json.get("is_admin", false).as<bool>()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::gitlab(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << GITLAB_URL << "/oauth/authorize"
 | 
				
			||||||
 | 
					           << "?redirect_uri=" << get_redirect_uri(req)
 | 
				
			||||||
 | 
					           << "&client_id=" << GITLAB_ID
 | 
				
			||||||
 | 
					           << "&scope=read_user&response_type=code";
 | 
				
			||||||
 | 
					        cbk(drogon::HttpResponse::newRedirectionResponse(ss.str()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void auth::gitlab_callback(req_type req, cbk_type cbk, std::string code) {
 | 
				
			||||||
 | 
					        auto tokens =  get_gitlab_tokens(req, code, false);
 | 
				
			||||||
 | 
					        if (!tokens.has_value())
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_unauth_res("Invalid code"));
 | 
				
			||||||
 | 
					        auto info =  get_gitlab_user(tokens->at);
 | 
				
			||||||
 | 
					        if (!info.has_value())
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_unauth_res("Invalid code"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        auto db_users =  user_mapper.findBy(
 | 
				
			||||||
 | 
					                db::Criteria(db::User::Cols::_name, db::CompareOps::EQ, info->name) &&
 | 
				
			||||||
 | 
					                db::Criteria(db::User::Cols::_gitlab, db::CompareOps::EQ, 1)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (db_users.empty()) {
 | 
				
			||||||
 | 
					            db::User new_user;
 | 
				
			||||||
 | 
					            new_user.setName(info->name);
 | 
				
			||||||
 | 
					            new_user.setPassword("");
 | 
				
			||||||
 | 
					            new_user.setGitlab(1);
 | 
				
			||||||
 | 
					            new_user.setRole(info->is_admin ? db::UserRole::ADMIN : db::UserRole::DISABLED);
 | 
				
			||||||
 | 
					            new_user.setRootId(0);
 | 
				
			||||||
 | 
					            new_user.setTfaType(db::tfaTypes::NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            user_mapper.insert(new_user);
 | 
				
			||||||
 | 
					            generate_root(new_user);
 | 
				
			||||||
 | 
					            db_users.push_back(new_user);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        db::User& db_user = db_users.at(0);
 | 
				
			||||||
 | 
					        db_user.setGitlabAt(tokens->at);
 | 
				
			||||||
 | 
					        db_user.setGitlabRt(tokens->rt);
 | 
				
			||||||
 | 
					        user_mapper.update(db_user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const std::string& token = get_token(db_user);
 | 
				
			||||||
 | 
					        cbk(drogon::HttpResponse::newRedirectionResponse("/set_token?token="+token));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										119
									
								
								backend/src/controllers/controllers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								backend/src/controllers/controllers.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					#ifndef BACKEND_CONTROLLERS_H
 | 
				
			||||||
 | 
					#define BACKEND_CONTROLLERS_H
 | 
				
			||||||
 | 
					#include <drogon/drogon.h>
 | 
				
			||||||
 | 
					#include <drogon/utils/coroutine.h>
 | 
				
			||||||
 | 
					#include <botan/rng.h>
 | 
				
			||||||
 | 
					#include <coroutine>
 | 
				
			||||||
 | 
					#include <variant>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using req_type = const drogon::HttpRequestPtr&;
 | 
				
			||||||
 | 
					using cbk_type = std::function<void(const drogon::HttpResponsePtr &)>&&;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					class admin : public drogon::HttpController<admin> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    METHOD_LIST_BEGIN
 | 
				
			||||||
 | 
					        METHOD_ADD(admin::users, "/users", drogon::Get, "Login", "Admin");
 | 
				
			||||||
 | 
					        METHOD_ADD(admin::set_role, "/set_role", drogon::Post, "Login", "Admin");
 | 
				
			||||||
 | 
					        METHOD_ADD(admin::logout, "/logout", drogon::Post, "Login", "Admin");
 | 
				
			||||||
 | 
					        METHOD_ADD(admin::delete_user, "/delete", drogon::Post, "Login", "Admin");
 | 
				
			||||||
 | 
					        METHOD_ADD(admin::disable_2fa, "/disable_2fa", drogon::Post, "Login", "Admin");
 | 
				
			||||||
 | 
					    METHOD_LIST_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void users(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void set_role(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void logout(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void delete_user(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void disable_2fa(req_type, cbk_type);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class auth : public drogon::HttpController<auth> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    METHOD_LIST_BEGIN
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::gitlab, "/gitlab", drogon::Get);
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::gitlab_callback, "/gitlab_callback?code={}", drogon::Get);
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::signup, "/signup", drogon::Post);
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::login, "/login", drogon::Post);
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::refresh, "/refresh", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::tfa_setup, "/2fa/setup", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::tfa_complete, "/2fa/complete", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::tfa_disable, "/2fa/disable", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::change_password, "/change_password", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(auth::logout_all, "/logout_all", drogon::Post, "Login");
 | 
				
			||||||
 | 
					    METHOD_LIST_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct gitlab_tokens {
 | 
				
			||||||
 | 
					        gitlab_tokens(std::string at, std::string rt) : at(std::move(at)), rt(std::move(rt)) {}
 | 
				
			||||||
 | 
					        std::string at, rt;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    struct gitlab_user {
 | 
				
			||||||
 | 
					        gitlab_user(std::string name, bool isAdmin) : name(std::move(name)), is_admin(isAdmin) {}
 | 
				
			||||||
 | 
					        std::string name;
 | 
				
			||||||
 | 
					        bool is_admin;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::unique_ptr<Botan::RNG> rng;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::optional<gitlab_tokens> get_gitlab_tokens(req_type, const std::string&, bool token);
 | 
				
			||||||
 | 
					    static std::optional<gitlab_user> get_gitlab_user(const std::string&);
 | 
				
			||||||
 | 
					    static bool verify2fa(const db::User&, uint32_t totp);
 | 
				
			||||||
 | 
					    static void send_mail(const db::User&);
 | 
				
			||||||
 | 
					    static std::string get_token(const db::User&);
 | 
				
			||||||
 | 
					    static void generate_root(db::User&);
 | 
				
			||||||
 | 
					    static void revoke_all(const db::User&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void gitlab(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void gitlab_callback(req_type, cbk_type, std::string code);
 | 
				
			||||||
 | 
					    void signup(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void login(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void refresh(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void tfa_setup(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void tfa_complete(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void tfa_disable(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void change_password(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void logout_all(req_type, cbk_type);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class fs : public drogon::HttpController<fs> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    METHOD_LIST_BEGIN
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::root, "/root", drogon::Get, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::node, "/node/{}", drogon::Get, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::path, "/path/{}", drogon::Get, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::create_node_req<false>, "/createFolder", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::create_node_req<true>, "/createFile", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::delete_node_req, "/delete/{}", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::upload, "/upload/{}", drogon::Post, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(fs::download, "/download", drogon::Post, "Login");
 | 
				
			||||||
 | 
					    METHOD_LIST_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::optional<db::INode> get_node(uint64_t node);
 | 
				
			||||||
 | 
					    static std::optional<db::INode> get_node_and_validate(const db::User& user, uint64_t node);
 | 
				
			||||||
 | 
					    static std::vector<db::INode> get_children(const db::INode& parent);
 | 
				
			||||||
 | 
					    static std::variant<db::INode, std::string> create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force = false);
 | 
				
			||||||
 | 
					    static void delete_node(db::INode node, bool allow_root = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void root(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void node(req_type, cbk_type, uint64_t node);
 | 
				
			||||||
 | 
					    void path(req_type, cbk_type, uint64_t node);
 | 
				
			||||||
 | 
					    template<bool file> void create_node_req(req_type req, cbk_type cbk);
 | 
				
			||||||
 | 
					    void delete_node_req(req_type, cbk_type, uint64_t node);
 | 
				
			||||||
 | 
					    void upload(req_type, cbk_type, uint64_t node);
 | 
				
			||||||
 | 
					    void download(req_type, cbk_type);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class user : public drogon::HttpController<user> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    METHOD_LIST_BEGIN
 | 
				
			||||||
 | 
					        METHOD_ADD(user::info, "/info", drogon::Get, "Login");
 | 
				
			||||||
 | 
					        METHOD_ADD(user::delete_user, "/delete", drogon::Post, "Login");
 | 
				
			||||||
 | 
					    METHOD_LIST_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void info(req_type, cbk_type);
 | 
				
			||||||
 | 
					    void delete_user(req_type, cbk_type);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif //BACKEND_CONTROLLERS_H
 | 
				
			||||||
							
								
								
									
										211
									
								
								backend/src/controllers/fs.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								backend/src/controllers/fs.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "performance-unnecessary-value-param"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <filesystem>
 | 
				
			||||||
 | 
					#include "controllers.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char windows_invalid_chars[] = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F<>:\"/\\|";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string generate_path(db::INode node) {
 | 
				
			||||||
 | 
					    db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					    std::stack<db::INode> path;
 | 
				
			||||||
 | 
					    path.push(node);
 | 
				
			||||||
 | 
					    while (node.getParentId() != nullptr) {
 | 
				
			||||||
 | 
					        node = inode_mapper.findByPrimaryKey(node.getValueOfParentId());
 | 
				
			||||||
 | 
					        path.push(node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::stringstream ss;
 | 
				
			||||||
 | 
					    while (!path.empty()) {
 | 
				
			||||||
 | 
					        const db::INode& seg = path.top();
 | 
				
			||||||
 | 
					        ss << seg.getValueOfName();
 | 
				
			||||||
 | 
					        if (seg.getValueOfIsFile() == 0) ss << '/';
 | 
				
			||||||
 | 
					        path.pop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ss.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    std::optional<db::INode> fs::get_node(uint64_t node) {
 | 
				
			||||||
 | 
					        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return inode_mapper.findByPrimaryKey(node);
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            return std::nullopt;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<db::INode> fs::get_node_and_validate(const db::User &user, uint64_t node) {
 | 
				
			||||||
 | 
					        auto inode = get_node(node);
 | 
				
			||||||
 | 
					        if (!inode.has_value()) return std::nullopt;
 | 
				
			||||||
 | 
					        if (inode->getValueOfOwnerId() != user.getValueOfId()) return std::nullopt;
 | 
				
			||||||
 | 
					        return inode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<db::INode> fs::get_children(const db::INode& parent) {
 | 
				
			||||||
 | 
					        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        return inode_mapper.findBy(db::Criteria(db::INode::Cols::_parent_id, db::CompareOps::EQ, parent.getValueOfId()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::variant<db::INode, std::string> fs::create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force) {
 | 
				
			||||||
 | 
					        // Stolen from https://github.com/boostorg/filesystem/blob/develop/src/portability.cpp
 | 
				
			||||||
 | 
					        if (!force)
 | 
				
			||||||
 | 
					            if (name.empty() || name[0] == ' ' || name.find_first_of(windows_invalid_chars, 0, sizeof(windows_invalid_chars)) != std::string::npos || *(name.end() - 1) == ' ' || *(name.end() - 1) == '.' || name == "." || name == "..")
 | 
				
			||||||
 | 
					                return {"Invalid name"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::INode node;
 | 
				
			||||||
 | 
					        node.setIsFile(file ? 1 : 0);
 | 
				
			||||||
 | 
					        node.setName(name);
 | 
				
			||||||
 | 
					        node.setOwnerId(owner.getValueOfId());
 | 
				
			||||||
 | 
					        if (parent.has_value()) {
 | 
				
			||||||
 | 
					            auto parent_node =  get_node_and_validate(owner, *parent);
 | 
				
			||||||
 | 
					            if (!parent_node.has_value())
 | 
				
			||||||
 | 
					                return {"Invalid parent"};
 | 
				
			||||||
 | 
					            if (parent_node->getValueOfIsFile() != 0)
 | 
				
			||||||
 | 
					                return {"Can't use file as parent"};
 | 
				
			||||||
 | 
					            auto children = get_children(*parent_node);
 | 
				
			||||||
 | 
					            for (const auto& child : children)
 | 
				
			||||||
 | 
					                if (child.getValueOfName() == name)
 | 
				
			||||||
 | 
					                    return {"File/Folder already exists"};
 | 
				
			||||||
 | 
					            node.setParentId(*parent);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        inode_mapper.insert(node);
 | 
				
			||||||
 | 
					        return {node};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::delete_node(db::INode node, bool allow_root) {
 | 
				
			||||||
 | 
					        if (node.getValueOfParentId() == 0 && (!allow_root)) return;
 | 
				
			||||||
 | 
					        if (node.getValueOfIsFile() == 0) {
 | 
				
			||||||
 | 
					            auto children =  get_children(node);
 | 
				
			||||||
 | 
					            for (const auto& child : children) delete_node(child, false);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            std::filesystem::path p("./files");
 | 
				
			||||||
 | 
					            p /= std::to_string(node.getValueOfId());
 | 
				
			||||||
 | 
					            std::filesystem::remove(p);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        inode_mapper.deleteOne(node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::root(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_root_res(user.getValueOfRootId()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::node(req_type req, cbk_type cbk, uint64_t node) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        auto inode =  get_node_and_validate(user, node);
 | 
				
			||||||
 | 
					        if (!inode.has_value())
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
				
			||||||
 | 
					        else if (inode->getValueOfIsFile() == 0) {
 | 
				
			||||||
 | 
					            std::vector<uint64_t> children;
 | 
				
			||||||
 | 
					            for (const db::INode& child : get_children(*inode)) children.push_back(child.getValueOfId());
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_node_folder_res(
 | 
				
			||||||
 | 
					                    inode->getValueOfId(),
 | 
				
			||||||
 | 
					                    inode->getValueOfName(),
 | 
				
			||||||
 | 
					                    inode->getParentId(),
 | 
				
			||||||
 | 
					                    children
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					        } else
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_node_file_res(
 | 
				
			||||||
 | 
					                    inode->getValueOfId(),
 | 
				
			||||||
 | 
					                    inode->getValueOfName(),
 | 
				
			||||||
 | 
					                    inode->getParentId(),
 | 
				
			||||||
 | 
					                    inode->getValueOfSize()
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::path(req_type req, cbk_type cbk, uint64_t node) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        auto inode = get_node_and_validate(user, node);
 | 
				
			||||||
 | 
					        if (!inode.has_value())
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_path_res( generate_path(*inode)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template<bool file>
 | 
				
			||||||
 | 
					    void fs::create_node_req(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        Json::Value& json = *req->jsonObject();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            uint64_t parent = dto::json_get<uint64_t>(json, "parent").value();
 | 
				
			||||||
 | 
					            std::string name = dto::json_get<std::string>(json, "name").value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto new_node = create_node(name, user, file, std::make_optional(parent));
 | 
				
			||||||
 | 
					            if (std::holds_alternative<std::string>(new_node))
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_badreq_res(std::get<std::string>(new_node)));
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                cbk(dto::Responses::get_new_node_res(std::get<db::INode>(new_node).getValueOfId()));
 | 
				
			||||||
 | 
					        } catch (const std::exception&) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::delete_node_req(req_type req, cbk_type cbk, uint64_t node) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        auto inode = get_node_and_validate(user, node);
 | 
				
			||||||
 | 
					        if (!inode.has_value())
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
				
			||||||
 | 
					        else if (inode->getValueOfParentId() == 0)
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Can't delete root"));
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					             delete_node(*inode);
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::upload(req_type req, cbk_type cbk, uint64_t node) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto inode = get_node_and_validate(user, node);
 | 
				
			||||||
 | 
					        if (!inode.has_value())
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
				
			||||||
 | 
					        if (inode->getValueOfIsFile() == 0)
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_badreq_res("Can't upload to a directory"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::MultiPartParser mpp;
 | 
				
			||||||
 | 
					        if (mpp.parse(req) != 0)
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_badreq_res("Failed to parse files"));
 | 
				
			||||||
 | 
					        if (mpp.getFiles().size() != 1)
 | 
				
			||||||
 | 
					            return cbk(dto::Responses::get_badreq_res("Exactly 1 file needed"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const drogon::HttpFile& file = mpp.getFiles().at(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::filesystem::path p("./files");
 | 
				
			||||||
 | 
					        p /= std::to_string(inode->getValueOfId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        file.saveAs(p.string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inode->setSize(file.fileLength());
 | 
				
			||||||
 | 
					        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					        inode_mapper.update(*inode);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void fs::download(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto node_id = req->getOptionalParameter<uint64_t>("id");
 | 
				
			||||||
 | 
					        if (!node_id.has_value()) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Invalid node"));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        auto inode =  get_node_and_validate(user, *node_id);
 | 
				
			||||||
 | 
					        if (!inode.has_value()) {
 | 
				
			||||||
 | 
					            cbk(dto::Responses::get_badreq_res("Invalid node"));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::filesystem::path p("./files");
 | 
				
			||||||
 | 
					        p /= std::to_string(inode->getValueOfId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cbk(drogon::HttpResponse::newFileResponse(
 | 
				
			||||||
 | 
					            p.string(),
 | 
				
			||||||
 | 
					            inode->getValueOfName()
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										29
									
								
								backend/src/controllers/user.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								backend/src/controllers/user.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "performance-unnecessary-value-param"
 | 
				
			||||||
 | 
					#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "controllers.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					    void user::info(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_user_info_res(
 | 
				
			||||||
 | 
					                user.getValueOfName(),
 | 
				
			||||||
 | 
					                user.getValueOfGitlab() != 0,
 | 
				
			||||||
 | 
					                db::User_getEnumTfaType(user) != db::tfaTypes::NONE)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void user::delete_user(req_type req, cbk_type cbk) {
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					        auth::revoke_all(user);
 | 
				
			||||||
 | 
					        fs::delete_node((fs::get_node(user.getValueOfRootId())).value(), true);
 | 
				
			||||||
 | 
					        user_mapper.deleteOne(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cbk(dto::Responses::get_success_res());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
							
								
								
									
										11
									
								
								backend/src/db/db.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								backend/src/db/db.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					#include "db.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace db {
 | 
				
			||||||
 | 
					    UserRole User_getEnumRole(const User& user) noexcept {
 | 
				
			||||||
 | 
					        return (UserRole)user.getValueOfRole();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tfaTypes User_getEnumTfaType(const User& user) noexcept {
 | 
				
			||||||
 | 
					        return (tfaTypes)user.getValueOfTfaType();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								backend/src/db/db.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								backend/src/db/db.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					#ifndef BACKEND_DB_H
 | 
				
			||||||
 | 
					#define BACKEND_DB_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drogon/utils/coroutine.h>
 | 
				
			||||||
 | 
					#include <drogon/drogon.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "model/Inode.h"
 | 
				
			||||||
 | 
					#include "model/Tokens.h"
 | 
				
			||||||
 | 
					#include "model/User.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string jwt_secret = "CUM";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace db {
 | 
				
			||||||
 | 
					    enum UserRole : int {
 | 
				
			||||||
 | 
					        ADMIN = 2,
 | 
				
			||||||
 | 
					        USER = 1,
 | 
				
			||||||
 | 
					        DISABLED = 0
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum tfaTypes : int {
 | 
				
			||||||
 | 
					        NONE = 0,
 | 
				
			||||||
 | 
					        EMAIL = 1,
 | 
				
			||||||
 | 
					        TOTP = 2
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using INode = drogon_model::sqlite3::Inode;
 | 
				
			||||||
 | 
					    using Token = drogon_model::sqlite3::Tokens;
 | 
				
			||||||
 | 
					    using User = drogon_model::sqlite3::User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using MapperInode = drogon::orm::Mapper<INode>;
 | 
				
			||||||
 | 
					    using MapperToken = drogon::orm::Mapper<Token>;
 | 
				
			||||||
 | 
					    using MapperUser = drogon::orm::Mapper<User>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using Criteria = drogon::orm::Criteria;
 | 
				
			||||||
 | 
					    using CompareOps = drogon::orm::CompareOperator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UserRole User_getEnumRole(const User&) noexcept;
 | 
				
			||||||
 | 
					    tfaTypes User_getEnumTfaType(const User&) noexcept;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //BACKEND_DB_H
 | 
				
			||||||
							
								
								
									
										1095
									
								
								backend/src/db/model/Inode.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1095
									
								
								backend/src/db/model/Inode.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										275
									
								
								backend/src/db/model/Inode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								backend/src/db/model/Inode.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,275 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Inode.h
 | 
				
			||||||
 | 
					 *  DO NOT EDIT. This file is generated by drogon_ctl
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <drogon/orm/Result.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Row.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Field.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/SqlBinder.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Mapper.h>
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					#include <drogon/orm/CoroMapper.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include <trantor/utils/Date.h>
 | 
				
			||||||
 | 
					#include <trantor/utils/Logger.h>
 | 
				
			||||||
 | 
					#include <json/json.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace drogon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace orm
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					class DbClient;
 | 
				
			||||||
 | 
					using DbClientPtr = std::shared_ptr<DbClient>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					namespace drogon_model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace sqlite3
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Inode
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    struct Cols
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string _id;
 | 
				
			||||||
 | 
					        static const std::string _is_file;
 | 
				
			||||||
 | 
					        static const std::string _name;
 | 
				
			||||||
 | 
					        static const std::string _parent_id;
 | 
				
			||||||
 | 
					        static const std::string _owner_id;
 | 
				
			||||||
 | 
					        static const std::string _size;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const static int primaryKeyNumber;
 | 
				
			||||||
 | 
					    const static std::string tableName;
 | 
				
			||||||
 | 
					    const static bool hasPrimaryKey;
 | 
				
			||||||
 | 
					    const static std::string primaryKeyName;
 | 
				
			||||||
 | 
					    using PrimaryKeyType = uint64_t;
 | 
				
			||||||
 | 
					    const PrimaryKeyType &getPrimaryKey() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param r One row of records in the SQL query result.
 | 
				
			||||||
 | 
					     * @param indexOffset Set the offset to -1 to access all columns by column names,
 | 
				
			||||||
 | 
					     * otherwise access all columns by offsets.
 | 
				
			||||||
 | 
					     * @note If the SQL is not a style of 'select * from table_name ...' (select all
 | 
				
			||||||
 | 
					     * columns by an asterisk), please set the offset to -1.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit Inode(const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit Inode(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     * @param pMasqueradingVector The aliases of table columns.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Inode(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Inode() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void updateByJson(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					    void updateByMasqueradedJson(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                 const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					    static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForCreation(const Json::Value &,
 | 
				
			||||||
 | 
					                                                const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                                    std::string &err);
 | 
				
			||||||
 | 
					    static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForUpdate(const Json::Value &,
 | 
				
			||||||
 | 
					                                          const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                          std::string &err);
 | 
				
			||||||
 | 
					    static bool validJsonOfField(size_t index,
 | 
				
			||||||
 | 
					                          const std::string &fieldName,
 | 
				
			||||||
 | 
					                          const Json::Value &pJson,
 | 
				
			||||||
 | 
					                          std::string &err,
 | 
				
			||||||
 | 
					                          bool isForCreation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column id
 | 
				
			||||||
 | 
					    void setId(const uint64_t &pId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column is_file  */
 | 
				
			||||||
 | 
					    ///Get the value of the column is_file, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfIsFile() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getIsFile() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column is_file
 | 
				
			||||||
 | 
					    void setIsFile(const uint64_t &pIsFile) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column name  */
 | 
				
			||||||
 | 
					    ///Get the value of the column name, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::string &getValueOfName() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::string> &getName() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column name
 | 
				
			||||||
 | 
					    void setName(const std::string &pName) noexcept;
 | 
				
			||||||
 | 
					    void setName(std::string &&pName) noexcept;
 | 
				
			||||||
 | 
					    void setNameToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column parent_id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column parent_id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfParentId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getParentId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column parent_id
 | 
				
			||||||
 | 
					    void setParentId(const uint64_t &pParentId) noexcept;
 | 
				
			||||||
 | 
					    void setParentIdToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column owner_id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column owner_id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfOwnerId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getOwnerId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column owner_id
 | 
				
			||||||
 | 
					    void setOwnerId(const uint64_t &pOwnerId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column size  */
 | 
				
			||||||
 | 
					    ///Get the value of the column size, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfSize() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getSize() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column size
 | 
				
			||||||
 | 
					    void setSize(const uint64_t &pSize) noexcept;
 | 
				
			||||||
 | 
					    void setSizeToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static size_t getColumnNumber() noexcept {  return 6;  }
 | 
				
			||||||
 | 
					    static const std::string &getColumnName(size_t index) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Json::Value toJson() const;
 | 
				
			||||||
 | 
					    Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
 | 
				
			||||||
 | 
					    /// Relationship interfaces
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    friend drogon::orm::Mapper<Inode>;
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					    friend drogon::orm::CoroMapper<Inode>;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    static const std::vector<std::string> &insertColumns() noexcept;
 | 
				
			||||||
 | 
					    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    const std::vector<std::string> updateColumns() const;
 | 
				
			||||||
 | 
					    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    ///For mysql or sqlite3
 | 
				
			||||||
 | 
					    void updateId(const uint64_t id);
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> id_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> isFile_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::string> name_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> parentId_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> ownerId_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> size_;
 | 
				
			||||||
 | 
					    struct MetaData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const std::string colName_;
 | 
				
			||||||
 | 
					        const std::string colType_;
 | 
				
			||||||
 | 
					        const std::string colDatabaseType_;
 | 
				
			||||||
 | 
					        const ssize_t colLength_;
 | 
				
			||||||
 | 
					        const bool isAutoVal_;
 | 
				
			||||||
 | 
					        const bool isPrimaryKey_;
 | 
				
			||||||
 | 
					        const bool notNull_;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    static const std::vector<MetaData> metaData_;
 | 
				
			||||||
 | 
					    bool dirtyFlag_[6]={ false };
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    static const std::string &sqlForFindingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="select * from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const std::string &sqlForDeletingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="delete from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::string sqlForInserting(bool &needSelection) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string sql="insert into " + tableName + " (";
 | 
				
			||||||
 | 
					        size_t parametersCount = 0;
 | 
				
			||||||
 | 
					        needSelection = false;
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "is_file,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "name,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[3])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "parent_id,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[4])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "owner_id,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[5])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "size,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql[sql.length()-1]=')';
 | 
				
			||||||
 | 
					            sql += " values (";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            sql += ") values (";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[3])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[4])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[5])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.resize(sql.length() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sql.append(1, ')');
 | 
				
			||||||
 | 
					        LOG_TRACE << sql;
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace sqlite3
 | 
				
			||||||
 | 
					} // namespace drogon_model
 | 
				
			||||||
							
								
								
									
										631
									
								
								backend/src/db/model/Tokens.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										631
									
								
								backend/src/db/model/Tokens.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,631 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Tokens.cc
 | 
				
			||||||
 | 
					 *  DO NOT EDIT. This file is generated by drogon_ctl
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Tokens.h"
 | 
				
			||||||
 | 
					#include <drogon/utils/Utilities.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace drogon;
 | 
				
			||||||
 | 
					using namespace drogon::orm;
 | 
				
			||||||
 | 
					using namespace drogon_model::sqlite3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string Tokens::Cols::_id = "id";
 | 
				
			||||||
 | 
					const std::string Tokens::Cols::_owner_id = "owner_id";
 | 
				
			||||||
 | 
					const std::string Tokens::Cols::_exp = "exp";
 | 
				
			||||||
 | 
					const std::string Tokens::primaryKeyName = "id";
 | 
				
			||||||
 | 
					const bool Tokens::hasPrimaryKey = true;
 | 
				
			||||||
 | 
					const std::string Tokens::tableName = "tokens";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::vector<typename Tokens::MetaData> Tokens::metaData_={
 | 
				
			||||||
 | 
					{"id","uint64_t","integer",8,1,1,1},
 | 
				
			||||||
 | 
					{"owner_id","uint64_t","integer",8,0,0,1},
 | 
				
			||||||
 | 
					{"exp","uint64_t","integer",8,0,0,1}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const std::string &Tokens::getColumnName(size_t index) noexcept(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(index < metaData_.size());
 | 
				
			||||||
 | 
					    return metaData_[index].colName_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Tokens::Tokens(const Row &r, const ssize_t indexOffset) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(indexOffset < 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!r["id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>(r["id"].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!r["owner_id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>(r["owner_id"].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!r["exp"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>(r["exp"].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        size_t offset = (size_t)indexOffset;
 | 
				
			||||||
 | 
					        if(offset + 3 > r.size())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LOG_FATAL << "Invalid SQL result for this model";
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        size_t index;
 | 
				
			||||||
 | 
					        index = offset + 0;
 | 
				
			||||||
 | 
					        if(!r[index].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>(r[index].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        index = offset + 1;
 | 
				
			||||||
 | 
					        if(!r[index].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>(r[index].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        index = offset + 2;
 | 
				
			||||||
 | 
					        if(!r[index].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>(r[index].as<uint64_t>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tokens::Tokens(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pMasqueradingVector.size() != 3)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        LOG_ERROR << "Bad masquerading vector";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[0] = true;
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[0]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[0]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[1] = true;
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[1]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[1]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[2] = true;
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[2]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[2]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tokens::Tokens(const Json::Value &pJson) noexcept(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pJson.isMember("id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[0]=true;
 | 
				
			||||||
 | 
					        if(!pJson["id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>((uint64_t)pJson["id"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("owner_id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[1]=true;
 | 
				
			||||||
 | 
					        if(!pJson["owner_id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>((uint64_t)pJson["owner_id"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("exp"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[2]=true;
 | 
				
			||||||
 | 
					        if(!pJson["exp"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>((uint64_t)pJson["exp"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Tokens::updateByMasqueradedJson(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                            const std::vector<std::string> &pMasqueradingVector) noexcept(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pMasqueradingVector.size() != 3)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        LOG_ERROR << "Bad masquerading vector";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[0]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[0]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[1] = true;
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[1]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[1]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[2] = true;
 | 
				
			||||||
 | 
					        if(!pJson[pMasqueradingVector[2]].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[2]].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Tokens::updateByJson(const Json::Value &pJson) noexcept(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pJson.isMember("id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!pJson["id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            id_=std::make_shared<uint64_t>((uint64_t)pJson["id"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("owner_id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[1] = true;
 | 
				
			||||||
 | 
					        if(!pJson["owner_id"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ownerId_=std::make_shared<uint64_t>((uint64_t)pJson["owner_id"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("exp"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dirtyFlag_[2] = true;
 | 
				
			||||||
 | 
					        if(!pJson["exp"].isNull())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            exp_=std::make_shared<uint64_t>((uint64_t)pJson["exp"].asUInt64());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint64_t &Tokens::getValueOfId() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const static uint64_t defaultValue = uint64_t();
 | 
				
			||||||
 | 
					    if(id_)
 | 
				
			||||||
 | 
					        return *id_;
 | 
				
			||||||
 | 
					    return defaultValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const std::shared_ptr<uint64_t> &Tokens::getId() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return id_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void Tokens::setId(const uint64_t &pId) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    id_ = std::make_shared<uint64_t>(pId);
 | 
				
			||||||
 | 
					    dirtyFlag_[0] = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const typename Tokens::PrimaryKeyType & Tokens::getPrimaryKey() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(id_);
 | 
				
			||||||
 | 
					    return *id_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint64_t &Tokens::getValueOfOwnerId() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const static uint64_t defaultValue = uint64_t();
 | 
				
			||||||
 | 
					    if(ownerId_)
 | 
				
			||||||
 | 
					        return *ownerId_;
 | 
				
			||||||
 | 
					    return defaultValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const std::shared_ptr<uint64_t> &Tokens::getOwnerId() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return ownerId_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void Tokens::setOwnerId(const uint64_t &pOwnerId) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ownerId_ = std::make_shared<uint64_t>(pOwnerId);
 | 
				
			||||||
 | 
					    dirtyFlag_[1] = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint64_t &Tokens::getValueOfExp() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const static uint64_t defaultValue = uint64_t();
 | 
				
			||||||
 | 
					    if(exp_)
 | 
				
			||||||
 | 
					        return *exp_;
 | 
				
			||||||
 | 
					    return defaultValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const std::shared_ptr<uint64_t> &Tokens::getExp() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return exp_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void Tokens::setExp(const uint64_t &pExp) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    exp_ = std::make_shared<uint64_t>(pExp);
 | 
				
			||||||
 | 
					    dirtyFlag_[2] = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Tokens::updateId(const uint64_t id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    id_ = std::make_shared<uint64_t>(id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::vector<std::string> &Tokens::insertColumns() noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    static const std::vector<std::string> inCols={
 | 
				
			||||||
 | 
					        "owner_id",
 | 
				
			||||||
 | 
					        "exp"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return inCols;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Tokens::outputArgs(drogon::orm::internal::SqlBinder &binder) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(getOwnerId())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << getValueOfOwnerId();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(getExp())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << getValueOfExp();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::vector<std::string> Tokens::updateColumns() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::vector<std::string> ret;
 | 
				
			||||||
 | 
					    if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret.push_back(getColumnName(1));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret.push_back(getColumnName(2));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Tokens::updateArgs(drogon::orm::internal::SqlBinder &binder) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(getOwnerId())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << getValueOfOwnerId();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(getExp())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << getValueOfExp();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            binder << nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Json::Value Tokens::toJson() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Json::Value ret;
 | 
				
			||||||
 | 
					    if(getId())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["id"]=(Json::UInt64)getValueOfId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["id"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(getOwnerId())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["owner_id"]=(Json::UInt64)getValueOfOwnerId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["owner_id"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(getExp())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["exp"]=(Json::UInt64)getValueOfExp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["exp"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Json::Value Tokens::toMasqueradedJson(
 | 
				
			||||||
 | 
					    const std::vector<std::string> &pMasqueradingVector) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Json::Value ret;
 | 
				
			||||||
 | 
					    if(pMasqueradingVector.size() == 3)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!pMasqueradingVector[0].empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(getId())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[0]]=(Json::UInt64)getValueOfId();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[0]]=Json::Value();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!pMasqueradingVector[1].empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(getOwnerId())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[1]]=(Json::UInt64)getValueOfOwnerId();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[1]]=Json::Value();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!pMasqueradingVector[2].empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(getExp())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[2]]=(Json::UInt64)getValueOfExp();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ret[pMasqueradingVector[2]]=Json::Value();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    LOG_ERROR << "Masquerade failed";
 | 
				
			||||||
 | 
					    if(getId())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["id"]=(Json::UInt64)getValueOfId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["id"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(getOwnerId())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["owner_id"]=(Json::UInt64)getValueOfOwnerId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["owner_id"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(getExp())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["exp"]=(Json::UInt64)getValueOfExp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret["exp"]=Json::Value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Tokens::validateJsonForCreation(const Json::Value &pJson, std::string &err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pJson.isMember("id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(0, "id", pJson["id"], err, true))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("owner_id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(1, "owner_id", pJson["owner_id"], err, true))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err="The owner_id column cannot be null";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("exp"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(2, "exp", pJson["exp"], err, true))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err="The exp column cannot be null";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool Tokens::validateMasqueradedJsonForCreation(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                                const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                                std::string &err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pMasqueradingVector.size() != 3)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err = "Bad masquerading vector";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[0].empty())
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(pJson.isMember(pMasqueradingVector[0]))
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					              if(!validJsonOfField(0, pMasqueradingVector[0], pJson[pMasqueradingVector[0]], err, true))
 | 
				
			||||||
 | 
					                  return false;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[1].empty())
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(pJson.isMember(pMasqueradingVector[1]))
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					              if(!validJsonOfField(1, pMasqueradingVector[1], pJson[pMasqueradingVector[1]], err, true))
 | 
				
			||||||
 | 
					                  return false;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            err="The " + pMasqueradingVector[1] + " column cannot be null";
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[2].empty())
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(pJson.isMember(pMasqueradingVector[2]))
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					              if(!validJsonOfField(2, pMasqueradingVector[2], pJson[pMasqueradingVector[2]], err, true))
 | 
				
			||||||
 | 
					                  return false;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            err="The " + pMasqueradingVector[2] + " column cannot be null";
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    catch(const Json::LogicError &e)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      err = e.what();
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool Tokens::validateJsonForUpdate(const Json::Value &pJson, std::string &err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pJson.isMember("id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(0, "id", pJson["id"], err, false))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err = "The value of primary key must be set in the json object for update";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("owner_id"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(1, "owner_id", pJson["owner_id"], err, false))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(pJson.isMember("exp"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(!validJsonOfField(2, "exp", pJson["exp"], err, false))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool Tokens::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                              const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                              std::string &err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if(pMasqueradingVector.size() != 3)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err = "Bad masquerading vector";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(!validJsonOfField(0, pMasqueradingVector[0], pJson[pMasqueradingVector[0]], err, false))
 | 
				
			||||||
 | 
					              return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        err = "The value of primary key must be set in the json object for update";
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(!validJsonOfField(1, pMasqueradingVector[1], pJson[pMasqueradingVector[1]], err, false))
 | 
				
			||||||
 | 
					              return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					          if(!validJsonOfField(2, pMasqueradingVector[2], pJson[pMasqueradingVector[2]], err, false))
 | 
				
			||||||
 | 
					              return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    catch(const Json::LogicError &e)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      err = e.what();
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool Tokens::validJsonOfField(size_t index,
 | 
				
			||||||
 | 
					                              const std::string &fieldName,
 | 
				
			||||||
 | 
					                              const Json::Value &pJson,
 | 
				
			||||||
 | 
					                              std::string &err,
 | 
				
			||||||
 | 
					                              bool isForCreation)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    switch(index)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        case 0:
 | 
				
			||||||
 | 
					            if(pJson.isNull())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="The " + fieldName + " column cannot be null";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(isForCreation)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="The automatic primary key cannot be set";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(!pJson.isUInt64())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="Type error in the "+fieldName+" field";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 1:
 | 
				
			||||||
 | 
					            if(pJson.isNull())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="The " + fieldName + " column cannot be null";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(!pJson.isUInt64())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="Type error in the "+fieldName+" field";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 2:
 | 
				
			||||||
 | 
					            if(pJson.isNull())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="The " + fieldName + " column cannot be null";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(!pJson.isUInt64())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                err="Type error in the "+fieldName+" field";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            err="Internal error in the server";
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										211
									
								
								backend/src/db/model/Tokens.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								backend/src/db/model/Tokens.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Tokens.h
 | 
				
			||||||
 | 
					 *  DO NOT EDIT. This file is generated by drogon_ctl
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <drogon/orm/Result.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Row.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Field.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/SqlBinder.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Mapper.h>
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					#include <drogon/orm/CoroMapper.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include <trantor/utils/Date.h>
 | 
				
			||||||
 | 
					#include <trantor/utils/Logger.h>
 | 
				
			||||||
 | 
					#include <json/json.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace drogon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace orm
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					class DbClient;
 | 
				
			||||||
 | 
					using DbClientPtr = std::shared_ptr<DbClient>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					namespace drogon_model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace sqlite3
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tokens
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    struct Cols
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string _id;
 | 
				
			||||||
 | 
					        static const std::string _owner_id;
 | 
				
			||||||
 | 
					        static const std::string _exp;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const static int primaryKeyNumber;
 | 
				
			||||||
 | 
					    const static std::string tableName;
 | 
				
			||||||
 | 
					    const static bool hasPrimaryKey;
 | 
				
			||||||
 | 
					    const static std::string primaryKeyName;
 | 
				
			||||||
 | 
					    using PrimaryKeyType = uint64_t;
 | 
				
			||||||
 | 
					    const PrimaryKeyType &getPrimaryKey() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param r One row of records in the SQL query result.
 | 
				
			||||||
 | 
					     * @param indexOffset Set the offset to -1 to access all columns by column names,
 | 
				
			||||||
 | 
					     * otherwise access all columns by offsets.
 | 
				
			||||||
 | 
					     * @note If the SQL is not a style of 'select * from table_name ...' (select all
 | 
				
			||||||
 | 
					     * columns by an asterisk), please set the offset to -1.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit Tokens(const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit Tokens(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     * @param pMasqueradingVector The aliases of table columns.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Tokens(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Tokens() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void updateByJson(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					    void updateByMasqueradedJson(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                 const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					    static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForCreation(const Json::Value &,
 | 
				
			||||||
 | 
					                                                const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                                    std::string &err);
 | 
				
			||||||
 | 
					    static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForUpdate(const Json::Value &,
 | 
				
			||||||
 | 
					                                          const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                          std::string &err);
 | 
				
			||||||
 | 
					    static bool validJsonOfField(size_t index,
 | 
				
			||||||
 | 
					                          const std::string &fieldName,
 | 
				
			||||||
 | 
					                          const Json::Value &pJson,
 | 
				
			||||||
 | 
					                          std::string &err,
 | 
				
			||||||
 | 
					                          bool isForCreation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column id
 | 
				
			||||||
 | 
					    void setId(const uint64_t &pId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column owner_id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column owner_id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfOwnerId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getOwnerId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column owner_id
 | 
				
			||||||
 | 
					    void setOwnerId(const uint64_t &pOwnerId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column exp  */
 | 
				
			||||||
 | 
					    ///Get the value of the column exp, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfExp() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getExp() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column exp
 | 
				
			||||||
 | 
					    void setExp(const uint64_t &pExp) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static size_t getColumnNumber() noexcept {  return 3;  }
 | 
				
			||||||
 | 
					    static const std::string &getColumnName(size_t index) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Json::Value toJson() const;
 | 
				
			||||||
 | 
					    Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
 | 
				
			||||||
 | 
					    /// Relationship interfaces
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    friend drogon::orm::Mapper<Tokens>;
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					    friend drogon::orm::CoroMapper<Tokens>;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    static const std::vector<std::string> &insertColumns() noexcept;
 | 
				
			||||||
 | 
					    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    const std::vector<std::string> updateColumns() const;
 | 
				
			||||||
 | 
					    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    ///For mysql or sqlite3
 | 
				
			||||||
 | 
					    void updateId(const uint64_t id);
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> id_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> ownerId_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> exp_;
 | 
				
			||||||
 | 
					    struct MetaData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const std::string colName_;
 | 
				
			||||||
 | 
					        const std::string colType_;
 | 
				
			||||||
 | 
					        const std::string colDatabaseType_;
 | 
				
			||||||
 | 
					        const ssize_t colLength_;
 | 
				
			||||||
 | 
					        const bool isAutoVal_;
 | 
				
			||||||
 | 
					        const bool isPrimaryKey_;
 | 
				
			||||||
 | 
					        const bool notNull_;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    static const std::vector<MetaData> metaData_;
 | 
				
			||||||
 | 
					    bool dirtyFlag_[3]={ false };
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    static const std::string &sqlForFindingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="select * from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const std::string &sqlForDeletingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="delete from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::string sqlForInserting(bool &needSelection) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string sql="insert into " + tableName + " (";
 | 
				
			||||||
 | 
					        size_t parametersCount = 0;
 | 
				
			||||||
 | 
					        needSelection = false;
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "owner_id,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "exp,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql[sql.length()-1]=')';
 | 
				
			||||||
 | 
					            sql += " values (";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            sql += ") values (";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.resize(sql.length() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sql.append(1, ')');
 | 
				
			||||||
 | 
					        LOG_TRACE << sql;
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace sqlite3
 | 
				
			||||||
 | 
					} // namespace drogon_model
 | 
				
			||||||
							
								
								
									
										1762
									
								
								backend/src/db/model/User.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1762
									
								
								backend/src/db/model/User.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										361
									
								
								backend/src/db/model/User.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								backend/src/db/model/User.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,361 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  User.h
 | 
				
			||||||
 | 
					 *  DO NOT EDIT. This file is generated by drogon_ctl
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <drogon/orm/Result.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Row.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Field.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/SqlBinder.h>
 | 
				
			||||||
 | 
					#include <drogon/orm/Mapper.h>
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					#include <drogon/orm/CoroMapper.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include <trantor/utils/Date.h>
 | 
				
			||||||
 | 
					#include <trantor/utils/Logger.h>
 | 
				
			||||||
 | 
					#include <json/json.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace drogon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace orm
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					class DbClient;
 | 
				
			||||||
 | 
					using DbClientPtr = std::shared_ptr<DbClient>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					namespace drogon_model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					namespace sqlite3
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class User
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    struct Cols
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string _id;
 | 
				
			||||||
 | 
					        static const std::string _gitlab;
 | 
				
			||||||
 | 
					        static const std::string _name;
 | 
				
			||||||
 | 
					        static const std::string _password;
 | 
				
			||||||
 | 
					        static const std::string _role;
 | 
				
			||||||
 | 
					        static const std::string _root_id;
 | 
				
			||||||
 | 
					        static const std::string _tfa_type;
 | 
				
			||||||
 | 
					        static const std::string _tfa_secret;
 | 
				
			||||||
 | 
					        static const std::string _gitlab_at;
 | 
				
			||||||
 | 
					        static const std::string _gitlab_rt;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const static int primaryKeyNumber;
 | 
				
			||||||
 | 
					    const static std::string tableName;
 | 
				
			||||||
 | 
					    const static bool hasPrimaryKey;
 | 
				
			||||||
 | 
					    const static std::string primaryKeyName;
 | 
				
			||||||
 | 
					    using PrimaryKeyType = uint64_t;
 | 
				
			||||||
 | 
					    const PrimaryKeyType &getPrimaryKey() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param r One row of records in the SQL query result.
 | 
				
			||||||
 | 
					     * @param indexOffset Set the offset to -1 to access all columns by column names,
 | 
				
			||||||
 | 
					     * otherwise access all columns by offsets.
 | 
				
			||||||
 | 
					     * @note If the SQL is not a style of 'select * from table_name ...' (select all
 | 
				
			||||||
 | 
					     * columns by an asterisk), please set the offset to -1.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit User(const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    explicit User(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @brief constructor
 | 
				
			||||||
 | 
					     * @param pJson The json object to construct a new instance.
 | 
				
			||||||
 | 
					     * @param pMasqueradingVector The aliases of table columns.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    User(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    User() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void updateByJson(const Json::Value &pJson) noexcept(false);
 | 
				
			||||||
 | 
					    void updateByMasqueradedJson(const Json::Value &pJson,
 | 
				
			||||||
 | 
					                                 const std::vector<std::string> &pMasqueradingVector) noexcept(false);
 | 
				
			||||||
 | 
					    static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForCreation(const Json::Value &,
 | 
				
			||||||
 | 
					                                                const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                                    std::string &err);
 | 
				
			||||||
 | 
					    static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
 | 
				
			||||||
 | 
					    static bool validateMasqueradedJsonForUpdate(const Json::Value &,
 | 
				
			||||||
 | 
					                                          const std::vector<std::string> &pMasqueradingVector,
 | 
				
			||||||
 | 
					                                          std::string &err);
 | 
				
			||||||
 | 
					    static bool validJsonOfField(size_t index,
 | 
				
			||||||
 | 
					                          const std::string &fieldName,
 | 
				
			||||||
 | 
					                          const Json::Value &pJson,
 | 
				
			||||||
 | 
					                          std::string &err,
 | 
				
			||||||
 | 
					                          bool isForCreation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column id
 | 
				
			||||||
 | 
					    void setId(const uint64_t &pId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column gitlab  */
 | 
				
			||||||
 | 
					    ///Get the value of the column gitlab, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfGitlab() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getGitlab() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column gitlab
 | 
				
			||||||
 | 
					    void setGitlab(const uint64_t &pGitlab) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column name  */
 | 
				
			||||||
 | 
					    ///Get the value of the column name, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::string &getValueOfName() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::string> &getName() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column name
 | 
				
			||||||
 | 
					    void setName(const std::string &pName) noexcept;
 | 
				
			||||||
 | 
					    void setName(std::string &&pName) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column password  */
 | 
				
			||||||
 | 
					    ///Get the value of the column password, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::string &getValueOfPassword() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::string> &getPassword() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column password
 | 
				
			||||||
 | 
					    void setPassword(const std::string &pPassword) noexcept;
 | 
				
			||||||
 | 
					    void setPassword(std::string &&pPassword) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column role  */
 | 
				
			||||||
 | 
					    ///Get the value of the column role, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfRole() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getRole() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column role
 | 
				
			||||||
 | 
					    void setRole(const uint64_t &pRole) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column root_id  */
 | 
				
			||||||
 | 
					    ///Get the value of the column root_id, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfRootId() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getRootId() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column root_id
 | 
				
			||||||
 | 
					    void setRootId(const uint64_t &pRootId) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column tfa_type  */
 | 
				
			||||||
 | 
					    ///Get the value of the column tfa_type, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const uint64_t &getValueOfTfaType() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<uint64_t> &getTfaType() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column tfa_type
 | 
				
			||||||
 | 
					    void setTfaType(const uint64_t &pTfaType) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column tfa_secret  */
 | 
				
			||||||
 | 
					    ///Get the value of the column tfa_secret, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::vector<char> &getValueOfTfaSecret() const noexcept;
 | 
				
			||||||
 | 
					    ///Return the column value by std::string with binary data
 | 
				
			||||||
 | 
					    std::string getValueOfTfaSecretAsString() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::vector<char>> &getTfaSecret() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column tfa_secret
 | 
				
			||||||
 | 
					    void setTfaSecret(const std::vector<char> &pTfaSecret) noexcept;
 | 
				
			||||||
 | 
					    void setTfaSecret(const std::string &pTfaSecret) noexcept;
 | 
				
			||||||
 | 
					    void setTfaSecretToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column gitlab_at  */
 | 
				
			||||||
 | 
					    ///Get the value of the column gitlab_at, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::string &getValueOfGitlabAt() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::string> &getGitlabAt() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column gitlab_at
 | 
				
			||||||
 | 
					    void setGitlabAt(const std::string &pGitlabAt) noexcept;
 | 
				
			||||||
 | 
					    void setGitlabAt(std::string &&pGitlabAt) noexcept;
 | 
				
			||||||
 | 
					    void setGitlabAtToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**  For column gitlab_rt  */
 | 
				
			||||||
 | 
					    ///Get the value of the column gitlab_rt, returns the default value if the column is null
 | 
				
			||||||
 | 
					    const std::string &getValueOfGitlabRt() const noexcept;
 | 
				
			||||||
 | 
					    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
				
			||||||
 | 
					    const std::shared_ptr<std::string> &getGitlabRt() const noexcept;
 | 
				
			||||||
 | 
					    ///Set the value of the column gitlab_rt
 | 
				
			||||||
 | 
					    void setGitlabRt(const std::string &pGitlabRt) noexcept;
 | 
				
			||||||
 | 
					    void setGitlabRt(std::string &&pGitlabRt) noexcept;
 | 
				
			||||||
 | 
					    void setGitlabRtToNull() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static size_t getColumnNumber() noexcept {  return 10;  }
 | 
				
			||||||
 | 
					    static const std::string &getColumnName(size_t index) noexcept(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Json::Value toJson() const;
 | 
				
			||||||
 | 
					    Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
 | 
				
			||||||
 | 
					    /// Relationship interfaces
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    friend drogon::orm::Mapper<User>;
 | 
				
			||||||
 | 
					#ifdef __cpp_impl_coroutine
 | 
				
			||||||
 | 
					    friend drogon::orm::CoroMapper<User>;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    static const std::vector<std::string> &insertColumns() noexcept;
 | 
				
			||||||
 | 
					    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    const std::vector<std::string> updateColumns() const;
 | 
				
			||||||
 | 
					    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
 | 
				
			||||||
 | 
					    ///For mysql or sqlite3
 | 
				
			||||||
 | 
					    void updateId(const uint64_t id);
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> id_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> gitlab_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::string> name_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::string> password_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> role_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> rootId_;
 | 
				
			||||||
 | 
					    std::shared_ptr<uint64_t> tfaType_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::vector<char>> tfaSecret_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::string> gitlabAt_;
 | 
				
			||||||
 | 
					    std::shared_ptr<std::string> gitlabRt_;
 | 
				
			||||||
 | 
					    struct MetaData
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const std::string colName_;
 | 
				
			||||||
 | 
					        const std::string colType_;
 | 
				
			||||||
 | 
					        const std::string colDatabaseType_;
 | 
				
			||||||
 | 
					        const ssize_t colLength_;
 | 
				
			||||||
 | 
					        const bool isAutoVal_;
 | 
				
			||||||
 | 
					        const bool isPrimaryKey_;
 | 
				
			||||||
 | 
					        const bool notNull_;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    static const std::vector<MetaData> metaData_;
 | 
				
			||||||
 | 
					    bool dirtyFlag_[10]={ false };
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    static const std::string &sqlForFindingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="select * from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const std::string &sqlForDeletingByPrimaryKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static const std::string sql="delete from " + tableName + " where id = ?";
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::string sqlForInserting(bool &needSelection) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string sql="insert into " + tableName + " (";
 | 
				
			||||||
 | 
					        size_t parametersCount = 0;
 | 
				
			||||||
 | 
					        needSelection = false;
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "gitlab,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "name,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[3])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "password,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[4])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "role,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[5])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "root_id,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[6])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "tfa_type,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[7])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "tfa_secret,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[8])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "gitlab_at,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[9])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql += "gitlab_rt,";
 | 
				
			||||||
 | 
					            ++parametersCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql[sql.length()-1]=')';
 | 
				
			||||||
 | 
					            sql += " values (";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            sql += ") values (";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(dirtyFlag_[1])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[2])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[3])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[4])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[5])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[6])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[7])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[8])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(dirtyFlag_[9])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.append("?,");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(parametersCount > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sql.resize(sql.length() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sql.append(1, ')');
 | 
				
			||||||
 | 
					        LOG_TRACE << sql;
 | 
				
			||||||
 | 
					        return sql;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace sqlite3
 | 
				
			||||||
 | 
					} // namespace drogon_model
 | 
				
			||||||
							
								
								
									
										5
									
								
								backend/src/db/model/model.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								backend/src/db/model/model.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "rdbms":"sqlite3",
 | 
				
			||||||
 | 
					  "filename":"run/sqlite.db",
 | 
				
			||||||
 | 
					  "tables":[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										56
									
								
								backend/src/dto/dto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								backend/src/dto/dto.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					#ifndef BACKEND_DTO_H
 | 
				
			||||||
 | 
					#define BACKEND_DTO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drogon/HttpResponse.h>
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace dto {
 | 
				
			||||||
 | 
					    template<typename T>
 | 
				
			||||||
 | 
					    std::optional<T> json_get(const Json::Value& j, const std::string& key) {
 | 
				
			||||||
 | 
					        return j.isMember(key)
 | 
				
			||||||
 | 
					            ? std::make_optional(j[key].as<T>())
 | 
				
			||||||
 | 
					            : std::nullopt;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inline db::User get_user(const drogon::HttpRequestPtr& req) {
 | 
				
			||||||
 | 
					        return req->attributes()->get<db::User>("user");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inline db::Token get_token(const drogon::HttpRequestPtr& req) {
 | 
				
			||||||
 | 
					        return req->attributes()->get<db::Token>("token");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    namespace Responses {
 | 
				
			||||||
 | 
					        struct GetUsersEntry {
 | 
				
			||||||
 | 
					            GetUsersEntry(int id, bool gitlab, bool tfa, std::string name, db::UserRole role)
 | 
				
			||||||
 | 
					                : id(id), gitlab(gitlab), tfa(tfa), name(std::move(name)), role(role) {}
 | 
				
			||||||
 | 
					            int id;
 | 
				
			||||||
 | 
					            bool gitlab, tfa;
 | 
				
			||||||
 | 
					            std::string name;
 | 
				
			||||||
 | 
					            db::UserRole role;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_error_res(drogon::HttpStatusCode, const std::string &msg);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_success_res();
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_success_res(Json::Value &);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inline drogon::HttpResponsePtr get_badreq_res(const std::string &msg) { return get_error_res(drogon::HttpStatusCode::k400BadRequest, msg); }
 | 
				
			||||||
 | 
					        inline drogon::HttpResponsePtr get_unauth_res(const std::string &msg) { return get_error_res(drogon::HttpStatusCode::k401Unauthorized, msg); }
 | 
				
			||||||
 | 
					        inline drogon::HttpResponsePtr get_forbdn_res(const std::string &msg) { return get_error_res(drogon::HttpStatusCode::k403Forbidden, msg); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_login_res(const std::string &jwt);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_tfa_setup_res(const std::string& secret, const std::string& qrcode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_user_info_res(const std::string& name, bool gitlab, bool tfa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_admin_users_res(const std::vector<GetUsersEntry>& users);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_root_res(uint64_t root);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_node_folder_res(uint64_t id, const std::string& name, const std::shared_ptr<uint64_t>& parent, const std::vector<uint64_t>& children);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_node_file_res(uint64_t id, const std::string& name, const std::shared_ptr<uint64_t>& parent, uint64_t size);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_path_res(const std::string& path);
 | 
				
			||||||
 | 
					        drogon::HttpResponsePtr get_new_node_res(uint64_t id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //BACKEND_DTO_H
 | 
				
			||||||
							
								
								
									
										98
									
								
								backend/src/dto/responses.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								backend/src/dto/responses.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					#include "dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace dto::Responses {
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_error_res(drogon::HttpStatusCode code, const std::string& msg) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["statusCode"] = static_cast<int>(code);
 | 
				
			||||||
 | 
					        json["message"] = msg;
 | 
				
			||||||
 | 
					        auto res = drogon::HttpResponse::newHttpJsonResponse(json);
 | 
				
			||||||
 | 
					        res->setStatusCode(code);
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_success_res() {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_success_res(Json::Value& json) {
 | 
				
			||||||
 | 
					        json["statusCode"] = 200;
 | 
				
			||||||
 | 
					        auto res = drogon::HttpResponse::newHttpJsonResponse(json);
 | 
				
			||||||
 | 
					        res->setStatusCode(drogon::HttpStatusCode::k200OK);
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_login_res(const std::string &jwt) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["jwt"] = jwt;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_tfa_setup_res(const std::string& secret, const std::string& qrcode) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["secret"] = secret;
 | 
				
			||||||
 | 
					        json["qrCode"] = qrcode;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_user_info_res(const std::string &name, bool gitlab, bool tfa) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["name"] = name;
 | 
				
			||||||
 | 
					        json["gitlab"] = gitlab;
 | 
				
			||||||
 | 
					        json["tfaEnabled"] = tfa;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_admin_users_res(const std::vector<GetUsersEntry>& users) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        for (const GetUsersEntry& user : users) {
 | 
				
			||||||
 | 
					            Json::Value entry;
 | 
				
			||||||
 | 
					            entry["id"] = user.id;
 | 
				
			||||||
 | 
					            entry["gitlab"] = user.gitlab;
 | 
				
			||||||
 | 
					            entry["name"] = user.name;
 | 
				
			||||||
 | 
					            entry["role"] = user.role;
 | 
				
			||||||
 | 
					            entry["tfaEnabled"] = user.tfa;
 | 
				
			||||||
 | 
					            json["users"].append(entry);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_root_res(uint64_t root) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["rootId"] = root;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_node_folder_res(uint64_t id, const std::string &name, const std::shared_ptr<uint64_t> &parent, const std::vector<uint64_t> &children) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["id"] = id;
 | 
				
			||||||
 | 
					        json["name"] = name;
 | 
				
			||||||
 | 
					        json["isFile"] = false;
 | 
				
			||||||
 | 
					        json["parent"] = (parent != nullptr) ? *parent : Json::Value::nullSingleton();
 | 
				
			||||||
 | 
					        for (uint64_t child : children)
 | 
				
			||||||
 | 
					            json["children"].append(child);
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_node_file_res(uint64_t id, const std::string &name, const std::shared_ptr<uint64_t> &parent, uint64_t size) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["id"] = id;
 | 
				
			||||||
 | 
					        json["name"] = name;
 | 
				
			||||||
 | 
					        json["isFile"] = true;
 | 
				
			||||||
 | 
					        json["parent"] = (parent != nullptr) ? *parent : Json::Value::nullSingleton();
 | 
				
			||||||
 | 
					        json["size"] = size;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_path_res(const std::string& path) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["path"] = path;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::HttpResponsePtr get_new_node_res(uint64_t id) {
 | 
				
			||||||
 | 
					        Json::Value json;
 | 
				
			||||||
 | 
					        json["id"] = id;
 | 
				
			||||||
 | 
					        return get_success_res(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										82
									
								
								backend/src/filters/filters.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								backend/src/filters/filters.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					#include "filters.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drogon/utils/coroutine.h>
 | 
				
			||||||
 | 
					#include <jwt-cpp/traits/kazuho-picojson/traits.h>
 | 
				
			||||||
 | 
					#include <jwt-cpp/jwt.h>
 | 
				
			||||||
 | 
					#include "db/db.h"
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					#include "controllers/controllers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void cleanup_tokens(db::MapperToken& mapper) {
 | 
				
			||||||
 | 
					    const uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
 | 
				
			||||||
 | 
					    mapper.deleteBy(
 | 
				
			||||||
 | 
					            db::Criteria(db::Token::Cols::_exp, db::CompareOps::LE, now)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Login::doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& cb, drogon::FilterChainCallback&& ccb) {
 | 
				
			||||||
 | 
					    std::string token_str;
 | 
				
			||||||
 | 
					    if (req->path() == "/api/fs/download") {
 | 
				
			||||||
 | 
					        token_str = req->getParameter("jwtToken");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        std::string auth_header = req->getHeader("Authorization");
 | 
				
			||||||
 | 
					        if (auth_header.empty() || (!auth_header.starts_with("Bearer ")))
 | 
				
			||||||
 | 
					            return cb(dto::Responses::get_unauth_res("Unauthorized"));
 | 
				
			||||||
 | 
					        token_str = auth_header.substr(7);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        auto token = jwt::decode<jwt::traits::kazuho_picojson>(token_str);
 | 
				
			||||||
 | 
					        jwt::verify<jwt::traits::kazuho_picojson>()
 | 
				
			||||||
 | 
					                .allow_algorithm(jwt::algorithm::hs256{jwt_secret})
 | 
				
			||||||
 | 
					                .verify(token);
 | 
				
			||||||
 | 
					        uint64_t token_id = token.get_payload_claim("jti").as_int();
 | 
				
			||||||
 | 
					        uint64_t user_id = token.get_payload_claim("sub").as_int();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto db = drogon::app().getDbClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::MapperUser user_mapper(db);
 | 
				
			||||||
 | 
					        db::MapperToken token_mapper(db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cleanup_tokens(token_mapper);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db::Token db_token = token_mapper.findByPrimaryKey(token_id);
 | 
				
			||||||
 | 
					        db::User db_user = user_mapper.findByPrimaryKey(db_token.getValueOfOwnerId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (db_user.getValueOfId() != user_id) throw std::exception();
 | 
				
			||||||
 | 
					        if (db::User_getEnumRole(db_user) == db::UserRole::DISABLED) throw std::exception();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (db_user.getValueOfGitlab() != 0) {
 | 
				
			||||||
 | 
					            auto info = api::auth::get_gitlab_user(db_user.getValueOfGitlabAt());
 | 
				
			||||||
 | 
					            if (!info.has_value()) {
 | 
				
			||||||
 | 
					                auto tokens = api::auth::get_gitlab_tokens(req, db_user.getValueOfGitlabRt(), true);
 | 
				
			||||||
 | 
					                info = api::auth::get_gitlab_user(tokens->at);
 | 
				
			||||||
 | 
					                if (!tokens.has_value() || !info.has_value()) {
 | 
				
			||||||
 | 
					                    api::auth::revoke_all(db_user);
 | 
				
			||||||
 | 
					                    throw std::exception();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                db_user.setGitlabAt(tokens->at);
 | 
				
			||||||
 | 
					                db_user.setGitlabRt(tokens->rt);
 | 
				
			||||||
 | 
					                user_mapper.update(db_user);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (info->name != db_user.getValueOfName()) {
 | 
				
			||||||
 | 
					                api::auth::revoke_all(db_user);
 | 
				
			||||||
 | 
					                throw std::exception();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req->attributes()->insert("token", db_token);
 | 
				
			||||||
 | 
					        req->attributes()->insert("user", db_user);
 | 
				
			||||||
 | 
					        ccb();
 | 
				
			||||||
 | 
					    } catch (const std::exception&) {
 | 
				
			||||||
 | 
					        cb(dto::Responses::get_unauth_res("Unauthorized"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Admin::doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& cb, drogon::FilterChainCallback&& ccb) {
 | 
				
			||||||
 | 
					    db::User user = dto::get_user(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (db::User_getEnumRole(user) != db::UserRole::ADMIN)
 | 
				
			||||||
 | 
					        cb(dto::Responses::get_forbdn_res("Forbidden"));
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        ccb();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								backend/src/filters/filters.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								backend/src/filters/filters.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					#ifndef BACKEND_FILTERS_H
 | 
				
			||||||
 | 
					#define BACKEND_FILTERS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drogon/HttpFilter.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Login : public drogon::HttpFilter<Login> {
 | 
				
			||||||
 | 
					    void doFilter(const drogon::HttpRequestPtr&, drogon::FilterCallback&&, drogon::FilterChainCallback&&) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Admin : public drogon::HttpFilter<Admin> {
 | 
				
			||||||
 | 
					    void doFilter(const drogon::HttpRequestPtr&, drogon::FilterCallback&&, drogon::FilterChainCallback&&) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //BACKEND_FILTERS_H
 | 
				
			||||||
							
								
								
									
										112
									
								
								backend/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								backend/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					#include <filesystem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drogon/drogon.h>
 | 
				
			||||||
 | 
					#include <curl/curl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "dto/dto.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void cleanup() {
 | 
				
			||||||
 | 
					    std::cout << "Stopping..." << std::endl;
 | 
				
			||||||
 | 
					    drogon::app().quit();
 | 
				
			||||||
 | 
					    std::cout << "Cleanup up uploads...";
 | 
				
			||||||
 | 
					    std::filesystem::remove_all("uploads");
 | 
				
			||||||
 | 
					    std::cout << " [Done]" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "Goodbye!" << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main() {
 | 
				
			||||||
 | 
					    std::cout << "Setting up..." << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "Initializing curl..." << std::flush;
 | 
				
			||||||
 | 
					    curl_global_init(CURL_GLOBAL_ALL);
 | 
				
			||||||
 | 
					    std::cout << " [Done]" << std::endl;
 | 
				
			||||||
 | 
					    if (!std::filesystem::exists("files")) {
 | 
				
			||||||
 | 
					        std::cout << "Creating files..." << std::flush;
 | 
				
			||||||
 | 
					        std::filesystem::create_directory("files");
 | 
				
			||||||
 | 
					        std::cout << " [Done]" << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!std::filesystem::exists("logs")) {
 | 
				
			||||||
 | 
					        std::cout << "Creating logs..." << std::flush;
 | 
				
			||||||
 | 
					        std::filesystem::create_directory("logs");
 | 
				
			||||||
 | 
					        std::cout << " [Done]" << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto* loop = drogon::app().getLoop();
 | 
				
			||||||
 | 
					    loop->queueInLoop([]{
 | 
				
			||||||
 | 
					        std::cout << "Starting..." << std::endl;
 | 
				
			||||||
 | 
					        std::cout << "Creating db tables..." << std::flush;
 | 
				
			||||||
 | 
					        auto db = drogon::app().getDbClient();
 | 
				
			||||||
 | 
					        db->execSqlSync("CREATE TABLE IF NOT EXISTS 'tokens' (\n"
 | 
				
			||||||
 | 
					                        "  'id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'owner_id' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'exp' INTEGER NOT NULL\n"
 | 
				
			||||||
 | 
					                        ")");
 | 
				
			||||||
 | 
					        db->execSqlSync("CREATE TABLE IF NOT EXISTS 'user' (\n"
 | 
				
			||||||
 | 
					                        "  'id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'gitlab' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'name' TEXT NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'password' TEXT NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'role' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'root_id' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'tfa_type' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'tfa_secret' BLOB,\n"
 | 
				
			||||||
 | 
					                        "  'gitlab_at' TEXT,\n"
 | 
				
			||||||
 | 
					                        "  'gitlab_rt' TEXT\n"
 | 
				
			||||||
 | 
					                        ")");
 | 
				
			||||||
 | 
					        db->execSqlSync("CREATE TABLE IF NOT EXISTS 'inode' (\n"
 | 
				
			||||||
 | 
					                        "  'id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'is_file' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'name' TEXT,\n"
 | 
				
			||||||
 | 
					                        "  'parent_id' INTEGER,\n"
 | 
				
			||||||
 | 
					                        "  'owner_id' INTEGER NOT NULL,\n"
 | 
				
			||||||
 | 
					                        "  'size' INTEGER\n"
 | 
				
			||||||
 | 
					                        ")");
 | 
				
			||||||
 | 
					        std::cout << " [Done]" << std::endl;
 | 
				
			||||||
 | 
					        std::cout << "Started!" << std::endl;
 | 
				
			||||||
 | 
					        std::cout << "Registered paths: " << std::endl;
 | 
				
			||||||
 | 
					        auto handlers = drogon::app().getHandlersInfo();
 | 
				
			||||||
 | 
					        for (const auto& handler : handlers) {
 | 
				
			||||||
 | 
					            std::cout << "  ";
 | 
				
			||||||
 | 
					            if (std::get<1>(handler) == drogon::HttpMethod::Post) std::cout << "POST ";
 | 
				
			||||||
 | 
					            else std::cout << "GET  ";
 | 
				
			||||||
 | 
					            std::string func = std::get<2>(handler).substr(16);
 | 
				
			||||||
 | 
					            func.resize(30, ' ');
 | 
				
			||||||
 | 
					            std::cout << '[' << func << "] ";
 | 
				
			||||||
 | 
					            std::cout << std::get<0>(handler) << std::endl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        std::cout << "Listening on:" << std::endl;
 | 
				
			||||||
 | 
					        auto listeners = drogon::app().getListeners();
 | 
				
			||||||
 | 
					        for (const auto& listener : listeners) {
 | 
				
			||||||
 | 
					            std::cout << "  " << listener.toIpPort() << std::endl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Json::Value access_logger;
 | 
				
			||||||
 | 
					    access_logger["name"] = "drogon::plugin::AccessLogger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Json::Value config;
 | 
				
			||||||
 | 
					    config["plugins"].append(access_logger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::app()
 | 
				
			||||||
 | 
					            .setClientMaxBodySize(1024L * 1024L * 1024L * 1024L) // 1 TB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .loadConfigJson(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .createDbClient("sqlite3", "", 0, "", "", "", 1, "sqlite.db")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .setCustom404Page(drogon::HttpResponse::newFileResponse("./static/index.html"), false)
 | 
				
			||||||
 | 
					            .setDocumentRoot("./static")
 | 
				
			||||||
 | 
					            .setBrStatic(true)
 | 
				
			||||||
 | 
					            .setStaticFilesCacheTime(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .setLogPath("./logs")
 | 
				
			||||||
 | 
					            .setLogLevel(trantor::Logger::LogLevel::kDebug)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .setIntSignalHandler(cleanup)
 | 
				
			||||||
 | 
					            .setTermSignalHandler(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .addListener("0.0.0.0", 1234)
 | 
				
			||||||
 | 
					            .setThreadNum(2);
 | 
				
			||||||
 | 
					    std::cout << "Setup done!" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drogon::app().run();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								backend/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								backend/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
 | 
				
			||||||
 | 
					  "name": "backend",
 | 
				
			||||||
 | 
					  "version-string": "1.0.0",
 | 
				
			||||||
 | 
					  "dependencies": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "name": "drogon",
 | 
				
			||||||
 | 
					      "features": ["orm", "sqlite3"]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "jwt-cpp",
 | 
				
			||||||
 | 
					    "botan",
 | 
				
			||||||
 | 
					    "curl",
 | 
				
			||||||
 | 
					    "pngpp",
 | 
				
			||||||
 | 
					    "nayuki-qr-code-generator",
 | 
				
			||||||
 | 
					    "libpng"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
export * as Requests from './requests';
 | 
					 | 
				
			||||||
export * as Responses from './responses';
 | 
					 | 
				
			||||||
export {
 | 
					 | 
				
			||||||
	UserRole,
 | 
					 | 
				
			||||||
	validateSync,
 | 
					 | 
				
			||||||
	validateAsync,
 | 
					 | 
				
			||||||
	validateAsyncInline
 | 
					 | 
				
			||||||
} from './utils';
 | 
					 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
import { BaseRequest } from './base';
 | 
					 | 
				
			||||||
import { IsEnum, IsNumber } from 'class-validator';
 | 
					 | 
				
			||||||
import { UserRole } from '../utils';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AdminRequest extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsNumber()
 | 
					 | 
				
			||||||
	user: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class SetUserRole extends AdminRequest {
 | 
					 | 
				
			||||||
	@IsEnum(UserRole)
 | 
					 | 
				
			||||||
	role: UserRole;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class LogoutAll extends AdminRequest {}
 | 
					 | 
				
			||||||
export class DeleteUser extends AdminRequest {}
 | 
					 | 
				
			||||||
export class DisableTfa extends AdminRequest {}
 | 
					 | 
				
			||||||
@@ -1,50 +0,0 @@
 | 
				
			|||||||
import { BaseRequest } from './base';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
	IsBoolean,
 | 
					 | 
				
			||||||
	IsEmail,
 | 
					 | 
				
			||||||
	IsNotEmpty,
 | 
					 | 
				
			||||||
	IsOptional,
 | 
					 | 
				
			||||||
	IsString
 | 
					 | 
				
			||||||
} from 'class-validator';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class SignUpRequest extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsEmail()
 | 
					 | 
				
			||||||
	username: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	password: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class LoginRequest extends SignUpRequest {
 | 
					 | 
				
			||||||
	@IsOptional()
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	otp?: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class TfaSetup extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	mail: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class TfaComplete extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	mail: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	code: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class ChangePasswordRequest extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	oldPassword: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	newPassword: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
import { BaseRequest } from './base';
 | 
					 | 
				
			||||||
import { IsInt, IsNotEmpty, IsString, Min } from 'class-validator';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CreateFolderRequest extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	parent: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class DeleteRequest extends BaseRequest {
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	node: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CreateFileRequest extends CreateFolderRequest {}
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
export * from './base';
 | 
					 | 
				
			||||||
export * as Auth from './auth';
 | 
					 | 
				
			||||||
export * as FS from './fs';
 | 
					 | 
				
			||||||
export * as Admin from './admin';
 | 
					 | 
				
			||||||
@@ -1,61 +0,0 @@
 | 
				
			|||||||
import { SuccessResponse } from './base';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
	IsArray,
 | 
					 | 
				
			||||||
	IsBoolean,
 | 
					 | 
				
			||||||
	IsEnum,
 | 
					 | 
				
			||||||
	IsNotEmpty,
 | 
					 | 
				
			||||||
	IsNumber,
 | 
					 | 
				
			||||||
	IsString,
 | 
					 | 
				
			||||||
	ValidateNested
 | 
					 | 
				
			||||||
} from 'class-validator';
 | 
					 | 
				
			||||||
import { UserRole, ValidateConstructor } from '../utils';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class GetUsersEntry {
 | 
					 | 
				
			||||||
	constructor(
 | 
					 | 
				
			||||||
		id: number,
 | 
					 | 
				
			||||||
		gitlab: boolean,
 | 
					 | 
				
			||||||
		name: string,
 | 
					 | 
				
			||||||
		role: UserRole,
 | 
					 | 
				
			||||||
		tfaEnabled: boolean
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		this.id = id;
 | 
					 | 
				
			||||||
		this.gitlab = gitlab;
 | 
					 | 
				
			||||||
		this.name = name;
 | 
					 | 
				
			||||||
		this.role = role;
 | 
					 | 
				
			||||||
		this.tfaEnabled = tfaEnabled;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNumber()
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	gitlab: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsEnum(UserRole)
 | 
					 | 
				
			||||||
	role: UserRole;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	tfaEnabled: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class GetUsers extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(users: GetUsersEntry[]) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.users = users;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsArray()
 | 
					 | 
				
			||||||
	@ValidateNested({ each: true })
 | 
					 | 
				
			||||||
	users: GetUsersEntry[];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class LogoutAllUser extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class DeleteUser extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class SetUserRole extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class DisableTfa extends SuccessResponse {}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
import { IsNumber, Max, Min } from 'class-validator';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class BaseResponse {
 | 
					 | 
				
			||||||
	constructor(statusCode: number) {
 | 
					 | 
				
			||||||
		this.statusCode = statusCode;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNumber()
 | 
					 | 
				
			||||||
	@Min(100)
 | 
					 | 
				
			||||||
	@Max(599)
 | 
					 | 
				
			||||||
	statusCode: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class SuccessResponse extends BaseResponse {
 | 
					 | 
				
			||||||
	constructor() {
 | 
					 | 
				
			||||||
		super(200);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	declare statusCode: 200;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class ErrorResponse extends BaseResponse {
 | 
					 | 
				
			||||||
	declare statusCode: 400 | 401 | 403;
 | 
					 | 
				
			||||||
	message?: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,89 +0,0 @@
 | 
				
			|||||||
import { SuccessResponse } from './base';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
	IsBoolean,
 | 
					 | 
				
			||||||
	IsInt,
 | 
					 | 
				
			||||||
	IsNotEmpty,
 | 
					 | 
				
			||||||
	IsOptional,
 | 
					 | 
				
			||||||
	IsString,
 | 
					 | 
				
			||||||
	Min
 | 
					 | 
				
			||||||
} from 'class-validator';
 | 
					 | 
				
			||||||
import { ValidateConstructor } from '../utils';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class GetRootResponse extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(rootId: number) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.rootId = rootId;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	rootId: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class GetNodeResponse extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(
 | 
					 | 
				
			||||||
		id: number,
 | 
					 | 
				
			||||||
		name: string,
 | 
					 | 
				
			||||||
		isFile: boolean,
 | 
					 | 
				
			||||||
		parent: number | null
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.id = id;
 | 
					 | 
				
			||||||
		this.name = name;
 | 
					 | 
				
			||||||
		this.isFile = isFile;
 | 
					 | 
				
			||||||
		this.parent = parent;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	isFile: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsOptional()
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	parent: number | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsOptional()
 | 
					 | 
				
			||||||
	@IsInt({ each: true })
 | 
					 | 
				
			||||||
	@Min(1, { each: true })
 | 
					 | 
				
			||||||
	children?: number[];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsOptional()
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(0)
 | 
					 | 
				
			||||||
	size?: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class GetPathResponse extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(path: string) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.path = path;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	path: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class CreateFolderResponse extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(id: number) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.id = id;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsInt()
 | 
					 | 
				
			||||||
	@Min(1)
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class UploadFileResponse extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class DeleteResponse extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class CreateFileResponse extends CreateFolderResponse {}
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
export * from './base';
 | 
					 | 
				
			||||||
export * as Auth from './auth';
 | 
					 | 
				
			||||||
export * as FS from './fs';
 | 
					 | 
				
			||||||
export * as User from './user';
 | 
					 | 
				
			||||||
export * as Admin from './admin';
 | 
					 | 
				
			||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
import { SuccessResponse } from './base';
 | 
					 | 
				
			||||||
import { ValidateConstructor } from '../utils';
 | 
					 | 
				
			||||||
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ValidateConstructor
 | 
					 | 
				
			||||||
export class UserInfoResponse extends SuccessResponse {
 | 
					 | 
				
			||||||
	constructor(name: string, gitlab: boolean, tfaEnabled: boolean) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.name = name;
 | 
					 | 
				
			||||||
		this.gitlab = gitlab;
 | 
					 | 
				
			||||||
		this.tfaEnabled = tfaEnabled;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsNotEmpty()
 | 
					 | 
				
			||||||
	@IsString()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	gitlab: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@IsBoolean()
 | 
					 | 
				
			||||||
	tfaEnabled: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class DeleteUserResponse extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class ChangePasswordResponse extends SuccessResponse {}
 | 
					 | 
				
			||||||
export class LogoutAllResponse extends SuccessResponse {}
 | 
					 | 
				
			||||||
							
								
								
									
										41
									
								
								dto/utils.ts
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								dto/utils.ts
									
									
									
									
									
								
							@@ -1,41 +0,0 @@
 | 
				
			|||||||
import { validate, validateSync as _validateSync } from 'class-validator';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export enum UserRole {
 | 
					 | 
				
			||||||
	ADMIN = 2,
 | 
					 | 
				
			||||||
	USER = 1,
 | 
					 | 
				
			||||||
	DISABLED = 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function validateSync<T extends object>(data: T): void {
 | 
					 | 
				
			||||||
	const errors = _validateSync(data);
 | 
					 | 
				
			||||||
	if (errors.length > 0) {
 | 
					 | 
				
			||||||
		console.error('Validation failed, errors: ', errors);
 | 
					 | 
				
			||||||
		throw new Error('Validation failed');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function validateAsync<T extends object>(data: T): Promise<void> {
 | 
					 | 
				
			||||||
	const errors = await validate(data);
 | 
					 | 
				
			||||||
	if (errors.length > 0) {
 | 
					 | 
				
			||||||
		console.error('Validation failed, errors: ', errors);
 | 
					 | 
				
			||||||
		throw new Error('Validation failed');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function validateAsyncInline<T extends object>(
 | 
					 | 
				
			||||||
	data: T
 | 
					 | 
				
			||||||
): Promise<T> {
 | 
					 | 
				
			||||||
	await validateAsync(data);
 | 
					 | 
				
			||||||
	return data;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function ValidateConstructor<T extends { new (...args: any[]): any }>(
 | 
					 | 
				
			||||||
	constr: T
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
	return class extends constr {
 | 
					 | 
				
			||||||
		constructor(...args: any[]) {
 | 
					 | 
				
			||||||
			super(...args);
 | 
					 | 
				
			||||||
			validateSync(this);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
	presets: ['@vue/cli-plugin-babel/preset']
 | 
					  presets: ["@vue/cli-plugin-babel/preset"],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "axios": "^0.27.2",
 | 
					    "axios": "^0.27.2",
 | 
				
			||||||
 | 
					    "class-transformer": "^0.5.1",
 | 
				
			||||||
 | 
					    "class-validator": "^0.13.2",
 | 
				
			||||||
    "core-js": "^3.8.3",
 | 
					    "core-js": "^3.8.3",
 | 
				
			||||||
    "filesize": "^9.0.11",
 | 
					    "filesize": "^9.0.11",
 | 
				
			||||||
    "jwt-decode": "^3.1.2",
 | 
					    "jwt-decode": "^3.1.2",
 | 
				
			||||||
@@ -27,8 +29,6 @@
 | 
				
			|||||||
    "@vue/cli-plugin-typescript": "~5.0.0",
 | 
					    "@vue/cli-plugin-typescript": "~5.0.0",
 | 
				
			||||||
    "@vue/cli-service": "~5.0.0",
 | 
					    "@vue/cli-service": "~5.0.0",
 | 
				
			||||||
    "@vue/eslint-config-typescript": "^9.1.0",
 | 
					    "@vue/eslint-config-typescript": "^9.1.0",
 | 
				
			||||||
    "class-transformer": "^0.5.1",
 | 
					 | 
				
			||||||
    "class-validator": "^0.13.2",
 | 
					 | 
				
			||||||
    "eslint": "^7.32.0",
 | 
					    "eslint": "^7.32.0",
 | 
				
			||||||
    "eslint-config-prettier": "^8.3.0",
 | 
					    "eslint-config-prettier": "^8.3.0",
 | 
				
			||||||
    "eslint-plugin-prettier": "^4.0.0",
 | 
					    "eslint-plugin-prettier": "^4.0.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,27 @@
 | 
				
			|||||||
<script setup async lang="ts">
 | 
					<script setup async lang="ts">
 | 
				
			||||||
import { provide, ref } from 'vue';
 | 
					import { provide, ref } from "vue";
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from "vue-router";
 | 
				
			||||||
import { TokenInjectType } from '@/api';
 | 
					import { TokenInjectType } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = ref<string | null>(localStorage.getItem('token'));
 | 
					const jwt = ref<string | null>(localStorage.getItem("token"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setToken(token: string) {
 | 
					function setToken(token: string) {
 | 
				
			||||||
  jwt.value = token;
 | 
					  jwt.value = token;
 | 
				
			||||||
	localStorage.setItem('token', token);
 | 
					  localStorage.setItem("token", token);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function logout() {
 | 
					function logout() {
 | 
				
			||||||
  jwt.value = null;
 | 
					  jwt.value = null;
 | 
				
			||||||
	localStorage.removeItem('token');
 | 
					  localStorage.removeItem("token");
 | 
				
			||||||
	router.push({ name: 'login' });
 | 
					  router.push({ name: "login" });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
provide<TokenInjectType>('jwt', {
 | 
					provide<TokenInjectType>("jwt", {
 | 
				
			||||||
  jwt,
 | 
					  jwt,
 | 
				
			||||||
  setToken,
 | 
					  setToken,
 | 
				
			||||||
	logout
 | 
					  logout,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import App from './App';
 | 
					import App from "./App";
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import { Requests, Responses, UserRole, get_token, post_token } from './base';
 | 
					import { Requests, Responses, UserRole, get_token, post_token } from "./base";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get_users = (token: string): Promise<Responses.Admin.GetUsers> =>
 | 
					export const get_users = (token: string): Promise<Responses.Admin.GetUsers> =>
 | 
				
			||||||
	get_token('/api/admin/users', token);
 | 
					  get_token("/api/admin/users", token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const set_role = (
 | 
					export const set_role = (
 | 
				
			||||||
  user: number,
 | 
					  user: number,
 | 
				
			||||||
@@ -9,10 +9,10 @@ export const set_role = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Admin.SetUserRole | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Admin.SetUserRole | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Admin.SetUserRole>(
 | 
					  post_token<Requests.Admin.SetUserRole>(
 | 
				
			||||||
		'/api/admin/set_role',
 | 
					    "/api/admin/set_role",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      user,
 | 
					      user,
 | 
				
			||||||
			role
 | 
					      role,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -22,9 +22,9 @@ export const logout = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Admin.LogoutAllUser | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Admin.LogoutAllUser | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Admin.LogoutAll>(
 | 
					  post_token<Requests.Admin.LogoutAll>(
 | 
				
			||||||
		'/api/admin/logout',
 | 
					    "/api/admin/logout",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
			user
 | 
					      user,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -34,9 +34,9 @@ export const delete_user = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Admin.DeleteUser | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Admin.DeleteUser | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Admin.DeleteUser>(
 | 
					  post_token<Requests.Admin.DeleteUser>(
 | 
				
			||||||
		'/api/admin/delete',
 | 
					    "/api/admin/delete",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
			user
 | 
					      user,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -46,9 +46,9 @@ export const disable_tfa = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Admin.DisableTfa | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Admin.DisableTfa | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Admin.DisableTfa>(
 | 
					  post_token<Requests.Admin.DisableTfa>(
 | 
				
			||||||
		'/api/admin/disable_2fa',
 | 
					    "/api/admin/disable_2fa",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
			user
 | 
					      user,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Responses, Requests, post, post_token } from './base';
 | 
					import { Responses, Requests, post, post_token } from "./base";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const auth_login = (
 | 
					export const auth_login = (
 | 
				
			||||||
  username: string,
 | 
					  username: string,
 | 
				
			||||||
@@ -9,25 +9,25 @@ export const auth_login = (
 | 
				
			|||||||
  | Responses.Auth.TfaRequiredResponse
 | 
					  | Responses.Auth.TfaRequiredResponse
 | 
				
			||||||
  | Responses.ErrorResponse
 | 
					  | Responses.ErrorResponse
 | 
				
			||||||
> =>
 | 
					> =>
 | 
				
			||||||
	post<Requests.Auth.LoginRequest>('/api/auth/login', {
 | 
					  post<Requests.Auth.LoginRequest>("/api/auth/login", {
 | 
				
			||||||
    username: username,
 | 
					    username: username,
 | 
				
			||||||
    password: password,
 | 
					    password: password,
 | 
				
			||||||
		otp: otp
 | 
					    otp: otp,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const auth_signup = (
 | 
					export const auth_signup = (
 | 
				
			||||||
  username: string,
 | 
					  username: string,
 | 
				
			||||||
  password: string
 | 
					  password: string
 | 
				
			||||||
): Promise<Responses.Auth.SignupResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.SignupResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post<Requests.Auth.SignUpRequest>('/api/auth/signup', {
 | 
					  post<Requests.Auth.SignUpRequest>("/api/auth/signup", {
 | 
				
			||||||
    username: username,
 | 
					    username: username,
 | 
				
			||||||
		password: password
 | 
					    password: password,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const refresh_token = (
 | 
					export const refresh_token = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Auth.RefreshResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.RefreshResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post_token('/api/auth/refresh', {}, token);
 | 
					  post_token("/api/auth/refresh", {}, token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const change_password = (
 | 
					export const change_password = (
 | 
				
			||||||
  oldPw: string,
 | 
					  oldPw: string,
 | 
				
			||||||
@@ -35,10 +35,10 @@ export const change_password = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Auth.ChangePasswordResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.ChangePasswordResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Auth.ChangePasswordRequest>(
 | 
					  post_token<Requests.Auth.ChangePasswordRequest>(
 | 
				
			||||||
		'/api/auth/change_password',
 | 
					    "/api/auth/change_password",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      oldPassword: oldPw,
 | 
					      oldPassword: oldPw,
 | 
				
			||||||
			newPassword: newPw
 | 
					      newPassword: newPw,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -46,7 +46,7 @@ export const change_password = (
 | 
				
			|||||||
export const logout_all = (
 | 
					export const logout_all = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Auth.LogoutAllResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.LogoutAllResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post_token('/api/auth/logout_all', {}, token);
 | 
					  post_token("/api/auth/logout_all", {}, token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function tfa_setup(
 | 
					export function tfa_setup(
 | 
				
			||||||
  mail: false,
 | 
					  mail: false,
 | 
				
			||||||
@@ -65,9 +65,9 @@ export function tfa_setup(
 | 
				
			|||||||
  | Responses.ErrorResponse
 | 
					  | Responses.ErrorResponse
 | 
				
			||||||
> {
 | 
					> {
 | 
				
			||||||
  return post_token<Requests.Auth.TfaSetup>(
 | 
					  return post_token<Requests.Auth.TfaSetup>(
 | 
				
			||||||
		'/api/auth/2fa/setup',
 | 
					    "/api/auth/2fa/setup",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
			mail
 | 
					      mail,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -79,10 +79,10 @@ export const tfa_complete = (
 | 
				
			|||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Auth.TfaCompletedResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.TfaCompletedResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.Auth.TfaComplete>(
 | 
					  post_token<Requests.Auth.TfaComplete>(
 | 
				
			||||||
		'/api/auth/2fa/complete',
 | 
					    "/api/auth/2fa/complete",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      mail,
 | 
					      mail,
 | 
				
			||||||
			code
 | 
					      code,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -90,4 +90,4 @@ export const tfa_complete = (
 | 
				
			|||||||
export const tfa_disable = (
 | 
					export const tfa_disable = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.Auth.RemoveTfaResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.Auth.RemoveTfaResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post_token('/api/auth/2fa/disable', {}, token);
 | 
					  post_token("/api/auth/2fa/disable", {}, token);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import axios from 'axios';
 | 
					import axios from "axios";
 | 
				
			||||||
import { Requests, Responses, UserRole } from '../../../dto';
 | 
					import { Requests, Responses, UserRole } from "../dto";
 | 
				
			||||||
export { Requests, Responses, UserRole };
 | 
					export { Requests, Responses, UserRole };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post = <T extends Requests.BaseRequest>(url: string, data: T) =>
 | 
					export const post = <T extends Requests.BaseRequest>(url: string, data: T) =>
 | 
				
			||||||
  axios
 | 
					  axios
 | 
				
			||||||
    .post(url, data, {
 | 
					    .post(url, data, {
 | 
				
			||||||
			headers: { 'Content-type': 'application/json' }
 | 
					      headers: { "Content-type": "application/json" },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .then((res) => res.data)
 | 
					    .then((res) => res.data)
 | 
				
			||||||
    .catch((err) => err.response.data);
 | 
					    .catch((err) => err.response.data);
 | 
				
			||||||
@@ -18,9 +18,9 @@ export const post_token = <T extends Requests.BaseRequest>(
 | 
				
			|||||||
  axios
 | 
					  axios
 | 
				
			||||||
    .post(url, data, {
 | 
					    .post(url, data, {
 | 
				
			||||||
      headers: {
 | 
					      headers: {
 | 
				
			||||||
				Authorization: 'Bearer ' + token,
 | 
					        Authorization: "Bearer " + token,
 | 
				
			||||||
				'Content-type': 'application/json'
 | 
					        "Content-type": "application/json",
 | 
				
			||||||
			}
 | 
					      },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .then((res) => res.data)
 | 
					    .then((res) => res.data)
 | 
				
			||||||
    .catch((err) => err.response.data);
 | 
					    .catch((err) => err.response.data);
 | 
				
			||||||
@@ -34,10 +34,10 @@ export const post_token_form = (
 | 
				
			|||||||
  axios
 | 
					  axios
 | 
				
			||||||
    .post(url, data, {
 | 
					    .post(url, data, {
 | 
				
			||||||
      headers: {
 | 
					      headers: {
 | 
				
			||||||
				Authorization: 'Bearer ' + token,
 | 
					        Authorization: "Bearer " + token,
 | 
				
			||||||
				'Content-type': 'multipart/form-data'
 | 
					        "Content-type": "multipart/form-data",
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
			onUploadProgress: onProgress
 | 
					      onUploadProgress: onProgress,
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .then((res) => res.data)
 | 
					    .then((res) => res.data)
 | 
				
			||||||
    .catch((err) => err.response.data);
 | 
					    .catch((err) => err.response.data);
 | 
				
			||||||
@@ -52,7 +52,7 @@ export const get = (url: string) =>
 | 
				
			|||||||
export const get_token = (url: string, token: string) =>
 | 
					export const get_token = (url: string, token: string) =>
 | 
				
			||||||
  axios
 | 
					  axios
 | 
				
			||||||
    .get(url, {
 | 
					    .get(url, {
 | 
				
			||||||
			headers: { Authorization: 'Bearer ' + token }
 | 
					      headers: { Authorization: "Bearer " + token },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .then((res) => res.data)
 | 
					    .then((res) => res.data)
 | 
				
			||||||
    .catch((err) => err.response.data);
 | 
					    .catch((err) => err.response.data);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,13 +4,13 @@ import {
 | 
				
			|||||||
  get_token,
 | 
					  get_token,
 | 
				
			||||||
  post_token,
 | 
					  post_token,
 | 
				
			||||||
  post_token_form,
 | 
					  post_token_form,
 | 
				
			||||||
	isErrorResponse
 | 
					  isErrorResponse,
 | 
				
			||||||
} from './base';
 | 
					} from "./base";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get_root = (
 | 
					export const get_root = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.FS.GetRootResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.FS.GetRootResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	get_token('/api/fs/root', token);
 | 
					  get_token("/api/fs/root", token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get_node = (
 | 
					export const get_node = (
 | 
				
			||||||
  token: string,
 | 
					  token: string,
 | 
				
			||||||
@@ -30,10 +30,10 @@ export const create_folder = (
 | 
				
			|||||||
  name: string
 | 
					  name: string
 | 
				
			||||||
): Promise<Responses.FS.CreateFolderResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.FS.CreateFolderResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.FS.CreateFolderRequest>(
 | 
					  post_token<Requests.FS.CreateFolderRequest>(
 | 
				
			||||||
		'/api/fs/createFolder',
 | 
					    "/api/fs/createFolder",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      parent: parent,
 | 
					      parent: parent,
 | 
				
			||||||
			name: name
 | 
					      name: name,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -44,10 +44,10 @@ export const create_file = (
 | 
				
			|||||||
  name: string
 | 
					  name: string
 | 
				
			||||||
): Promise<Responses.FS.CreateFileResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.FS.CreateFileResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
  post_token<Requests.FS.CreateFileRequest>(
 | 
					  post_token<Requests.FS.CreateFileRequest>(
 | 
				
			||||||
		'/api/fs/createFile',
 | 
					    "/api/fs/createFile",
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      parent: parent,
 | 
					      parent: parent,
 | 
				
			||||||
			name: name
 | 
					      name: name,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -56,13 +56,7 @@ export const delete_node = (
 | 
				
			|||||||
  token: string,
 | 
					  token: string,
 | 
				
			||||||
  node: number
 | 
					  node: number
 | 
				
			||||||
): Promise<Responses.FS.DeleteResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.FS.DeleteResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post_token<Requests.FS.DeleteRequest>(
 | 
					  post_token(`/api/fs/delete/${node}`, {}, token);
 | 
				
			||||||
		'/api/fs/delete',
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			node: node
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		token
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const upload_file = async (
 | 
					export const upload_file = async (
 | 
				
			||||||
  token: string,
 | 
					  token: string,
 | 
				
			||||||
@@ -74,20 +68,15 @@ export const upload_file = async (
 | 
				
			|||||||
  if (isErrorResponse(node)) return node;
 | 
					  if (isErrorResponse(node)) return node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const form = new FormData();
 | 
					  const form = new FormData();
 | 
				
			||||||
	form.set('file', file);
 | 
					  form.set("file", file);
 | 
				
			||||||
	return post_token_form(
 | 
					  return post_token_form(`/api/fs/upload/${node.id}`, form, token, onProgress);
 | 
				
			||||||
		`/api/fs/upload/${node.id}`,
 | 
					 | 
				
			||||||
		form,
 | 
					 | 
				
			||||||
		token,
 | 
					 | 
				
			||||||
		onProgress
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function download_file(token: string, id: number) {
 | 
					export function download_file(token: string, id: number) {
 | 
				
			||||||
	const form = document.createElement('form');
 | 
					  const form = document.createElement("form");
 | 
				
			||||||
	form.method = 'post';
 | 
					  form.method = "post";
 | 
				
			||||||
	form.target = '_blank';
 | 
					  form.target = "_blank";
 | 
				
			||||||
	form.action = '/api/fs/download';
 | 
					  form.action = "/api/fs/download";
 | 
				
			||||||
  form.innerHTML = `<input type="hidden" name="jwtToken" value="${token}"><input type="hidden" name="id" value="${id}">`;
 | 
					  form.innerHTML = `<input type="hidden" name="jwtToken" value="${token}"><input type="hidden" name="id" value="${id}">`;
 | 
				
			||||||
  document.body.appendChild(form);
 | 
					  document.body.appendChild(form);
 | 
				
			||||||
  form.submit();
 | 
					  form.submit();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
export { Requests, Responses, UserRole, isErrorResponse } from './base';
 | 
					export { Requests, Responses, UserRole, isErrorResponse } from "./base";
 | 
				
			||||||
export * as Auth from './auth';
 | 
					export * as Auth from "./auth";
 | 
				
			||||||
export * as FS from './fs';
 | 
					export * as FS from "./fs";
 | 
				
			||||||
export * as User from './user';
 | 
					export * as User from "./user";
 | 
				
			||||||
export * as Admin from './admin';
 | 
					export * as Admin from "./admin";
 | 
				
			||||||
export * from './util';
 | 
					export * from "./util";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import { Responses, get_token, post_token } from '@/api/base';
 | 
					import { Responses, get_token, post_token } from "@/api/base";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get_user_info = (
 | 
					export const get_user_info = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.User.UserInfoResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.User.UserInfoResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	get_token('/api/user/info', token);
 | 
					  get_token("/api/user/info", token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const delete_user = (
 | 
					export const delete_user = (
 | 
				
			||||||
  token: string
 | 
					  token: string
 | 
				
			||||||
): Promise<Responses.User.DeleteUserResponse | Responses.ErrorResponse> =>
 | 
					): Promise<Responses.User.DeleteUserResponse | Responses.ErrorResponse> =>
 | 
				
			||||||
	post_token('/api/user/delete', {}, token);
 | 
					  post_token("/api/user/delete", {}, token);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import jwtDecode, { JwtPayload } from 'jwt-decode';
 | 
					import jwtDecode, { JwtPayload } from "jwt-decode";
 | 
				
			||||||
import { Ref, UnwrapRef } from 'vue';
 | 
					import { Ref, UnwrapRef } from "vue";
 | 
				
			||||||
import { isErrorResponse } from './base';
 | 
					import { isErrorResponse } from "./base";
 | 
				
			||||||
import { refresh_token } from './auth';
 | 
					import { refresh_token } from "./auth";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function check_token(
 | 
					export async function check_token(
 | 
				
			||||||
  token: TokenInjectType
 | 
					  token: TokenInjectType
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,21 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineEmits, defineProps, inject } from 'vue';
 | 
					import { defineEmits, defineProps, inject } from "vue";
 | 
				
			||||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
					import { check_token, FS, Responses, TokenInjectType } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
  node: Responses.FS.GetNodeResponse;
 | 
					  node: Responses.FS.GetNodeResponse;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
	(e: 'reloadNode'): void;
 | 
					  (e: "reloadNode"): void;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function del() {
 | 
					async function del() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
  await FS.delete_node(token, props.node.id);
 | 
					  await FS.delete_node(token, props.node.id);
 | 
				
			||||||
	emit('reloadNode');
 | 
					  emit("reloadNode");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function download() {
 | 
					async function download() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,26 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineEmits, defineProps, inject, reactive, ref, watch } from 'vue';
 | 
					import { defineEmits, defineProps, inject, reactive, ref, watch } from "vue";
 | 
				
			||||||
import { FS, Responses, check_token, TokenInjectType } from '@/api';
 | 
					import { FS, Responses, check_token, TokenInjectType } from "@/api";
 | 
				
			||||||
import DirEntry from '@/components/FSView/DirEntry.vue';
 | 
					import DirEntry from "@/components/FSView/DirEntry.vue";
 | 
				
			||||||
import UploadFileDialog from '@/components/UploadDialog/UploadFileDialog.vue';
 | 
					import UploadFileDialog from "@/components/UploadDialog/UploadFileDialog.vue";
 | 
				
			||||||
import { NModal } from 'naive-ui';
 | 
					import { NModal } from "naive-ui";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
  node: Responses.FS.GetNodeResponse;
 | 
					  node: Responses.FS.GetNodeResponse;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
	(e: 'reloadNode'): void;
 | 
					  (e: "reloadNode"): void;
 | 
				
			||||||
	(e: 'gotoRoot'): void;
 | 
					  (e: "gotoRoot"): void;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fileInput = ref<HTMLInputElement>();
 | 
					const fileInput = ref<HTMLInputElement>();
 | 
				
			||||||
const uploadDialog = ref();
 | 
					const uploadDialog = ref();
 | 
				
			||||||
const uploadDialogShow = ref(false);
 | 
					const uploadDialogShow = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const new_folder_name = ref('');
 | 
					const new_folder_name = ref("");
 | 
				
			||||||
const files = ref<File[]>([]);
 | 
					const files = ref<File[]>([]);
 | 
				
			||||||
const nodes = ref<Responses.FS.GetNodeResponse[]>([]);
 | 
					const nodes = ref<Responses.FS.GetNodeResponse[]>([]);
 | 
				
			||||||
const hasParent = ref(false);
 | 
					const hasParent = ref(false);
 | 
				
			||||||
@@ -29,7 +29,7 @@ const parentNode = reactive<Responses.FS.GetNodeResponse>({
 | 
				
			|||||||
  statusCode: 200,
 | 
					  statusCode: 200,
 | 
				
			||||||
  isFile: false,
 | 
					  isFile: false,
 | 
				
			||||||
  parent: null,
 | 
					  parent: null,
 | 
				
			||||||
	name: '..'
 | 
					  name: "..",
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(
 | 
					watch(
 | 
				
			||||||
@@ -43,10 +43,7 @@ watch(
 | 
				
			|||||||
    await Promise.all(
 | 
					    await Promise.all(
 | 
				
			||||||
      to.children?.map(async (child) => {
 | 
					      to.children?.map(async (child) => {
 | 
				
			||||||
        nodes.value.push(
 | 
					        nodes.value.push(
 | 
				
			||||||
					(await FS.get_node(
 | 
					          (await FS.get_node(token, child)) as Responses.FS.GetNodeResponse
 | 
				
			||||||
						token,
 | 
					 | 
				
			||||||
						child
 | 
					 | 
				
			||||||
					)) as Responses.FS.GetNodeResponse
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }) ?? []
 | 
					      }) ?? []
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
@@ -58,7 +55,7 @@ async function newFolder() {
 | 
				
			|||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
  await FS.create_folder(token, props.node.id, new_folder_name.value);
 | 
					  await FS.create_folder(token, props.node.id, new_folder_name.value);
 | 
				
			||||||
	emit('reloadNode');
 | 
					  emit("reloadNode");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function uploadFiles() {
 | 
					async function uploadFiles() {
 | 
				
			||||||
@@ -69,18 +66,14 @@ async function uploadFiles() {
 | 
				
			|||||||
async function uploadFilesDialogOpen() {
 | 
					async function uploadFilesDialogOpen() {
 | 
				
			||||||
  await uploadDialog.value?.startUpload(props.node.id);
 | 
					  await uploadDialog.value?.startUpload(props.node.id);
 | 
				
			||||||
  uploadDialogShow.value = false;
 | 
					  uploadDialogShow.value = false;
 | 
				
			||||||
	if (fileInput.value) fileInput.value.value = '';
 | 
					  if (fileInput.value) fileInput.value.value = "";
 | 
				
			||||||
	emit('reloadNode');
 | 
					  emit("reloadNode");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
		<input
 | 
					    <input type="text" placeholder="Folder name" v-model="new_folder_name" />
 | 
				
			||||||
			type="text"
 | 
					 | 
				
			||||||
			placeholder="Folder name"
 | 
					 | 
				
			||||||
			v-model="new_folder_name"
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
    <a href="#" @click="newFolder()">create folder</a>
 | 
					    <a href="#" @click="newFolder()">create folder</a>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineProps, inject } from 'vue';
 | 
					import { defineProps, inject } from "vue";
 | 
				
			||||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
					import { check_token, FS, Responses, TokenInjectType } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
  node: Responses.FS.GetNodeResponse;
 | 
					  node: Responses.FS.GetNodeResponse;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function del() {
 | 
					async function del() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,7 @@
 | 
				
			|||||||
  <div class="hello">
 | 
					  <div class="hello">
 | 
				
			||||||
    <h1>{{ msg }}</h1>
 | 
					    <h1>{{ msg }}</h1>
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
			For a guide and recipes on how to configure / customize this
 | 
					      For a guide and recipes on how to configure / customize this project,<br />
 | 
				
			||||||
			project,<br />
 | 
					 | 
				
			||||||
      check out the
 | 
					      check out the
 | 
				
			||||||
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener"
 | 
					      <a href="https://cli.vuejs.org" target="_blank" rel="noopener"
 | 
				
			||||||
        >vue-cli documentation</a
 | 
					        >vue-cli documentation</a
 | 
				
			||||||
@@ -55,9 +54,7 @@
 | 
				
			|||||||
    <h3>Essential Links</h3>
 | 
					    <h3>Essential Links</h3>
 | 
				
			||||||
    <ul>
 | 
					    <ul>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a href="https://vuejs.org" target="_blank" rel="noopener"
 | 
					        <a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
 | 
				
			||||||
					>Core Docs</a
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
        <a href="https://forum.vuejs.org" target="_blank" rel="noopener"
 | 
					        <a href="https://forum.vuejs.org" target="_blank" rel="noopener"
 | 
				
			||||||
@@ -70,33 +67,23 @@
 | 
				
			|||||||
        >
 | 
					        >
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a
 | 
					        <a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
 | 
				
			||||||
					href="https://twitter.com/vuejs"
 | 
					 | 
				
			||||||
					target="_blank"
 | 
					 | 
				
			||||||
					rel="noopener"
 | 
					 | 
				
			||||||
          >Twitter</a
 | 
					          >Twitter</a
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a href="https://news.vuejs.org" target="_blank" rel="noopener"
 | 
					        <a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
 | 
				
			||||||
					>News</a
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
    <h3>Ecosystem</h3>
 | 
					    <h3>Ecosystem</h3>
 | 
				
			||||||
    <ul>
 | 
					    <ul>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a
 | 
					        <a href="https://router.vuejs.org" target="_blank" rel="noopener"
 | 
				
			||||||
					href="https://router.vuejs.org"
 | 
					 | 
				
			||||||
					target="_blank"
 | 
					 | 
				
			||||||
					rel="noopener"
 | 
					 | 
				
			||||||
          >vue-router</a
 | 
					          >vue-router</a
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a href="https://vuex.vuejs.org" target="_blank" rel="noopener"
 | 
					        <a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
 | 
				
			||||||
					>vuex</a
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
        <a
 | 
					        <a
 | 
				
			||||||
@@ -107,10 +94,7 @@
 | 
				
			|||||||
        >
 | 
					        >
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <li>
 | 
					      <li>
 | 
				
			||||||
				<a
 | 
					        <a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
 | 
				
			||||||
					href="https://vue-loader.vuejs.org"
 | 
					 | 
				
			||||||
					target="_blank"
 | 
					 | 
				
			||||||
					rel="noopener"
 | 
					 | 
				
			||||||
          >vue-loader</a
 | 
					          >vue-loader</a
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
@@ -127,13 +111,13 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { defineComponent } from 'vue';
 | 
					import { defineComponent } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
	name: 'HelloWorld',
 | 
					  name: "HelloWorld",
 | 
				
			||||||
  props: {
 | 
					  props: {
 | 
				
			||||||
		msg: String
 | 
					    msg: String,
 | 
				
			||||||
	}
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineProps, defineExpose, ref } from 'vue';
 | 
					import { defineProps, defineExpose, ref } from "vue";
 | 
				
			||||||
import { isErrorResponse, FS } from '@/api';
 | 
					import { isErrorResponse, FS } from "@/api";
 | 
				
			||||||
import { NProgress } from 'naive-ui';
 | 
					import { NProgress } from "naive-ui";
 | 
				
			||||||
import filesize from 'filesize';
 | 
					import filesize from "filesize";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
  file: File;
 | 
					  file: File;
 | 
				
			||||||
@@ -10,8 +10,8 @@ const props = defineProps<{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const progress = ref(0);
 | 
					const progress = ref(0);
 | 
				
			||||||
const percentage = ref(0);
 | 
					const percentage = ref(0);
 | 
				
			||||||
const err = ref('');
 | 
					const err = ref("");
 | 
				
			||||||
const status = ref('info');
 | 
					const status = ref("info");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function startUpload(parent: number, token: string) {
 | 
					async function startUpload(parent: number, token: string) {
 | 
				
			||||||
  const resp = await FS.upload_file(token, parent, props.file, (e) => {
 | 
					  const resp = await FS.upload_file(token, parent, props.file, (e) => {
 | 
				
			||||||
@@ -20,13 +20,13 @@ async function startUpload(parent: number, token: string) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
  percentage.value = 100;
 | 
					  percentage.value = 100;
 | 
				
			||||||
  if (isErrorResponse(resp)) {
 | 
					  if (isErrorResponse(resp)) {
 | 
				
			||||||
		err.value = resp.message ?? 'Error';
 | 
					    err.value = resp.message ?? "Error";
 | 
				
			||||||
		status.value = 'error';
 | 
					    status.value = "error";
 | 
				
			||||||
	} else status.value = 'success';
 | 
					  } else status.value = "success";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({
 | 
					defineExpose({
 | 
				
			||||||
	startUpload
 | 
					  startUpload,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineProps, defineExpose, ref, inject } from 'vue';
 | 
					import { defineProps, defineExpose, ref, inject } from "vue";
 | 
				
			||||||
import { check_token, TokenInjectType } from '@/api';
 | 
					import { check_token, TokenInjectType } from "@/api";
 | 
				
			||||||
import UploadEntry from '@/components/UploadDialog/UploadEntry.vue';
 | 
					import UploadEntry from "@/components/UploadDialog/UploadEntry.vue";
 | 
				
			||||||
import { NCard } from 'naive-ui';
 | 
					import { NCard } from "naive-ui";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const entries = ref<typeof UploadEntry[]>([]);
 | 
					const entries = ref<typeof UploadEntry[]>([]);
 | 
				
			||||||
const done = ref(false);
 | 
					const done = ref(false);
 | 
				
			||||||
@@ -22,7 +22,7 @@ async function startUpload(parent: number) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({
 | 
					defineExpose({
 | 
				
			||||||
	startUpload
 | 
					  startUpload,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
defineProps<{
 | 
					defineProps<{
 | 
				
			||||||
  files: File[];
 | 
					  files: File[];
 | 
				
			||||||
@@ -32,12 +32,7 @@ defineProps<{
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <n-card title="Upload Files">
 | 
					  <n-card title="Upload Files">
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
			<UploadEntry
 | 
					      <UploadEntry v-for="f in files" :key="f.name" ref="entries" :file="f" />
 | 
				
			||||||
				v-for="f in files"
 | 
					 | 
				
			||||||
				:key="f.name"
 | 
					 | 
				
			||||||
				ref="entries"
 | 
					 | 
				
			||||||
				:file="f"
 | 
					 | 
				
			||||||
			/>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <button v-if="done" @click="canCloseResolve()">Close</button>
 | 
					      <button v-if="done" @click="canCloseResolve()">Close</button>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								frontend/src/dto/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								frontend/src/dto/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					export * as Requests from "./requests";
 | 
				
			||||||
 | 
					export * as Responses from "./responses";
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  UserRole,
 | 
				
			||||||
 | 
					  validateSync,
 | 
				
			||||||
 | 
					  validateAsync,
 | 
				
			||||||
 | 
					  validateAsyncInline,
 | 
				
			||||||
 | 
					} from "./utils";
 | 
				
			||||||
							
								
								
									
										17
									
								
								frontend/src/dto/requests/admin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/src/dto/requests/admin.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import { BaseRequest } from "./base";
 | 
				
			||||||
 | 
					import { IsEnum, IsNumber } from "class-validator";
 | 
				
			||||||
 | 
					import { UserRole } from "../utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AdminRequest extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsNumber()
 | 
				
			||||||
 | 
					  user: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SetUserRole extends AdminRequest {
 | 
				
			||||||
 | 
					  @IsEnum(UserRole)
 | 
				
			||||||
 | 
					  role: UserRole;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LogoutAll extends AdminRequest {}
 | 
				
			||||||
 | 
					export class DeleteUser extends AdminRequest {}
 | 
				
			||||||
 | 
					export class DisableTfa extends AdminRequest {}
 | 
				
			||||||
							
								
								
									
										50
									
								
								frontend/src/dto/requests/auth.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								frontend/src/dto/requests/auth.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					import { BaseRequest } from "./base";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  IsBoolean,
 | 
				
			||||||
 | 
					  IsEmail,
 | 
				
			||||||
 | 
					  IsNotEmpty,
 | 
				
			||||||
 | 
					  IsOptional,
 | 
				
			||||||
 | 
					  IsString,
 | 
				
			||||||
 | 
					} from "class-validator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SignUpRequest extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsEmail()
 | 
				
			||||||
 | 
					  username: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  password: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LoginRequest extends SignUpRequest {
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  otp?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class TfaSetup extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  mail: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class TfaComplete extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  mail: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  code: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ChangePasswordRequest extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  oldPassword: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  newPassword: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								frontend/src/dto/requests/fs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								frontend/src/dto/requests/fs.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import { BaseRequest } from "./base";
 | 
				
			||||||
 | 
					import { IsInt, IsNotEmpty, IsString, Min } from "class-validator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CreateFolderRequest extends BaseRequest {
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(1)
 | 
				
			||||||
 | 
					  parent: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CreateFileRequest extends CreateFolderRequest {}
 | 
				
			||||||
							
								
								
									
										4
									
								
								frontend/src/dto/requests/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/src/dto/requests/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					export * from "./base";
 | 
				
			||||||
 | 
					export * as Auth from "./auth";
 | 
				
			||||||
 | 
					export * as FS from "./fs";
 | 
				
			||||||
 | 
					export * as Admin from "./admin";
 | 
				
			||||||
							
								
								
									
										61
									
								
								frontend/src/dto/responses/admin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								frontend/src/dto/responses/admin.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					import { SuccessResponse } from "./base";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  IsArray,
 | 
				
			||||||
 | 
					  IsBoolean,
 | 
				
			||||||
 | 
					  IsEnum,
 | 
				
			||||||
 | 
					  IsNotEmpty,
 | 
				
			||||||
 | 
					  IsNumber,
 | 
				
			||||||
 | 
					  IsString,
 | 
				
			||||||
 | 
					  ValidateNested,
 | 
				
			||||||
 | 
					} from "class-validator";
 | 
				
			||||||
 | 
					import { UserRole, ValidateConstructor } from "../utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class GetUsersEntry {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    id: number,
 | 
				
			||||||
 | 
					    gitlab: boolean,
 | 
				
			||||||
 | 
					    name: string,
 | 
				
			||||||
 | 
					    role: UserRole,
 | 
				
			||||||
 | 
					    tfaEnabled: boolean
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.id = id;
 | 
				
			||||||
 | 
					    this.gitlab = gitlab;
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					    this.role = role;
 | 
				
			||||||
 | 
					    this.tfaEnabled = tfaEnabled;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNumber()
 | 
				
			||||||
 | 
					  id: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  gitlab: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsEnum(UserRole)
 | 
				
			||||||
 | 
					  role: UserRole;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  tfaEnabled: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class GetUsers extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(users: GetUsersEntry[]) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.users = users;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsArray()
 | 
				
			||||||
 | 
					  @ValidateNested({ each: true })
 | 
				
			||||||
 | 
					  users: GetUsersEntry[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LogoutAllUser extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class DeleteUser extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class SetUserRole extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class DisableTfa extends SuccessResponse {}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { SuccessResponse } from './base';
 | 
					import { SuccessResponse } from "./base";
 | 
				
			||||||
import { IsBase32, IsJWT, IsNotEmpty } from 'class-validator';
 | 
					import { IsBase32, IsJWT, IsNotEmpty } from "class-validator";
 | 
				
			||||||
import { ValidateConstructor } from '../utils';
 | 
					import { ValidateConstructor } from "../utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ValidateConstructor
 | 
					@ValidateConstructor
 | 
				
			||||||
export class LoginResponse extends SuccessResponse {
 | 
					export class LoginResponse extends SuccessResponse {
 | 
				
			||||||
							
								
								
									
										25
									
								
								frontend/src/dto/responses/base.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								frontend/src/dto/responses/base.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { IsNumber, Max, Min } from "class-validator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class BaseResponse {
 | 
				
			||||||
 | 
					  constructor(statusCode: number) {
 | 
				
			||||||
 | 
					    this.statusCode = statusCode;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNumber()
 | 
				
			||||||
 | 
					  @Min(100)
 | 
				
			||||||
 | 
					  @Max(599)
 | 
				
			||||||
 | 
					  statusCode: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SuccessResponse extends BaseResponse {
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    super(200);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  declare statusCode: 200;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ErrorResponse extends BaseResponse {
 | 
				
			||||||
 | 
					  declare statusCode: 400 | 401 | 403;
 | 
				
			||||||
 | 
					  message?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										89
									
								
								frontend/src/dto/responses/fs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								frontend/src/dto/responses/fs.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					import { SuccessResponse } from "./base";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  IsBoolean,
 | 
				
			||||||
 | 
					  IsInt,
 | 
				
			||||||
 | 
					  IsNotEmpty,
 | 
				
			||||||
 | 
					  IsOptional,
 | 
				
			||||||
 | 
					  IsString,
 | 
				
			||||||
 | 
					  Min,
 | 
				
			||||||
 | 
					} from "class-validator";
 | 
				
			||||||
 | 
					import { ValidateConstructor } from "../utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class GetRootResponse extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(rootId: number) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.rootId = rootId;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(1)
 | 
				
			||||||
 | 
					  rootId: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class GetNodeResponse extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    id: number,
 | 
				
			||||||
 | 
					    name: string,
 | 
				
			||||||
 | 
					    isFile: boolean,
 | 
				
			||||||
 | 
					    parent: number | null
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.id = id;
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					    this.isFile = isFile;
 | 
				
			||||||
 | 
					    this.parent = parent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(1)
 | 
				
			||||||
 | 
					  id: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  isFile: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(1)
 | 
				
			||||||
 | 
					  parent: number | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt({ each: true })
 | 
				
			||||||
 | 
					  @Min(1, { each: true })
 | 
				
			||||||
 | 
					  children?: number[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  size?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class GetPathResponse extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(path: string) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.path = path;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  path: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class CreateFolderResponse extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(id: number) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.id = id;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(1)
 | 
				
			||||||
 | 
					  id: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UploadFileResponse extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class DeleteResponse extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class CreateFileResponse extends CreateFolderResponse {}
 | 
				
			||||||
							
								
								
									
										5
									
								
								frontend/src/dto/responses/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/src/dto/responses/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					export * from "./base";
 | 
				
			||||||
 | 
					export * as Auth from "./auth";
 | 
				
			||||||
 | 
					export * as FS from "./fs";
 | 
				
			||||||
 | 
					export * as User from "./user";
 | 
				
			||||||
 | 
					export * as Admin from "./admin";
 | 
				
			||||||
							
								
								
									
										27
									
								
								frontend/src/dto/responses/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								frontend/src/dto/responses/user.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import { SuccessResponse } from "./base";
 | 
				
			||||||
 | 
					import { ValidateConstructor } from "../utils";
 | 
				
			||||||
 | 
					import { IsBoolean, IsNotEmpty, IsString } from "class-validator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ValidateConstructor
 | 
				
			||||||
 | 
					export class UserInfoResponse extends SuccessResponse {
 | 
				
			||||||
 | 
					  constructor(name: string, gitlab: boolean, tfaEnabled: boolean) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					    this.gitlab = gitlab;
 | 
				
			||||||
 | 
					    this.tfaEnabled = tfaEnabled;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  gitlab: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  tfaEnabled: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class DeleteUserResponse extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class ChangePasswordResponse extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class LogoutAllResponse extends SuccessResponse {}
 | 
				
			||||||
							
								
								
									
										41
									
								
								frontend/src/dto/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								frontend/src/dto/utils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import { validate, validateSync as _validateSync } from "class-validator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum UserRole {
 | 
				
			||||||
 | 
					  ADMIN = 2,
 | 
				
			||||||
 | 
					  USER = 1,
 | 
				
			||||||
 | 
					  DISABLED = 0,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function validateSync<T extends object>(data: T): void {
 | 
				
			||||||
 | 
					  const errors = _validateSync(data);
 | 
				
			||||||
 | 
					  if (errors.length > 0) {
 | 
				
			||||||
 | 
					    console.error("Validation failed, errors: ", errors);
 | 
				
			||||||
 | 
					    throw new Error("Validation failed");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function validateAsync<T extends object>(data: T): Promise<void> {
 | 
				
			||||||
 | 
					  const errors = await validate(data);
 | 
				
			||||||
 | 
					  if (errors.length > 0) {
 | 
				
			||||||
 | 
					    console.error("Validation failed, errors: ", errors);
 | 
				
			||||||
 | 
					    throw new Error("Validation failed");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function validateAsyncInline<T extends object>(
 | 
				
			||||||
 | 
					  data: T
 | 
				
			||||||
 | 
					): Promise<T> {
 | 
				
			||||||
 | 
					  await validateAsync(data);
 | 
				
			||||||
 | 
					  return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ValidateConstructor<T extends { new (...args: any[]): any }>(
 | 
				
			||||||
 | 
					  constr: T
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  return class extends constr {
 | 
				
			||||||
 | 
					    constructor(...args: any[]) {
 | 
				
			||||||
 | 
					      super(...args);
 | 
				
			||||||
 | 
					      validateSync(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import { createApp } from 'vue';
 | 
					import { createApp } from "vue";
 | 
				
			||||||
import router from './router';
 | 
					import router from "./router";
 | 
				
			||||||
import AppAsyncWrapper from './AppAsyncWrapper.vue';
 | 
					import AppAsyncWrapper from "./AppAsyncWrapper.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = createApp(AppAsyncWrapper);
 | 
					const app = createApp(AppAsyncWrapper);
 | 
				
			||||||
app.use(router);
 | 
					app.use(router);
 | 
				
			||||||
app.config.unwrapInjectedRef = true;
 | 
					app.config.unwrapInjectedRef = true;
 | 
				
			||||||
app.mount('#app');
 | 
					app.mount("#app");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,63 +1,63 @@
 | 
				
			|||||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
 | 
					import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
 | 
				
			||||||
import LoginView from '@/views/LoginView.vue';
 | 
					import LoginView from "@/views/LoginView.vue";
 | 
				
			||||||
import SignupView from '@/views/SignupView.vue';
 | 
					import SignupView from "@/views/SignupView.vue";
 | 
				
			||||||
import HomeView from '@/views/HomeView.vue';
 | 
					import HomeView from "@/views/HomeView.vue";
 | 
				
			||||||
import AboutView from '@/views/AboutView.vue';
 | 
					import AboutView from "@/views/AboutView.vue";
 | 
				
			||||||
import FSView from '@/views/FSView.vue';
 | 
					import FSView from "@/views/FSView.vue";
 | 
				
			||||||
import SetTokenView from '@/views/SetTokenView.vue';
 | 
					import SetTokenView from "@/views/SetTokenView.vue";
 | 
				
			||||||
import ProfileView from '@/views/ProfileView.vue';
 | 
					import ProfileView from "@/views/ProfileView.vue";
 | 
				
			||||||
import TFAView from '@/views/TFAView.vue';
 | 
					import TFAView from "@/views/TFAView.vue";
 | 
				
			||||||
import AdminView from '@/views/AdminView.vue';
 | 
					import AdminView from "@/views/AdminView.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes: Array<RouteRecordRaw> = [
 | 
					const routes: Array<RouteRecordRaw> = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/',
 | 
					    path: "/",
 | 
				
			||||||
		name: 'home',
 | 
					    name: "home",
 | 
				
			||||||
		component: HomeView
 | 
					    component: HomeView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/profile',
 | 
					    path: "/profile",
 | 
				
			||||||
		name: 'profile',
 | 
					    name: "profile",
 | 
				
			||||||
		component: ProfileView
 | 
					    component: ProfileView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/profile/2fa-enable',
 | 
					    path: "/profile/2fa-enable",
 | 
				
			||||||
		name: '2fa',
 | 
					    name: "2fa",
 | 
				
			||||||
		component: TFAView
 | 
					    component: TFAView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/admin',
 | 
					    path: "/admin",
 | 
				
			||||||
		component: AdminView
 | 
					    component: AdminView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/about',
 | 
					    path: "/about",
 | 
				
			||||||
		component: AboutView
 | 
					    component: AboutView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/login',
 | 
					    path: "/login",
 | 
				
			||||||
		name: 'login',
 | 
					    name: "login",
 | 
				
			||||||
		component: LoginView
 | 
					    component: LoginView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/signup',
 | 
					    path: "/signup",
 | 
				
			||||||
		name: 'signup',
 | 
					    name: "signup",
 | 
				
			||||||
		component: SignupView
 | 
					    component: SignupView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/fs/:node_id',
 | 
					    path: "/fs/:node_id",
 | 
				
			||||||
		name: 'fs',
 | 
					    name: "fs",
 | 
				
			||||||
		component: FSView
 | 
					    component: FSView,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
		path: '/set_token',
 | 
					    path: "/set_token",
 | 
				
			||||||
		component: SetTokenView
 | 
					    component: SetTokenView,
 | 
				
			||||||
	}
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = createRouter({
 | 
					const router = createRouter({
 | 
				
			||||||
  history: createWebHistory(process.env.BASE_URL),
 | 
					  history: createWebHistory(process.env.BASE_URL),
 | 
				
			||||||
	routes
 | 
					  routes,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default router;
 | 
					export default router;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,16 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { inject, onBeforeMount, ref } from 'vue';
 | 
					import { inject, onBeforeMount, ref } from "vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Responses,
 | 
					  Responses,
 | 
				
			||||||
  check_token,
 | 
					  check_token,
 | 
				
			||||||
  TokenInjectType,
 | 
					  TokenInjectType,
 | 
				
			||||||
  Admin,
 | 
					  Admin,
 | 
				
			||||||
	isErrorResponse
 | 
					  isErrorResponse,
 | 
				
			||||||
} from '@/api';
 | 
					} from "@/api";
 | 
				
			||||||
import { onBeforeRouteUpdate } from 'vue-router';
 | 
					import { onBeforeRouteUpdate } from "vue-router";
 | 
				
			||||||
import router from '@/router';
 | 
					import router from "@/router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const users = ref<Responses.Admin.GetUsersEntry[]>([]);
 | 
					const users = ref<Responses.Admin.GetUsersEntry[]>([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +25,7 @@ async function updatePanel() {
 | 
				
			|||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const res = await Admin.get_users(token);
 | 
					  const res = await Admin.get_users(token);
 | 
				
			||||||
	if (isErrorResponse(res)) return router.replace({ path: '/' });
 | 
					  if (isErrorResponse(res)) return router.replace({ path: "/" });
 | 
				
			||||||
  users.value = res.users;
 | 
					  users.value = res.users;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -77,7 +77,7 @@ async function deleteUser(user: number) {
 | 
				
			|||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
    <tr v-for="user in users" :key="user.id">
 | 
					    <tr v-for="user in users" :key="user.id">
 | 
				
			||||||
      <td>{{ user.name }}</td>
 | 
					      <td>{{ user.name }}</td>
 | 
				
			||||||
			<td>{{ user.gitlab ? 'Gitlab' : 'Password' }}</td>
 | 
					      <td>{{ user.gitlab ? "Gitlab" : "Password" }}</td>
 | 
				
			||||||
      <td>
 | 
					      <td>
 | 
				
			||||||
        <select @change="setRole(user.id, $event.target.value)">
 | 
					        <select @change="setRole(user.id, $event.target.value)">
 | 
				
			||||||
          <option value="0" :selected="user.role === 0 ? true : null">
 | 
					          <option value="0" :selected="user.role === 0 ? true : null">
 | 
				
			||||||
@@ -93,7 +93,7 @@ async function deleteUser(user: number) {
 | 
				
			|||||||
      </td>
 | 
					      </td>
 | 
				
			||||||
      <td v-if="user.gitlab"></td>
 | 
					      <td v-if="user.gitlab"></td>
 | 
				
			||||||
      <td v-else>
 | 
					      <td v-else>
 | 
				
			||||||
				{{ user.tfaEnabled ? 'Enabled' : 'Disabled' }}
 | 
					        {{ user.tfaEnabled ? "Enabled" : "Disabled" }}
 | 
				
			||||||
      </td>
 | 
					      </td>
 | 
				
			||||||
      <td>
 | 
					      <td>
 | 
				
			||||||
        <button v-if="user.tfaEnabled" @click="disableTfa(user.id)">
 | 
					        <button v-if="user.tfaEnabled" @click="disableTfa(user.id)">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,21 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 | 
					import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
 | 
				
			||||||
import { inject, onBeforeMount, ref } from 'vue';
 | 
					import { inject, onBeforeMount, ref } from "vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  check_token,
 | 
					  check_token,
 | 
				
			||||||
  FS,
 | 
					  FS,
 | 
				
			||||||
  Responses,
 | 
					  Responses,
 | 
				
			||||||
  isErrorResponse,
 | 
					  isErrorResponse,
 | 
				
			||||||
	TokenInjectType
 | 
					  TokenInjectType,
 | 
				
			||||||
} from '@/api';
 | 
					} from "@/api";
 | 
				
			||||||
import DirViewer from '@/components/FSView/DirViewer.vue';
 | 
					import DirViewer from "@/components/FSView/DirViewer.vue";
 | 
				
			||||||
import FileViewer from '@/components/FSView/FileViewer.vue';
 | 
					import FileViewer from "@/components/FSView/FileViewer.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
const route = useRoute();
 | 
					const route = useRoute();
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const path = ref('');
 | 
					const path = ref("");
 | 
				
			||||||
const node = ref<Responses.FS.GetNodeResponse | null>(null);
 | 
					const node = ref<Responses.FS.GetNodeResponse | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function fetch_node(node_id: number) {
 | 
					async function fetch_node(node_id: number) {
 | 
				
			||||||
@@ -23,7 +23,7 @@ async function fetch_node(node_id: number) {
 | 
				
			|||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
  let [p, n] = [
 | 
					  let [p, n] = [
 | 
				
			||||||
    await FS.get_path(token, node_id),
 | 
					    await FS.get_path(token, node_id),
 | 
				
			||||||
		await FS.get_node(token, node_id)
 | 
					    await FS.get_node(token, node_id),
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  if (isErrorResponse(p)) return gotoRoot();
 | 
					  if (isErrorResponse(p)) return gotoRoot();
 | 
				
			||||||
  if (isErrorResponse(n)) return gotoRoot();
 | 
					  if (isErrorResponse(n)) return gotoRoot();
 | 
				
			||||||
@@ -48,8 +48,8 @@ async function gotoRoot() {
 | 
				
			|||||||
  if (isErrorResponse(rootRes)) return jwt.logout();
 | 
					  if (isErrorResponse(rootRes)) return jwt.logout();
 | 
				
			||||||
  const root = rootRes.rootId;
 | 
					  const root = rootRes.rootId;
 | 
				
			||||||
  await router.replace({
 | 
					  await router.replace({
 | 
				
			||||||
		name: 'fs',
 | 
					    name: "fs",
 | 
				
			||||||
		params: { node_id: root }
 | 
					    params: { node_id: root },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
<template><p></p></template>
 | 
					<template><p></p></template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { onBeforeRouteUpdate, useRouter } from 'vue-router';
 | 
					import { onBeforeRouteUpdate, useRouter } from "vue-router";
 | 
				
			||||||
import { inject, onBeforeMount } from 'vue';
 | 
					import { inject, onBeforeMount } from "vue";
 | 
				
			||||||
import { FS, check_token, isErrorResponse, TokenInjectType } from '@/api';
 | 
					import { FS, check_token, isErrorResponse, TokenInjectType } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function start_redirect() {
 | 
					async function start_redirect() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
@@ -14,8 +14,8 @@ async function start_redirect() {
 | 
				
			|||||||
  const root = await FS.get_root(token);
 | 
					  const root = await FS.get_root(token);
 | 
				
			||||||
  if (isErrorResponse(root)) return jwt.logout();
 | 
					  if (isErrorResponse(root)) return jwt.logout();
 | 
				
			||||||
  await router.replace({
 | 
					  await router.replace({
 | 
				
			||||||
		name: 'fs',
 | 
					    name: "fs",
 | 
				
			||||||
		params: { node_id: root.rootId }
 | 
					    params: { node_id: root.rootId },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,43 +1,43 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ref, inject } from 'vue';
 | 
					import { ref, inject } from "vue";
 | 
				
			||||||
import { Auth, FS, isErrorResponse, TokenInjectType } from '@/api';
 | 
					import { Auth, FS, isErrorResponse, TokenInjectType } from "@/api";
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from "vue-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const username = ref('');
 | 
					const username = ref("");
 | 
				
			||||||
const password = ref('');
 | 
					const password = ref("");
 | 
				
			||||||
const otp = ref('');
 | 
					const otp = ref("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const error = ref('');
 | 
					const error = ref("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const requestOtp = ref(false);
 | 
					const requestOtp = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function login() {
 | 
					async function login() {
 | 
				
			||||||
	error.value = '';
 | 
					  error.value = "";
 | 
				
			||||||
	if (username.value === '' || password.value === '') {
 | 
					  if (username.value === "" || password.value === "") {
 | 
				
			||||||
		error.value = 'Email and/or Password missing';
 | 
					    error.value = "Email and/or Password missing";
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const res = await (requestOtp.value
 | 
					  const res = await (requestOtp.value
 | 
				
			||||||
    ? Auth.auth_login(username.value, password.value, otp.value)
 | 
					    ? Auth.auth_login(username.value, password.value, otp.value)
 | 
				
			||||||
    : Auth.auth_login(username.value, password.value));
 | 
					    : Auth.auth_login(username.value, password.value));
 | 
				
			||||||
	if (isErrorResponse(res)) error.value = 'Login failed: ' + res.message;
 | 
					  if (isErrorResponse(res)) error.value = "Login failed: " + res.message;
 | 
				
			||||||
	else if ('jwt' in res) {
 | 
					  else if ("jwt" in res) {
 | 
				
			||||||
    const root = await FS.get_root(res.jwt);
 | 
					    const root = await FS.get_root(res.jwt);
 | 
				
			||||||
    if (isErrorResponse(root)) {
 | 
					    if (isErrorResponse(root)) {
 | 
				
			||||||
			error.value = 'Get root failed: ' + root.message;
 | 
					      error.value = "Get root failed: " + root.message;
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    jwt.setToken(res.jwt);
 | 
					    jwt.setToken(res.jwt);
 | 
				
			||||||
    await router.push({
 | 
					    await router.push({
 | 
				
			||||||
			name: 'fs',
 | 
					      name: "fs",
 | 
				
			||||||
			params: { node_id: root.rootId }
 | 
					      params: { node_id: root.rootId },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
		error.value = '';
 | 
					    error.value = "";
 | 
				
			||||||
    requestOtp.value = true;
 | 
					    requestOtp.value = true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,22 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ref, inject, onBeforeMount } from 'vue';
 | 
					import { ref, inject, onBeforeMount } from "vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Auth,
 | 
					  Auth,
 | 
				
			||||||
  User,
 | 
					  User,
 | 
				
			||||||
  check_token,
 | 
					  check_token,
 | 
				
			||||||
  isErrorResponse,
 | 
					  isErrorResponse,
 | 
				
			||||||
  TokenInjectType,
 | 
					  TokenInjectType,
 | 
				
			||||||
	Responses
 | 
					  Responses,
 | 
				
			||||||
} from '@/api';
 | 
					} from "@/api";
 | 
				
			||||||
import { onBeforeRouteUpdate } from 'vue-router';
 | 
					import { onBeforeRouteUpdate } from "vue-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const error = ref('');
 | 
					const error = ref("");
 | 
				
			||||||
const oldPw = ref('');
 | 
					const oldPw = ref("");
 | 
				
			||||||
const newPw = ref('');
 | 
					const newPw = ref("");
 | 
				
			||||||
const newPw2 = ref('');
 | 
					const newPw2 = ref("");
 | 
				
			||||||
const user = ref<Responses.User.UserInfoResponse | null>(null);
 | 
					const user = ref<Responses.User.UserInfoResponse | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onBeforeRouteUpdate(async () => {
 | 
					onBeforeRouteUpdate(async () => {
 | 
				
			||||||
  await updateProfile();
 | 
					  await updateProfile();
 | 
				
			||||||
@@ -48,8 +48,8 @@ async function logoutAll() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function changePw() {
 | 
					async function changePw() {
 | 
				
			||||||
	if (oldPw.value === '' || newPw.value === '' || newPw2.value === '') {
 | 
					  if (oldPw.value === "" || newPw.value === "" || newPw2.value === "") {
 | 
				
			||||||
		error.value = 'Password missing';
 | 
					    error.value = "Password missing";
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (newPw.value !== newPw2.value) {
 | 
					  if (newPw.value !== newPw2.value) {
 | 
				
			||||||
@@ -60,7 +60,7 @@ async function changePw() {
 | 
				
			|||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
  const res = await Auth.change_password(oldPw.value, newPw.value, token);
 | 
					  const res = await Auth.change_password(oldPw.value, newPw.value, token);
 | 
				
			||||||
  if (isErrorResponse(res))
 | 
					  if (isErrorResponse(res))
 | 
				
			||||||
		error.value = 'Password change failed: ' + res.message;
 | 
					    error.value = "Password change failed: " + res.message;
 | 
				
			||||||
  else jwt.logout();
 | 
					  else jwt.logout();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -76,19 +76,11 @@ async function tfaDisable() {
 | 
				
			|||||||
  <template v-if="user">
 | 
					  <template v-if="user">
 | 
				
			||||||
    <div v-if="error !== ''" v-text="error"></div>
 | 
					    <div v-if="error !== ''" v-text="error"></div>
 | 
				
			||||||
    <div>User: {{ user.name }}</div>
 | 
					    <div>User: {{ user.name }}</div>
 | 
				
			||||||
		<div>Signed in with {{ user.gitlab ? 'gitlab' : 'password' }}</div>
 | 
					    <div>Signed in with {{ user.gitlab ? "gitlab" : "password" }}</div>
 | 
				
			||||||
    <template v-if="!user.gitlab">
 | 
					    <template v-if="!user.gitlab">
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
				<input
 | 
					        <input type="password" placeholder="Old password" v-model="oldPw" />
 | 
				
			||||||
					type="password"
 | 
					        <input type="password" placeholder="New password" v-model="newPw" />
 | 
				
			||||||
					placeholder="Old password"
 | 
					 | 
				
			||||||
					v-model="oldPw"
 | 
					 | 
				
			||||||
				/>
 | 
					 | 
				
			||||||
				<input
 | 
					 | 
				
			||||||
					type="password"
 | 
					 | 
				
			||||||
					placeholder="New password"
 | 
					 | 
				
			||||||
					v-model="newPw"
 | 
					 | 
				
			||||||
				/>
 | 
					 | 
				
			||||||
        <input
 | 
					        <input
 | 
				
			||||||
          type="password"
 | 
					          type="password"
 | 
				
			||||||
          placeholder="Repeat new password"
 | 
					          placeholder="Repeat new password"
 | 
				
			||||||
@@ -99,15 +91,11 @@ async function tfaDisable() {
 | 
				
			|||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
          2 Factor authentication:
 | 
					          2 Factor authentication:
 | 
				
			||||||
					{{ user.tfaEnabled ? 'Enabled' : 'Disabled' }}
 | 
					          {{ user.tfaEnabled ? "Enabled" : "Disabled" }}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
					<a href="#" v-if="user.tfaEnabled" @click="tfaDisable">
 | 
					          <a href="#" v-if="user.tfaEnabled" @click="tfaDisable"> Disable </a>
 | 
				
			||||||
						Disable
 | 
					          <router-link to="/profile/2fa-enable" v-else> Enable </router-link>
 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
					<router-link to="/profile/2fa-enable" v-else>
 | 
					 | 
				
			||||||
						Enable
 | 
					 | 
				
			||||||
					</router-link>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,15 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { inject } from 'vue';
 | 
					import { inject } from "vue";
 | 
				
			||||||
import { TokenInjectType } from '@/api';
 | 
					import { TokenInjectType } from "@/api";
 | 
				
			||||||
import { useRoute, useRouter } from 'vue-router';
 | 
					import { useRoute, useRouter } from "vue-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
const route = useRoute();
 | 
					const route = useRoute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ('token' in route.query) jwt.setToken(route.query['token'] as string);
 | 
					if ("token" in route.query) jwt.setToken(route.query["token"] as string);
 | 
				
			||||||
router.replace({ path: '/' });
 | 
					router.replace({ path: "/" });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,15 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ref } from 'vue';
 | 
					import { ref } from "vue";
 | 
				
			||||||
import { Auth, isErrorResponse } from '@/api';
 | 
					import { Auth, isErrorResponse } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const username = ref('');
 | 
					const username = ref("");
 | 
				
			||||||
const password = ref('');
 | 
					const password = ref("");
 | 
				
			||||||
const password2 = ref('');
 | 
					const password2 = ref("");
 | 
				
			||||||
const error = ref('');
 | 
					const error = ref("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function signup() {
 | 
					async function signup() {
 | 
				
			||||||
	if (username.value === '' || password.value === '') {
 | 
					  if (username.value === "" || password.value === "") {
 | 
				
			||||||
		error.value = 'Email and/or Password missing';
 | 
					    error.value = "Email and/or Password missing";
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (password.value !== password2.value) {
 | 
					  if (password.value !== password2.value) {
 | 
				
			||||||
@@ -18,8 +18,8 @@ async function signup() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  const res = await Auth.auth_signup(username.value, password.value);
 | 
					  const res = await Auth.auth_signup(username.value, password.value);
 | 
				
			||||||
  error.value = isErrorResponse(res)
 | 
					  error.value = isErrorResponse(res)
 | 
				
			||||||
		? 'Signup failed: ' + res.message
 | 
					    ? "Signup failed: " + res.message
 | 
				
			||||||
		: 'Signup successful, please wait till an admin unlocks your account.';
 | 
					    : "Signup successful, please wait till an admin unlocks your account.";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,31 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ref, inject } from 'vue';
 | 
					import { ref, inject } from "vue";
 | 
				
			||||||
import { Auth, check_token, isErrorResponse, TokenInjectType } from '@/api';
 | 
					import { Auth, check_token, isErrorResponse, TokenInjectType } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum state {
 | 
					enum state {
 | 
				
			||||||
  SELECT,
 | 
					  SELECT,
 | 
				
			||||||
  MAIL,
 | 
					  MAIL,
 | 
				
			||||||
	TOTP
 | 
					  TOTP,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const currentState = ref<state>(state.SELECT);
 | 
					const currentState = ref<state>(state.SELECT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const error = ref('');
 | 
					const error = ref("");
 | 
				
			||||||
const qrImage = ref('');
 | 
					const qrImage = ref("");
 | 
				
			||||||
const secret = ref('');
 | 
					const secret = ref("");
 | 
				
			||||||
const code = ref('');
 | 
					const code = ref("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
					const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function selectMail() {
 | 
					async function selectMail() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
	error.value = 'Working...';
 | 
					  error.value = "Working...";
 | 
				
			||||||
  const res = await Auth.tfa_setup(true, token);
 | 
					  const res = await Auth.tfa_setup(true, token);
 | 
				
			||||||
  if (isErrorResponse(res))
 | 
					  if (isErrorResponse(res))
 | 
				
			||||||
		error.value = 'Failed to select 2fa type: ' + res.message;
 | 
					    error.value = "Failed to select 2fa type: " + res.message;
 | 
				
			||||||
  else {
 | 
					  else {
 | 
				
			||||||
		error.value = '';
 | 
					    error.value = "";
 | 
				
			||||||
    currentState.value = state.MAIL;
 | 
					    currentState.value = state.MAIL;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -33,14 +33,14 @@ async function selectMail() {
 | 
				
			|||||||
async function selectTotp() {
 | 
					async function selectTotp() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
	error.value = 'Working...';
 | 
					  error.value = "Working...";
 | 
				
			||||||
  const res = await Auth.tfa_setup(false, token);
 | 
					  const res = await Auth.tfa_setup(false, token);
 | 
				
			||||||
  if (isErrorResponse(res))
 | 
					  if (isErrorResponse(res))
 | 
				
			||||||
		error.value = 'Failed to select 2fa type: ' + res.message;
 | 
					    error.value = "Failed to select 2fa type: " + res.message;
 | 
				
			||||||
  else {
 | 
					  else {
 | 
				
			||||||
    qrImage.value = res.qrCode;
 | 
					    qrImage.value = res.qrCode;
 | 
				
			||||||
    secret.value = res.secret;
 | 
					    secret.value = res.secret;
 | 
				
			||||||
		error.value = '';
 | 
					    error.value = "";
 | 
				
			||||||
    currentState.value = state.TOTP;
 | 
					    currentState.value = state.TOTP;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -48,14 +48,14 @@ async function selectTotp() {
 | 
				
			|||||||
async function submit() {
 | 
					async function submit() {
 | 
				
			||||||
  const token = await check_token(jwt);
 | 
					  const token = await check_token(jwt);
 | 
				
			||||||
  if (!token) return;
 | 
					  if (!token) return;
 | 
				
			||||||
	error.value = 'Working...';
 | 
					  error.value = "Working...";
 | 
				
			||||||
  const res = await Auth.tfa_complete(
 | 
					  const res = await Auth.tfa_complete(
 | 
				
			||||||
    currentState.value === state.MAIL,
 | 
					    currentState.value === state.MAIL,
 | 
				
			||||||
    code.value,
 | 
					    code.value,
 | 
				
			||||||
    token
 | 
					    token
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  if (isErrorResponse(res))
 | 
					  if (isErrorResponse(res))
 | 
				
			||||||
		error.value = 'Failed to submit code: ' + res.message;
 | 
					    error.value = "Failed to submit code: " + res.message;
 | 
				
			||||||
  else jwt.logout();
 | 
					  else jwt.logout();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
const { defineConfig } = require('@vue/cli-service');
 | 
					const { defineConfig } = require("@vue/cli-service");
 | 
				
			||||||
module.exports = defineConfig({
 | 
					module.exports = defineConfig({
 | 
				
			||||||
  transpileDependencies: true,
 | 
					  transpileDependencies: true,
 | 
				
			||||||
  configureWebpack: {
 | 
					  configureWebpack: {
 | 
				
			||||||
    resolve: {
 | 
					    resolve: {
 | 
				
			||||||
      fallback: {
 | 
					      fallback: {
 | 
				
			||||||
        crypto: false,
 | 
					        crypto: false,
 | 
				
			||||||
				stream: require.resolve('stream-browserify')
 | 
					        stream: require.resolve("stream-browserify"),
 | 
				
			||||||
			}
 | 
					      },
 | 
				
			||||||
		}
 | 
					    },
 | 
				
			||||||
	}
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
	"$schema": "https://json.schemastore.org/nest-cli",
 | 
					 | 
				
			||||||
	"collection": "@nestjs/schematics",
 | 
					 | 
				
			||||||
	"monorepo": true,
 | 
					 | 
				
			||||||
	"sourceRoot": "src",
 | 
					 | 
				
			||||||
	"compilerOptions": {
 | 
					 | 
				
			||||||
		"tsConfigPath": "tsconfig.json"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										122
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								package.json
									
									
									
									
									
								
							@@ -1,122 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
	"name": "fileserver",
 | 
					 | 
				
			||||||
	"private": true,
 | 
					 | 
				
			||||||
	"version": "1.0.0",
 | 
					 | 
				
			||||||
	"description": "Crackhead fileserver",
 | 
					 | 
				
			||||||
	"license": "MIT",
 | 
					 | 
				
			||||||
	"scripts": {
 | 
					 | 
				
			||||||
		"prebuild": "rimraf dist",
 | 
					 | 
				
			||||||
		"build": "nest build",
 | 
					 | 
				
			||||||
		"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
 | 
					 | 
				
			||||||
		"start": "nest start",
 | 
					 | 
				
			||||||
		"start:dev": "nest start --watch",
 | 
					 | 
				
			||||||
		"lint": "eslint \"src/**/*.ts\"",
 | 
					 | 
				
			||||||
		"lint-fix": "eslint \"src/**/*.ts\" --fix",
 | 
					 | 
				
			||||||
		"test": "jest",
 | 
					 | 
				
			||||||
		"test:watch": "jest --watch",
 | 
					 | 
				
			||||||
		"test:cov": "jest --coverage",
 | 
					 | 
				
			||||||
		"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
 | 
					 | 
				
			||||||
		"test:e2e": "jest --config ./test/jest-e2e.json",
 | 
					 | 
				
			||||||
		"genapi": "ts-node tools/apigen.ts",
 | 
					 | 
				
			||||||
		"updateDto": "cd dto && yarn build && cd .. && yarn add ./dto && cd frontend && yarn add ../dto",
 | 
					 | 
				
			||||||
		"lint-fix-all": "yarn lint-fix && cd dto && yarn lint-fix && cd ../frontend && yarn lint --fix"
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	"dependencies": {
 | 
					 | 
				
			||||||
		"@fastify/multipart": "^7.1.0",
 | 
					 | 
				
			||||||
		"@fastify/static": "^6.5.0",
 | 
					 | 
				
			||||||
		"@nestjs/common": "^9.0.8",
 | 
					 | 
				
			||||||
		"@nestjs/core": "^9.0.8",
 | 
					 | 
				
			||||||
		"@nestjs/jwt": "^9.0.0",
 | 
					 | 
				
			||||||
		"@nestjs/passport": "^9.0.0",
 | 
					 | 
				
			||||||
		"@nestjs/platform-fastify": "^9.0.8",
 | 
					 | 
				
			||||||
		"@nestjs/serve-static": "^3.0.0",
 | 
					 | 
				
			||||||
		"@nestjs/typeorm": "^9.0.0",
 | 
					 | 
				
			||||||
		"argon2": "^0.28.7",
 | 
					 | 
				
			||||||
		"axios": "^0.27.2",
 | 
					 | 
				
			||||||
		"class-transformer": "^0.5.1",
 | 
					 | 
				
			||||||
		"class-validator": "^0.13.2",
 | 
					 | 
				
			||||||
		"jsonwebtoken": "^8.5.1",
 | 
					 | 
				
			||||||
		"nodemailer": "^6.7.8",
 | 
					 | 
				
			||||||
		"notp": "^2.0.3",
 | 
					 | 
				
			||||||
		"passport": "^0.6.0",
 | 
					 | 
				
			||||||
		"passport-jwt": "^4.0.0",
 | 
					 | 
				
			||||||
		"passport-local": "^1.0.0",
 | 
					 | 
				
			||||||
		"qrcode": "^1.5.1",
 | 
					 | 
				
			||||||
		"reflect-metadata": "^0.1.13",
 | 
					 | 
				
			||||||
		"rxjs": "^7.5.6",
 | 
					 | 
				
			||||||
		"sqlite3": "^5.0.11",
 | 
					 | 
				
			||||||
		"thirty-two": "^1.0.2",
 | 
					 | 
				
			||||||
		"typeorm": "^0.3.7"
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	"runtimeDependencies": [
 | 
					 | 
				
			||||||
		"@fastify/multipart",
 | 
					 | 
				
			||||||
		"@fastify/static",
 | 
					 | 
				
			||||||
		"@nestjs/common",
 | 
					 | 
				
			||||||
		"@nestjs/core",
 | 
					 | 
				
			||||||
		"@nestjs/platform-fastify",
 | 
					 | 
				
			||||||
		"@nestjs/serve-static",
 | 
					 | 
				
			||||||
		"argon2",
 | 
					 | 
				
			||||||
		"class-transformer",
 | 
					 | 
				
			||||||
		"class-validator",
 | 
					 | 
				
			||||||
		"reflect-metadata",
 | 
					 | 
				
			||||||
		"rxjs",
 | 
					 | 
				
			||||||
		"sqlite3",
 | 
					 | 
				
			||||||
		"typeorm"
 | 
					 | 
				
			||||||
	],
 | 
					 | 
				
			||||||
	"devDependencies": {
 | 
					 | 
				
			||||||
		"@nestjs/cli": "^9.0.0",
 | 
					 | 
				
			||||||
		"@nestjs/schematics": "^9.0.1",
 | 
					 | 
				
			||||||
		"@nestjs/testing": "^9.0.8",
 | 
					 | 
				
			||||||
		"@types/express": "^4.17.13",
 | 
					 | 
				
			||||||
		"@types/jest": "^28.1.6",
 | 
					 | 
				
			||||||
		"@types/jsonwebtoken": "^8.5.8",
 | 
					 | 
				
			||||||
		"@types/node": "^18.6.5",
 | 
					 | 
				
			||||||
		"@types/nodemailer": "^6.4.5",
 | 
					 | 
				
			||||||
		"@types/notp": "^2.0.2",
 | 
					 | 
				
			||||||
		"@types/passport-jwt": "^3.0.6",
 | 
					 | 
				
			||||||
		"@types/passport-local": "^1.0.34",
 | 
					 | 
				
			||||||
		"@types/qrcode": "^1.5.0",
 | 
					 | 
				
			||||||
		"@types/supertest": "^2.0.12",
 | 
					 | 
				
			||||||
		"@types/webpack": "^5.28.0",
 | 
					 | 
				
			||||||
		"@types/webpack-node-externals": "^2.5.3",
 | 
					 | 
				
			||||||
		"@typescript-eslint/eslint-plugin": "^5.33.0",
 | 
					 | 
				
			||||||
		"@typescript-eslint/parser": "^5.33.0",
 | 
					 | 
				
			||||||
		"@typescript-eslint/typescript-estree": "^5.33.0",
 | 
					 | 
				
			||||||
		"copy-webpack-plugin": "^11.0.0",
 | 
					 | 
				
			||||||
		"eslint": "^8.21.0",
 | 
					 | 
				
			||||||
		"eslint-config-prettier": "^8.5.0",
 | 
					 | 
				
			||||||
		"eslint-plugin-no-relative-import-paths": "^1.4.0",
 | 
					 | 
				
			||||||
		"eslint-plugin-prettier": "^4.2.1",
 | 
					 | 
				
			||||||
		"jest": "^28.1.3",
 | 
					 | 
				
			||||||
		"prettier": "^2.7.1",
 | 
					 | 
				
			||||||
		"rimraf": "^3.0.2",
 | 
					 | 
				
			||||||
		"source-map-support": "^0.5.21",
 | 
					 | 
				
			||||||
		"supertest": "^6.2.4",
 | 
					 | 
				
			||||||
		"ts-jest": "^28.0.7",
 | 
					 | 
				
			||||||
		"ts-loader": "^9.3.1",
 | 
					 | 
				
			||||||
		"ts-node": "^10.9.1",
 | 
					 | 
				
			||||||
		"tsconfig-paths": "^4.1.0",
 | 
					 | 
				
			||||||
		"tsconfig-paths-webpack-plugin": "^4.0.0",
 | 
					 | 
				
			||||||
		"typescript": "^4.7.4",
 | 
					 | 
				
			||||||
		"webpack": "^5.74.0",
 | 
					 | 
				
			||||||
		"webpack-cli": "^4.10.0",
 | 
					 | 
				
			||||||
		"webpack-node-externals": "^3.0.0"
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	"jest": {
 | 
					 | 
				
			||||||
		"moduleFileExtensions": [
 | 
					 | 
				
			||||||
			"js",
 | 
					 | 
				
			||||||
			"json",
 | 
					 | 
				
			||||||
			"ts"
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		"rootDir": "src",
 | 
					 | 
				
			||||||
		"testRegex": ".*\\.spec\\.ts$",
 | 
					 | 
				
			||||||
		"transform": {
 | 
					 | 
				
			||||||
			"^.+\\.(t|j)s$": "ts-jest"
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"collectCoverageFrom": [
 | 
					 | 
				
			||||||
			"**/*.(t|j)s"
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		"coverageDirectory": "../coverage",
 | 
					 | 
				
			||||||
		"testEnvironment": "node"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
### Create account
 | 
					 | 
				
			||||||
POST http://127.0.0.1:8080/api/auth/signup
 | 
					 | 
				
			||||||
Content-Type: application/json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{"username": "root@mattv.de", "password": "123"}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Wrong authenctication
 | 
					 | 
				
			||||||
POST http://127.0.0.1:8080/api/auth/login
 | 
					 | 
				
			||||||
Content-Type: application/json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{"username": "root@mattv.de", "password": "this is not correct"}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Correct authentication
 | 
					 | 
				
			||||||
POST http://127.0.0.1:8080/api/auth/login
 | 
					 | 
				
			||||||
Content-Type: application/json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{"username": "root@mattv.de", "password": "123"}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> {% client.global.set("auth_token", response.body.jwt); %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Check if authenticated with admin perms
 | 
					 | 
				
			||||||
GET http://127.0.0.1:8080/test/hello2
 | 
					 | 
				
			||||||
Authorization: Bearer {{auth_token}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Refresh token
 | 
					 | 
				
			||||||
POST http://127.0.0.1:8080/api/auth/refresh
 | 
					 | 
				
			||||||
Authorization: Bearer {{auth_token}}
 | 
					 | 
				
			||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
import { Controller, Get, Module } from '@nestjs/common';
 | 
					 | 
				
			||||||
import { TypeOrmModule } from '@nestjs/typeorm';
 | 
					 | 
				
			||||||
import { INode, JWTToken, User } from './entities';
 | 
					 | 
				
			||||||
import FileSystemModule from './modules/filesystem';
 | 
					 | 
				
			||||||
import { JWTAuthGuard, Role, RoleGuard } from './authguards';
 | 
					 | 
				
			||||||
import AuthModule from './modules/auth';
 | 
					 | 
				
			||||||
import { ServeStaticModule } from '@nestjs/serve-static';
 | 
					 | 
				
			||||||
import { join } from 'path';
 | 
					 | 
				
			||||||
import { cwd } from 'process';
 | 
					 | 
				
			||||||
import { UserRole } from '../dto/';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
declare const PROD: boolean | undefined;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Controller('test')
 | 
					 | 
				
			||||||
class TestController {
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	@Get('hello')
 | 
					 | 
				
			||||||
	getHello(): string {
 | 
					 | 
				
			||||||
		return 'UwU';
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Get('hello2')
 | 
					 | 
				
			||||||
	getHelloAdmin(): string {
 | 
					 | 
				
			||||||
		return 'UwU Admin';
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Module({
 | 
					 | 
				
			||||||
	imports: [
 | 
					 | 
				
			||||||
		TypeOrmModule.forRoot({
 | 
					 | 
				
			||||||
			type: 'sqlite',
 | 
					 | 
				
			||||||
			database: 'sqlite.db',
 | 
					 | 
				
			||||||
			synchronize: true,
 | 
					 | 
				
			||||||
			entities: [User, INode, JWTToken]
 | 
					 | 
				
			||||||
		}),
 | 
					 | 
				
			||||||
		ServeStaticModule.forRoot({
 | 
					 | 
				
			||||||
			rootPath:
 | 
					 | 
				
			||||||
				typeof PROD !== 'undefined' && PROD
 | 
					 | 
				
			||||||
					? join(cwd(), 'frontend')
 | 
					 | 
				
			||||||
					: join(__dirname, '..', '..', 'frontend', 'dist'),
 | 
					 | 
				
			||||||
			exclude: ['/api*']
 | 
					 | 
				
			||||||
		}),
 | 
					 | 
				
			||||||
		FileSystemModule,
 | 
					 | 
				
			||||||
		AuthModule
 | 
					 | 
				
			||||||
	],
 | 
					 | 
				
			||||||
	controllers: [TestController],
 | 
					 | 
				
			||||||
	providers: [
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			provide: 'APP_GUARD',
 | 
					 | 
				
			||||||
			useClass: JWTAuthGuard
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			provide: 'APP_GUARD',
 | 
					 | 
				
			||||||
			useClass: RoleGuard
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	]
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
export class AppModule {}
 | 
					 | 
				
			||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
	CanActivate,
 | 
					 | 
				
			||||||
	ExecutionContext,
 | 
					 | 
				
			||||||
	Injectable,
 | 
					 | 
				
			||||||
	SetMetadata
 | 
					 | 
				
			||||||
} from '@nestjs/common';
 | 
					 | 
				
			||||||
import { AuthGuard } from '@nestjs/passport';
 | 
					 | 
				
			||||||
import { Reflector } from '@nestjs/core';
 | 
					 | 
				
			||||||
import { User } from './entities';
 | 
					 | 
				
			||||||
import { UserRole } from '../dto';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const IS_PUBLIC_KEY = 'isPublic';
 | 
					 | 
				
			||||||
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Injectable()
 | 
					 | 
				
			||||||
export class JWTAuthGuard extends AuthGuard('jwt') {
 | 
					 | 
				
			||||||
	constructor(private reflector: Reflector) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	canActivate(context: ExecutionContext) {
 | 
					 | 
				
			||||||
		const isPublic = this.reflector.getAllAndOverride<boolean>(
 | 
					 | 
				
			||||||
			IS_PUBLIC_KEY,
 | 
					 | 
				
			||||||
			[context.getHandler(), context.getClass()]
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		if (isPublic) return true;
 | 
					 | 
				
			||||||
		return super.canActivate(context);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ROLE_KEY = 'role';
 | 
					 | 
				
			||||||
export const Role = (role: UserRole) => SetMetadata(ROLE_KEY, role);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Injectable()
 | 
					 | 
				
			||||||
export class RoleGuard implements CanActivate {
 | 
					 | 
				
			||||||
	constructor(private reflector: Reflector) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	canActivate(context: ExecutionContext) {
 | 
					 | 
				
			||||||
		const requiredRole = this.reflector.getAllAndOverride<UserRole>(
 | 
					 | 
				
			||||||
			ROLE_KEY,
 | 
					 | 
				
			||||||
			[context.getHandler(), context.getClass()]
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		if (!requiredRole) return true;
 | 
					 | 
				
			||||||
		const user: User = context.switchToHttp().getRequest().user;
 | 
					 | 
				
			||||||
		return user.role >= requiredRole;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,83 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
	BadRequestException,
 | 
					 | 
				
			||||||
	Body,
 | 
					 | 
				
			||||||
	Controller,
 | 
					 | 
				
			||||||
	Get,
 | 
					 | 
				
			||||||
	Post,
 | 
					 | 
				
			||||||
	Request,
 | 
					 | 
				
			||||||
	ValidationPipe
 | 
					 | 
				
			||||||
} from '@nestjs/common';
 | 
					 | 
				
			||||||
import { AuthService } from 'services/auth';
 | 
					 | 
				
			||||||
import { Requests, Responses, UserRole } from '../../dto';
 | 
					 | 
				
			||||||
import { Role } from 'authguards';
 | 
					 | 
				
			||||||
import { tfaTypes } from 'entities';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Controller('api/admin')
 | 
					 | 
				
			||||||
export default class AdminController {
 | 
					 | 
				
			||||||
	constructor(private authService: AuthService) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Get('users')
 | 
					 | 
				
			||||||
	async getUsers(): Promise<Responses.Admin.GetUsers> {
 | 
					 | 
				
			||||||
		const users = await this.authService.getUsers();
 | 
					 | 
				
			||||||
		const entries = users.map(
 | 
					 | 
				
			||||||
			(user) =>
 | 
					 | 
				
			||||||
				new Responses.Admin.GetUsersEntry(
 | 
					 | 
				
			||||||
					user.id,
 | 
					 | 
				
			||||||
					user.isGitlabUser,
 | 
					 | 
				
			||||||
					user.name,
 | 
					 | 
				
			||||||
					user.role,
 | 
					 | 
				
			||||||
					this.authService.requiresTfa(user)
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return new Responses.Admin.GetUsers(entries);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Post('set_role')
 | 
					 | 
				
			||||||
	async setRole(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Admin.SetUserRole
 | 
					 | 
				
			||||||
	): Promise<Responses.Admin.SetUserRole> {
 | 
					 | 
				
			||||||
		const user = await this.authService.getUser(data.user);
 | 
					 | 
				
			||||||
		if (!user) throw new BadRequestException('Invalid user');
 | 
					 | 
				
			||||||
		await this.authService.setUserRole(user, data.role);
 | 
					 | 
				
			||||||
		return new Responses.Admin.SetUserRole();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Post('logout')
 | 
					 | 
				
			||||||
	async logout(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Admin.LogoutAll
 | 
					 | 
				
			||||||
	): Promise<Responses.Admin.LogoutAllUser> {
 | 
					 | 
				
			||||||
		const user = await this.authService.getUser(data.user);
 | 
					 | 
				
			||||||
		if (!user) throw new BadRequestException('Invalid user');
 | 
					 | 
				
			||||||
		await this.authService.revokeAll(user);
 | 
					 | 
				
			||||||
		return new Responses.Admin.LogoutAllUser();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Post('delete')
 | 
					 | 
				
			||||||
	async delete(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Admin.DeleteUser
 | 
					 | 
				
			||||||
	): Promise<Responses.Admin.DeleteUser> {
 | 
					 | 
				
			||||||
		const user = await this.authService.getUser(data.user);
 | 
					 | 
				
			||||||
		if (!user) throw new BadRequestException('Invalid user');
 | 
					 | 
				
			||||||
		await this.authService.deleteUser(user);
 | 
					 | 
				
			||||||
		return new Responses.Admin.DeleteUser();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Role(UserRole.ADMIN)
 | 
					 | 
				
			||||||
	@Post('disable_2fa')
 | 
					 | 
				
			||||||
	async disableTfa(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Admin.DisableTfa
 | 
					 | 
				
			||||||
	): Promise<Responses.Admin.DisableTfa> {
 | 
					 | 
				
			||||||
		const user = await this.authService.getUser(data.user);
 | 
					 | 
				
			||||||
		if (!user) throw new BadRequestException('Invalid user');
 | 
					 | 
				
			||||||
		await this.authService.setTfaType(user, tfaTypes.NONE);
 | 
					 | 
				
			||||||
		return new Responses.Admin.DisableTfa();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,150 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
	BadRequestException,
 | 
					 | 
				
			||||||
	Body,
 | 
					 | 
				
			||||||
	Controller,
 | 
					 | 
				
			||||||
	Get,
 | 
					 | 
				
			||||||
	HttpCode,
 | 
					 | 
				
			||||||
	Post,
 | 
					 | 
				
			||||||
	Query,
 | 
					 | 
				
			||||||
	Redirect,
 | 
					 | 
				
			||||||
	Request,
 | 
					 | 
				
			||||||
	UnauthorizedException,
 | 
					 | 
				
			||||||
	UseGuards,
 | 
					 | 
				
			||||||
	ValidationPipe
 | 
					 | 
				
			||||||
} from '@nestjs/common';
 | 
					 | 
				
			||||||
import { AuthService } from 'services/auth';
 | 
					 | 
				
			||||||
import { AuthGuard } from '@nestjs/passport';
 | 
					 | 
				
			||||||
import { Public } from 'authguards';
 | 
					 | 
				
			||||||
import { Requests, Responses } from '../../dto';
 | 
					 | 
				
			||||||
import { tfaTypes } from 'entities';
 | 
					 | 
				
			||||||
import { toDataURL } from 'qrcode';
 | 
					 | 
				
			||||||
import * as base32 from 'thirty-two';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Controller('api/auth')
 | 
					 | 
				
			||||||
export default class AuthController {
 | 
					 | 
				
			||||||
	constructor(private authService: AuthService) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Public()
 | 
					 | 
				
			||||||
	@UseGuards(AuthGuard('local'))
 | 
					 | 
				
			||||||
	@Post('login')
 | 
					 | 
				
			||||||
	@HttpCode(200)
 | 
					 | 
				
			||||||
	async login(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Auth.LoginRequest
 | 
					 | 
				
			||||||
	): Promise<
 | 
					 | 
				
			||||||
		Responses.Auth.LoginResponse | Responses.Auth.TfaRequiredResponse
 | 
					 | 
				
			||||||
	> {
 | 
					 | 
				
			||||||
		if (this.authService.requiresTfa(req.user)) {
 | 
					 | 
				
			||||||
			if (!data.otp) {
 | 
					 | 
				
			||||||
				if (req.user.tfaType == tfaTypes.EMAIL)
 | 
					 | 
				
			||||||
					await this.authService.sendTfaMail(req.user);
 | 
					 | 
				
			||||||
				return new Responses.Auth.TfaRequiredResponse();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (!(await this.authService.verifyTfa(req.user, data.otp)))
 | 
					 | 
				
			||||||
				throw new UnauthorizedException('Incorrect 2fa');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return new Responses.Auth.LoginResponse(
 | 
					 | 
				
			||||||
			await this.authService.login(req, req.user)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('2fa/disable')
 | 
					 | 
				
			||||||
	async tfaDisable(
 | 
					 | 
				
			||||||
		@Request() req
 | 
					 | 
				
			||||||
	): Promise<Responses.Auth.RemoveTfaResponse> {
 | 
					 | 
				
			||||||
		await this.authService.setTfaType(req.user, tfaTypes.NONE);
 | 
					 | 
				
			||||||
		await this.authService.revokeAll(req.user);
 | 
					 | 
				
			||||||
		return new Responses.Auth.RemoveTfaResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('2fa/complete')
 | 
					 | 
				
			||||||
	async tfaMail(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Auth.TfaComplete
 | 
					 | 
				
			||||||
	): Promise<Responses.Auth.TfaCompletedResponse> {
 | 
					 | 
				
			||||||
		const type = data.mail ? tfaTypes.EMAIL : tfaTypes.TOTP;
 | 
					 | 
				
			||||||
		if (!(await this.authService.verifyTfa(req.user, data.code, type))) {
 | 
					 | 
				
			||||||
			throw new UnauthorizedException('Incorrect 2fa');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		await this.authService.setTfaType(req.user, type);
 | 
					 | 
				
			||||||
		await this.authService.revokeAll(req.user);
 | 
					 | 
				
			||||||
		return new Responses.Auth.TfaCompletedResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('2fa/setup')
 | 
					 | 
				
			||||||
	async setupTotp(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Auth.TfaSetup
 | 
					 | 
				
			||||||
	): Promise<
 | 
					 | 
				
			||||||
		| Responses.Auth.RequestTotpTfaResponse
 | 
					 | 
				
			||||||
		| Responses.Auth.RequestEmailTfaResponse
 | 
					 | 
				
			||||||
	> {
 | 
					 | 
				
			||||||
		const secret = await this.authService.setupTfa(req.user);
 | 
					 | 
				
			||||||
		if (data.mail) return new Responses.Auth.RequestEmailTfaResponse();
 | 
					 | 
				
			||||||
		return new Responses.Auth.RequestTotpTfaResponse(
 | 
					 | 
				
			||||||
			await toDataURL(
 | 
					 | 
				
			||||||
				`otpauth://totp/MFileserver:${req.user.name}?secret=${base32
 | 
					 | 
				
			||||||
					.encode(secret)
 | 
					 | 
				
			||||||
					.toString()}&issuer=MFileserver`
 | 
					 | 
				
			||||||
			),
 | 
					 | 
				
			||||||
			base32.encode(secret).toString()
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Public()
 | 
					 | 
				
			||||||
	@Post('signup')
 | 
					 | 
				
			||||||
	async signup(
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Auth.SignUpRequest
 | 
					 | 
				
			||||||
	): Promise<Responses.Auth.SignupResponse> {
 | 
					 | 
				
			||||||
		if ((await this.authService.findUser(data.username, false)) != null)
 | 
					 | 
				
			||||||
			throw new BadRequestException('Username already taken');
 | 
					 | 
				
			||||||
		await this.authService.signup(data.username, data.password);
 | 
					 | 
				
			||||||
		return new Responses.Auth.SignupResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('refresh')
 | 
					 | 
				
			||||||
	async refresh(@Request() req): Promise<Responses.Auth.RefreshResponse> {
 | 
					 | 
				
			||||||
		const token = await this.authService.login(req, req.user);
 | 
					 | 
				
			||||||
		await this.authService.revoke(req.token);
 | 
					 | 
				
			||||||
		return await new Responses.Auth.RefreshResponse(token);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Public()
 | 
					 | 
				
			||||||
	@Redirect()
 | 
					 | 
				
			||||||
	@Get('gitlab')
 | 
					 | 
				
			||||||
	async gitlab(@Request() req) {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			url: this.authService.getGitlabAuthUrl(req)
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Public()
 | 
					 | 
				
			||||||
	@Redirect()
 | 
					 | 
				
			||||||
	@Get('gitlab_callback')
 | 
					 | 
				
			||||||
	async gitlabCallback(@Request() req, @Query('code') code) {
 | 
					 | 
				
			||||||
		const user = await this.authService.getGitlabUserFromCode(req, code);
 | 
					 | 
				
			||||||
		const token = await this.authService.login(req, user);
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			url: `/set_token?token=${token}`
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('change_password')
 | 
					 | 
				
			||||||
	async changePassword(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.Auth.ChangePasswordRequest
 | 
					 | 
				
			||||||
	): Promise<Responses.Auth.ChangePasswordResponse> {
 | 
					 | 
				
			||||||
		await this.authService.changePassword(
 | 
					 | 
				
			||||||
			req.user,
 | 
					 | 
				
			||||||
			data.oldPassword,
 | 
					 | 
				
			||||||
			data.newPassword
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return new Responses.Auth.ChangePasswordResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('logout_all')
 | 
					 | 
				
			||||||
	async logoutAll(@Request() req): Promise<Responses.Auth.LogoutAllResponse> {
 | 
					 | 
				
			||||||
		await this.authService.revokeAll(req.user);
 | 
					 | 
				
			||||||
		return new Responses.Auth.LogoutAllResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,121 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
	Body,
 | 
					 | 
				
			||||||
	Controller,
 | 
					 | 
				
			||||||
	Get,
 | 
					 | 
				
			||||||
	Param,
 | 
					 | 
				
			||||||
	ParseIntPipe,
 | 
					 | 
				
			||||||
	Post,
 | 
					 | 
				
			||||||
	Request,
 | 
					 | 
				
			||||||
	StreamableFile,
 | 
					 | 
				
			||||||
	ValidationPipe
 | 
					 | 
				
			||||||
} from '@nestjs/common';
 | 
					 | 
				
			||||||
import { Responses, Requests, validateAsyncInline, UserRole } from '../../dto';
 | 
					 | 
				
			||||||
import FileSystemService from 'services/filesystem';
 | 
					 | 
				
			||||||
import { Role } from 'authguards';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Controller('api/fs')
 | 
					 | 
				
			||||||
export default class FileSystemController {
 | 
					 | 
				
			||||||
	constructor(private fsService: FileSystemService) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Get('root')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async getRoot(@Request() req): Promise<Responses.FS.GetRootResponse> {
 | 
					 | 
				
			||||||
		return new Responses.FS.GetRootResponse(req.user.rootId);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Get('node/:node')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async getNode(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Param('node', ParseIntPipe) nodeId
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.GetNodeResponse> {
 | 
					 | 
				
			||||||
		const node = await this.fsService.getNodeAndValidate(nodeId, req.user);
 | 
					 | 
				
			||||||
		const data = new Responses.FS.GetNodeResponse(
 | 
					 | 
				
			||||||
			nodeId,
 | 
					 | 
				
			||||||
			node.name,
 | 
					 | 
				
			||||||
			node.isFile,
 | 
					 | 
				
			||||||
			node.parentId
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (data.isFile) {
 | 
					 | 
				
			||||||
			data.size = node.size;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			data.children = (await node.children).map((child) => child.id);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return validateAsyncInline(data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Get('path/:node')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async getPath(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Param('node', ParseIntPipe) nodeId
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.GetPathResponse> {
 | 
					 | 
				
			||||||
		return new Responses.FS.GetPathResponse(
 | 
					 | 
				
			||||||
			await this.fsService.generatePath(
 | 
					 | 
				
			||||||
				await this.fsService.getNodeAndValidate(nodeId, req.user)
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('createFolder')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async createFolder(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.FS.CreateFolderRequest
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.CreateFolderResponse> {
 | 
					 | 
				
			||||||
		const newChild = await this.fsService.create(
 | 
					 | 
				
			||||||
			await this.fsService.getNodeAndValidate(data.parent, req.user),
 | 
					 | 
				
			||||||
			data.name,
 | 
					 | 
				
			||||||
			req.user,
 | 
					 | 
				
			||||||
			false
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return new Responses.FS.CreateFolderResponse(newChild.id);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('createFile')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async createFile(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.FS.CreateFileRequest
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.CreateFileResponse> {
 | 
					 | 
				
			||||||
		const newChild = await this.fsService.create(
 | 
					 | 
				
			||||||
			await this.fsService.getNodeAndValidate(data.parent, req.user),
 | 
					 | 
				
			||||||
			data.name,
 | 
					 | 
				
			||||||
			req.user,
 | 
					 | 
				
			||||||
			true
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return new Responses.FS.CreateFileResponse(newChild.id);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('delete')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async delete(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body(new ValidationPipe()) data: Requests.FS.DeleteRequest
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.DeleteResponse> {
 | 
					 | 
				
			||||||
		await this.fsService.delete(
 | 
					 | 
				
			||||||
			await this.fsService.getNodeAndValidate(data.node, req.user)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return new Responses.FS.DeleteResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('upload/:node')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async upload(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Param('node', ParseIntPipe) nodeId
 | 
					 | 
				
			||||||
	): Promise<Responses.FS.UploadFileResponse> {
 | 
					 | 
				
			||||||
		await this.fsService.uploadFile(await req.file(), nodeId, req.user);
 | 
					 | 
				
			||||||
		return new Responses.FS.UploadFileResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('download')
 | 
					 | 
				
			||||||
	@Role(UserRole.USER)
 | 
					 | 
				
			||||||
	async download(
 | 
					 | 
				
			||||||
		@Request() req,
 | 
					 | 
				
			||||||
		@Body('id', ParseIntPipe) id
 | 
					 | 
				
			||||||
	): Promise<StreamableFile> {
 | 
					 | 
				
			||||||
		return this.fsService.downloadFile(id, req.user);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
import { Controller, Get, Post, Request } from '@nestjs/common';
 | 
					 | 
				
			||||||
import { AuthService } from 'services/auth';
 | 
					 | 
				
			||||||
import { Responses } from '../../dto';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Controller('api/user')
 | 
					 | 
				
			||||||
export default class UserController {
 | 
					 | 
				
			||||||
	constructor(private authService: AuthService) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Get('info')
 | 
					 | 
				
			||||||
	async getUserInfo(
 | 
					 | 
				
			||||||
		@Request() req
 | 
					 | 
				
			||||||
	): Promise<Responses.User.UserInfoResponse> {
 | 
					 | 
				
			||||||
		return new Responses.User.UserInfoResponse(
 | 
					 | 
				
			||||||
			req.user.name,
 | 
					 | 
				
			||||||
			req.user.isGitlabUser,
 | 
					 | 
				
			||||||
			this.authService.requiresTfa(req.user)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Post('delete')
 | 
					 | 
				
			||||||
	async deleteUser(
 | 
					 | 
				
			||||||
		@Request() req
 | 
					 | 
				
			||||||
	): Promise<Responses.User.DeleteUserResponse> {
 | 
					 | 
				
			||||||
		await this.authService.deleteUser(req.user);
 | 
					 | 
				
			||||||
		return new Responses.User.DeleteUserResponse();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,95 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
	Entity,
 | 
					 | 
				
			||||||
	Column,
 | 
					 | 
				
			||||||
	PrimaryGeneratedColumn,
 | 
					 | 
				
			||||||
	ManyToOne,
 | 
					 | 
				
			||||||
	OneToMany,
 | 
					 | 
				
			||||||
	OneToOne
 | 
					 | 
				
			||||||
} from 'typeorm';
 | 
					 | 
				
			||||||
import { UserRole } from '../dto';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export enum tfaTypes {
 | 
					 | 
				
			||||||
	NONE = 0,
 | 
					 | 
				
			||||||
	EMAIL = 1,
 | 
					 | 
				
			||||||
	TOTP = 2
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Entity()
 | 
					 | 
				
			||||||
export class INode {
 | 
					 | 
				
			||||||
	@PrimaryGeneratedColumn()
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
	@Column()
 | 
					 | 
				
			||||||
	isFile: boolean;
 | 
					 | 
				
			||||||
	@Column()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	size: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	parentId: number;
 | 
					 | 
				
			||||||
	@ManyToOne(() => INode, (node) => node.children)
 | 
					 | 
				
			||||||
	parent: Promise<INode>;
 | 
					 | 
				
			||||||
	@OneToMany(() => INode, (node) => node.parent)
 | 
					 | 
				
			||||||
	children: Promise<INode[]>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	ownerId: number;
 | 
					 | 
				
			||||||
	@ManyToOne(() => User)
 | 
					 | 
				
			||||||
	owner: Promise<User>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Entity()
 | 
					 | 
				
			||||||
export class User {
 | 
					 | 
				
			||||||
	@PrimaryGeneratedColumn()
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
	@Column({ default: false })
 | 
					 | 
				
			||||||
	isGitlabUser: boolean;
 | 
					 | 
				
			||||||
	@Column()
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
	@Column()
 | 
					 | 
				
			||||||
	password: string;
 | 
					 | 
				
			||||||
	@Column({
 | 
					 | 
				
			||||||
		type: 'int',
 | 
					 | 
				
			||||||
		default: UserRole.DISABLED,
 | 
					 | 
				
			||||||
		transformer: {
 | 
					 | 
				
			||||||
			from: (db: number): UserRole => db,
 | 
					 | 
				
			||||||
			to: (role: UserRole): number => role
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	role: UserRole;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	rootId: number;
 | 
					 | 
				
			||||||
	@OneToOne(() => INode)
 | 
					 | 
				
			||||||
	root: Promise<INode>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({
 | 
					 | 
				
			||||||
		type: 'int',
 | 
					 | 
				
			||||||
		default: tfaTypes.NONE,
 | 
					 | 
				
			||||||
		transformer: {
 | 
					 | 
				
			||||||
			from: (db: number): tfaTypes => db,
 | 
					 | 
				
			||||||
			to: (type: tfaTypes): number => type
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	tfaType: tfaTypes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	tfaSecret: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	gitlabAT: string;
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	gitlabRT: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Entity()
 | 
					 | 
				
			||||||
export class JWTToken {
 | 
					 | 
				
			||||||
	@PrimaryGeneratedColumn()
 | 
					 | 
				
			||||||
	id: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column()
 | 
					 | 
				
			||||||
	ownerId: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column({ nullable: true })
 | 
					 | 
				
			||||||
	exp: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										20
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/main.ts
									
									
									
									
									
								
							@@ -1,20 +0,0 @@
 | 
				
			|||||||
import { NestFactory } from '@nestjs/core';
 | 
					 | 
				
			||||||
import { AppModule } from './app.module';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
	FastifyAdapter,
 | 
					 | 
				
			||||||
	NestFastifyApplication
 | 
					 | 
				
			||||||
} from '@nestjs/platform-fastify';
 | 
					 | 
				
			||||||
import fastifyMultipart from '@fastify/multipart';
 | 
					 | 
				
			||||||
import { existsSync, mkdirSync } from 'fs';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function bootstrap() {
 | 
					 | 
				
			||||||
	if (!existsSync('files')) mkdirSync('files');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const app = await NestFactory.create<NestFastifyApplication>(
 | 
					 | 
				
			||||||
		AppModule,
 | 
					 | 
				
			||||||
		new FastifyAdapter({ logger: true })
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
	await app.register(fastifyMultipart);
 | 
					 | 
				
			||||||
	await app.listen(8080, '0.0.0.0');
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
bootstrap();
 | 
					 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
import { Module } from '@nestjs/common';
 | 
					 | 
				
			||||||
import { TypeOrmModule } from '@nestjs/typeorm';
 | 
					 | 
				
			||||||
import { INode, JWTToken, User } from 'entities';
 | 
					 | 
				
			||||||
import { AuthService, AuthLocalService, AuthJwtService } from 'services/auth';
 | 
					 | 
				
			||||||
import FileSystemService from 'services/filesystem';
 | 
					 | 
				
			||||||
import AuthController from 'controller/auth';
 | 
					 | 
				
			||||||
import UserController from 'controller/user';
 | 
					 | 
				
			||||||
import AdminController from 'controller/admin';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Module({
 | 
					 | 
				
			||||||
	imports: [TypeOrmModule.forFeature([User, INode, JWTToken])],
 | 
					 | 
				
			||||||
	providers: [
 | 
					 | 
				
			||||||
		AuthService,
 | 
					 | 
				
			||||||
		AuthLocalService,
 | 
					 | 
				
			||||||
		AuthJwtService,
 | 
					 | 
				
			||||||
		FileSystemService
 | 
					 | 
				
			||||||
	],
 | 
					 | 
				
			||||||
	controllers: [AuthController, UserController, AdminController]
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
export default class AuthModule {}
 | 
					 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user