Rewrote backend in c++
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -115,3 +115,5 @@ fabric.properties
 | 
			
		||||
.idea/**/azureSettings.xml
 | 
			
		||||
 | 
			
		||||
# End of https://www.toptal.com/developers/gitignore/api/clion
 | 
			
		||||
 | 
			
		||||
run/
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
							
								
								
									
										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/filters/filters.h
 | 
			
		||||
        src/filters/filters.cpp
 | 
			
		||||
        src/controllers/auth/auth_gitlab.cpp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if (MINGW)
 | 
			
		||||
    target_link_libraries(backend -static-libgcc -static-libstdc++)
 | 
			
		||||
endif (MINGW)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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 = {
 | 
			
		||||
	presets: ['@vue/cli-plugin-babel/preset']
 | 
			
		||||
  presets: ["@vue/cli-plugin-babel/preset"],
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "axios": "^0.27.2",
 | 
			
		||||
    "class-transformer": "^0.5.1",
 | 
			
		||||
    "class-validator": "^0.13.2",
 | 
			
		||||
    "core-js": "^3.8.3",
 | 
			
		||||
    "filesize": "^9.0.11",
 | 
			
		||||
    "jwt-decode": "^3.1.2",
 | 
			
		||||
@@ -27,8 +29,6 @@
 | 
			
		||||
    "@vue/cli-plugin-typescript": "~5.0.0",
 | 
			
		||||
    "@vue/cli-service": "~5.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "^9.1.0",
 | 
			
		||||
    "class-transformer": "^0.5.1",
 | 
			
		||||
    "class-validator": "^0.13.2",
 | 
			
		||||
    "eslint": "^7.32.0",
 | 
			
		||||
    "eslint-config-prettier": "^8.3.0",
 | 
			
		||||
    "eslint-plugin-prettier": "^4.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,27 @@
 | 
			
		||||
<script setup async lang="ts">
 | 
			
		||||
import { provide, ref } from 'vue';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { TokenInjectType } from '@/api';
 | 
			
		||||
import { provide, ref } from "vue";
 | 
			
		||||
import { useRouter } from "vue-router";
 | 
			
		||||
import { TokenInjectType } from "@/api";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 | 
			
		||||
const jwt = ref<string | null>(localStorage.getItem('token'));
 | 
			
		||||
const jwt = ref<string | null>(localStorage.getItem("token"));
 | 
			
		||||
 | 
			
		||||
function setToken(token: string) {
 | 
			
		||||
  jwt.value = token;
 | 
			
		||||
	localStorage.setItem('token', token);
 | 
			
		||||
  localStorage.setItem("token", token);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logout() {
 | 
			
		||||
  jwt.value = null;
 | 
			
		||||
	localStorage.removeItem('token');
 | 
			
		||||
	router.push({ name: 'login' });
 | 
			
		||||
  localStorage.removeItem("token");
 | 
			
		||||
  router.push({ name: "login" });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
provide<TokenInjectType>('jwt', {
 | 
			
		||||
provide<TokenInjectType>("jwt", {
 | 
			
		||||
  jwt,
 | 
			
		||||
  setToken,
 | 
			
		||||
	logout
 | 
			
		||||
  logout,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import App from './App';
 | 
			
		||||
import App from "./App";
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<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> =>
 | 
			
		||||
	get_token('/api/admin/users', token);
 | 
			
		||||
  get_token("/api/admin/users", token);
 | 
			
		||||
 | 
			
		||||
export const set_role = (
 | 
			
		||||
  user: number,
 | 
			
		||||
@@ -9,10 +9,10 @@ export const set_role = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Admin.SetUserRole | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Admin.SetUserRole>(
 | 
			
		||||
		'/api/admin/set_role',
 | 
			
		||||
    "/api/admin/set_role",
 | 
			
		||||
    {
 | 
			
		||||
      user,
 | 
			
		||||
			role
 | 
			
		||||
      role,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -22,9 +22,9 @@ export const logout = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Admin.LogoutAllUser | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Admin.LogoutAll>(
 | 
			
		||||
		'/api/admin/logout',
 | 
			
		||||
    "/api/admin/logout",
 | 
			
		||||
    {
 | 
			
		||||
			user
 | 
			
		||||
      user,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -34,9 +34,9 @@ export const delete_user = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Admin.DeleteUser | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Admin.DeleteUser>(
 | 
			
		||||
		'/api/admin/delete',
 | 
			
		||||
    "/api/admin/delete",
 | 
			
		||||
    {
 | 
			
		||||
			user
 | 
			
		||||
      user,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -46,9 +46,9 @@ export const disable_tfa = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Admin.DisableTfa | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Admin.DisableTfa>(
 | 
			
		||||
		'/api/admin/disable_2fa',
 | 
			
		||||
    "/api/admin/disable_2fa",
 | 
			
		||||
    {
 | 
			
		||||
			user
 | 
			
		||||
      user,
 | 
			
		||||
    },
 | 
			
		||||
    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 = (
 | 
			
		||||
  username: string,
 | 
			
		||||
@@ -9,25 +9,25 @@ export const auth_login = (
 | 
			
		||||
  | Responses.Auth.TfaRequiredResponse
 | 
			
		||||
  | Responses.ErrorResponse
 | 
			
		||||
> =>
 | 
			
		||||
	post<Requests.Auth.LoginRequest>('/api/auth/login', {
 | 
			
		||||
  post<Requests.Auth.LoginRequest>("/api/auth/login", {
 | 
			
		||||
    username: username,
 | 
			
		||||
    password: password,
 | 
			
		||||
		otp: otp
 | 
			
		||||
    otp: otp,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export const auth_signup = (
 | 
			
		||||
  username: string,
 | 
			
		||||
  password: string
 | 
			
		||||
): Promise<Responses.Auth.SignupResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post<Requests.Auth.SignUpRequest>('/api/auth/signup', {
 | 
			
		||||
  post<Requests.Auth.SignUpRequest>("/api/auth/signup", {
 | 
			
		||||
    username: username,
 | 
			
		||||
		password: password
 | 
			
		||||
    password: password,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export const refresh_token = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Auth.RefreshResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token('/api/auth/refresh', {}, token);
 | 
			
		||||
  post_token("/api/auth/refresh", {}, token);
 | 
			
		||||
 | 
			
		||||
export const change_password = (
 | 
			
		||||
  oldPw: string,
 | 
			
		||||
@@ -35,10 +35,10 @@ export const change_password = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Auth.ChangePasswordResponse | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Auth.ChangePasswordRequest>(
 | 
			
		||||
		'/api/auth/change_password',
 | 
			
		||||
    "/api/auth/change_password",
 | 
			
		||||
    {
 | 
			
		||||
      oldPassword: oldPw,
 | 
			
		||||
			newPassword: newPw
 | 
			
		||||
      newPassword: newPw,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -46,7 +46,7 @@ export const change_password = (
 | 
			
		||||
export const logout_all = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Auth.LogoutAllResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token('/api/auth/logout_all', {}, token);
 | 
			
		||||
  post_token("/api/auth/logout_all", {}, token);
 | 
			
		||||
 | 
			
		||||
export function tfa_setup(
 | 
			
		||||
  mail: false,
 | 
			
		||||
@@ -65,9 +65,9 @@ export function tfa_setup(
 | 
			
		||||
  | Responses.ErrorResponse
 | 
			
		||||
> {
 | 
			
		||||
  return post_token<Requests.Auth.TfaSetup>(
 | 
			
		||||
		'/api/auth/2fa/setup',
 | 
			
		||||
    "/api/auth/2fa/setup",
 | 
			
		||||
    {
 | 
			
		||||
			mail
 | 
			
		||||
      mail,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -79,10 +79,10 @@ export const tfa_complete = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.Auth.TfaCompletedResponse | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.Auth.TfaComplete>(
 | 
			
		||||
		'/api/auth/2fa/complete',
 | 
			
		||||
    "/api/auth/2fa/complete",
 | 
			
		||||
    {
 | 
			
		||||
      mail,
 | 
			
		||||
			code
 | 
			
		||||
      code,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -90,4 +90,4 @@ export const tfa_complete = (
 | 
			
		||||
export const tfa_disable = (
 | 
			
		||||
  token: string
 | 
			
		||||
): 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 { Requests, Responses, UserRole } from '../../../dto';
 | 
			
		||||
import axios from "axios";
 | 
			
		||||
import { Requests, Responses, UserRole } from "../dto";
 | 
			
		||||
export { Requests, Responses, UserRole };
 | 
			
		||||
 | 
			
		||||
export const post = <T extends Requests.BaseRequest>(url: string, data: T) =>
 | 
			
		||||
  axios
 | 
			
		||||
    .post(url, data, {
 | 
			
		||||
			headers: { 'Content-type': 'application/json' }
 | 
			
		||||
      headers: { "Content-type": "application/json" },
 | 
			
		||||
    })
 | 
			
		||||
    .then((res) => res.data)
 | 
			
		||||
    .catch((err) => err.response.data);
 | 
			
		||||
@@ -18,9 +18,9 @@ export const post_token = <T extends Requests.BaseRequest>(
 | 
			
		||||
  axios
 | 
			
		||||
    .post(url, data, {
 | 
			
		||||
      headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'application/json'
 | 
			
		||||
			}
 | 
			
		||||
        Authorization: "Bearer " + token,
 | 
			
		||||
        "Content-type": "application/json",
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    .then((res) => res.data)
 | 
			
		||||
    .catch((err) => err.response.data);
 | 
			
		||||
@@ -34,10 +34,10 @@ export const post_token_form = (
 | 
			
		||||
  axios
 | 
			
		||||
    .post(url, data, {
 | 
			
		||||
      headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'multipart/form-data'
 | 
			
		||||
        Authorization: "Bearer " + token,
 | 
			
		||||
        "Content-type": "multipart/form-data",
 | 
			
		||||
      },
 | 
			
		||||
			onUploadProgress: onProgress
 | 
			
		||||
      onUploadProgress: onProgress,
 | 
			
		||||
    })
 | 
			
		||||
    .then((res) => res.data)
 | 
			
		||||
    .catch((err) => err.response.data);
 | 
			
		||||
@@ -52,7 +52,7 @@ export const get = (url: string) =>
 | 
			
		||||
export const get_token = (url: string, token: string) =>
 | 
			
		||||
  axios
 | 
			
		||||
    .get(url, {
 | 
			
		||||
			headers: { Authorization: 'Bearer ' + token }
 | 
			
		||||
      headers: { Authorization: "Bearer " + token },
 | 
			
		||||
    })
 | 
			
		||||
    .then((res) => res.data)
 | 
			
		||||
    .catch((err) => err.response.data);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,13 @@ import {
 | 
			
		||||
  get_token,
 | 
			
		||||
  post_token,
 | 
			
		||||
  post_token_form,
 | 
			
		||||
	isErrorResponse
 | 
			
		||||
} from './base';
 | 
			
		||||
  isErrorResponse,
 | 
			
		||||
} from "./base";
 | 
			
		||||
 | 
			
		||||
export const get_root = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.FS.GetRootResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	get_token('/api/fs/root', token);
 | 
			
		||||
  get_token("/api/fs/root", token);
 | 
			
		||||
 | 
			
		||||
export const get_node = (
 | 
			
		||||
  token: string,
 | 
			
		||||
@@ -30,10 +30,10 @@ export const create_folder = (
 | 
			
		||||
  name: string
 | 
			
		||||
): Promise<Responses.FS.CreateFolderResponse | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.FS.CreateFolderRequest>(
 | 
			
		||||
		'/api/fs/createFolder',
 | 
			
		||||
    "/api/fs/createFolder",
 | 
			
		||||
    {
 | 
			
		||||
      parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
      name: name,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -44,10 +44,10 @@ export const create_file = (
 | 
			
		||||
  name: string
 | 
			
		||||
): Promise<Responses.FS.CreateFileResponse | Responses.ErrorResponse> =>
 | 
			
		||||
  post_token<Requests.FS.CreateFileRequest>(
 | 
			
		||||
		'/api/fs/createFile',
 | 
			
		||||
    "/api/fs/createFile",
 | 
			
		||||
    {
 | 
			
		||||
      parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
      name: name,
 | 
			
		||||
    },
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
@@ -56,13 +56,7 @@ export const delete_node = (
 | 
			
		||||
  token: string,
 | 
			
		||||
  node: number
 | 
			
		||||
): Promise<Responses.FS.DeleteResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token<Requests.FS.DeleteRequest>(
 | 
			
		||||
		'/api/fs/delete',
 | 
			
		||||
		{
 | 
			
		||||
			node: node
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
  post_token(`/api/fs/delete/${node}`, {}, token);
 | 
			
		||||
 | 
			
		||||
export const upload_file = async (
 | 
			
		||||
  token: string,
 | 
			
		||||
@@ -74,20 +68,15 @@ export const upload_file = async (
 | 
			
		||||
  if (isErrorResponse(node)) return node;
 | 
			
		||||
 | 
			
		||||
  const form = new FormData();
 | 
			
		||||
	form.set('file', file);
 | 
			
		||||
	return post_token_form(
 | 
			
		||||
		`/api/fs/upload/${node.id}`,
 | 
			
		||||
		form,
 | 
			
		||||
		token,
 | 
			
		||||
		onProgress
 | 
			
		||||
	);
 | 
			
		||||
  form.set("file", file);
 | 
			
		||||
  return post_token_form(`/api/fs/upload/${node.id}`, form, token, onProgress);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function download_file(token: string, id: number) {
 | 
			
		||||
	const form = document.createElement('form');
 | 
			
		||||
	form.method = 'post';
 | 
			
		||||
	form.target = '_blank';
 | 
			
		||||
	form.action = '/api/fs/download';
 | 
			
		||||
  const form = document.createElement("form");
 | 
			
		||||
  form.method = "post";
 | 
			
		||||
  form.target = "_blank";
 | 
			
		||||
  form.action = "/api/fs/download";
 | 
			
		||||
  form.innerHTML = `<input type="hidden" name="jwtToken" value="${token}"><input type="hidden" name="id" value="${id}">`;
 | 
			
		||||
  document.body.appendChild(form);
 | 
			
		||||
  form.submit();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
export { Requests, Responses, UserRole, isErrorResponse } from './base';
 | 
			
		||||
export * as Auth from './auth';
 | 
			
		||||
export * as FS from './fs';
 | 
			
		||||
export * as User from './user';
 | 
			
		||||
export * as Admin from './admin';
 | 
			
		||||
export * from './util';
 | 
			
		||||
export { Requests, Responses, UserRole, isErrorResponse } from "./base";
 | 
			
		||||
export * as Auth from "./auth";
 | 
			
		||||
export * as FS from "./fs";
 | 
			
		||||
export * as User from "./user";
 | 
			
		||||
export * as Admin from "./admin";
 | 
			
		||||
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 = (
 | 
			
		||||
  token: string
 | 
			
		||||
): Promise<Responses.User.UserInfoResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	get_token('/api/user/info', token);
 | 
			
		||||
  get_token("/api/user/info", token);
 | 
			
		||||
 | 
			
		||||
export const delete_user = (
 | 
			
		||||
  token: string
 | 
			
		||||
): 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 { Ref, UnwrapRef } from 'vue';
 | 
			
		||||
import { isErrorResponse } from './base';
 | 
			
		||||
import { refresh_token } from './auth';
 | 
			
		||||
import jwtDecode, { JwtPayload } from "jwt-decode";
 | 
			
		||||
import { Ref, UnwrapRef } from "vue";
 | 
			
		||||
import { isErrorResponse } from "./base";
 | 
			
		||||
import { refresh_token } from "./auth";
 | 
			
		||||
 | 
			
		||||
export async function check_token(
 | 
			
		||||
  token: TokenInjectType
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,21 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineEmits, defineProps, inject } from 'vue';
 | 
			
		||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
			
		||||
import { defineEmits, defineProps, inject } from "vue";
 | 
			
		||||
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<{
 | 
			
		||||
  node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'reloadNode'): void;
 | 
			
		||||
  (e: "reloadNode"): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
async function del() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
  await FS.delete_node(token, props.node.id);
 | 
			
		||||
	emit('reloadNode');
 | 
			
		||||
  emit("reloadNode");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function download() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineEmits, defineProps, inject, reactive, ref, watch } from 'vue';
 | 
			
		||||
import { FS, Responses, check_token, TokenInjectType } from '@/api';
 | 
			
		||||
import DirEntry from '@/components/FSView/DirEntry.vue';
 | 
			
		||||
import UploadFileDialog from '@/components/UploadDialog/UploadFileDialog.vue';
 | 
			
		||||
import { NModal } from 'naive-ui';
 | 
			
		||||
import { defineEmits, defineProps, inject, reactive, ref, watch } from "vue";
 | 
			
		||||
import { FS, Responses, check_token, TokenInjectType } from "@/api";
 | 
			
		||||
import DirEntry from "@/components/FSView/DirEntry.vue";
 | 
			
		||||
import UploadFileDialog from "@/components/UploadDialog/UploadFileDialog.vue";
 | 
			
		||||
import { NModal } from "naive-ui";
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'reloadNode'): void;
 | 
			
		||||
	(e: 'gotoRoot'): void;
 | 
			
		||||
  (e: "reloadNode"): void;
 | 
			
		||||
  (e: "gotoRoot"): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const fileInput = ref<HTMLInputElement>();
 | 
			
		||||
const uploadDialog = ref();
 | 
			
		||||
const uploadDialogShow = ref(false);
 | 
			
		||||
 | 
			
		||||
const new_folder_name = ref('');
 | 
			
		||||
const new_folder_name = ref("");
 | 
			
		||||
const files = ref<File[]>([]);
 | 
			
		||||
const nodes = ref<Responses.FS.GetNodeResponse[]>([]);
 | 
			
		||||
const hasParent = ref(false);
 | 
			
		||||
@@ -29,7 +29,7 @@ const parentNode = reactive<Responses.FS.GetNodeResponse>({
 | 
			
		||||
  statusCode: 200,
 | 
			
		||||
  isFile: false,
 | 
			
		||||
  parent: null,
 | 
			
		||||
	name: '..'
 | 
			
		||||
  name: "..",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
@@ -43,10 +43,7 @@ watch(
 | 
			
		||||
    await Promise.all(
 | 
			
		||||
      to.children?.map(async (child) => {
 | 
			
		||||
        nodes.value.push(
 | 
			
		||||
					(await FS.get_node(
 | 
			
		||||
						token,
 | 
			
		||||
						child
 | 
			
		||||
					)) as Responses.FS.GetNodeResponse
 | 
			
		||||
          (await FS.get_node(token, child)) as Responses.FS.GetNodeResponse
 | 
			
		||||
        );
 | 
			
		||||
      }) ?? []
 | 
			
		||||
    );
 | 
			
		||||
@@ -58,7 +55,7 @@ async function newFolder() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
  await FS.create_folder(token, props.node.id, new_folder_name.value);
 | 
			
		||||
	emit('reloadNode');
 | 
			
		||||
  emit("reloadNode");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function uploadFiles() {
 | 
			
		||||
@@ -69,18 +66,14 @@ async function uploadFiles() {
 | 
			
		||||
async function uploadFilesDialogOpen() {
 | 
			
		||||
  await uploadDialog.value?.startUpload(props.node.id);
 | 
			
		||||
  uploadDialogShow.value = false;
 | 
			
		||||
	if (fileInput.value) fileInput.value.value = '';
 | 
			
		||||
	emit('reloadNode');
 | 
			
		||||
  if (fileInput.value) fileInput.value.value = "";
 | 
			
		||||
  emit("reloadNode");
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
		<input
 | 
			
		||||
			type="text"
 | 
			
		||||
			placeholder="Folder name"
 | 
			
		||||
			v-model="new_folder_name"
 | 
			
		||||
		/>
 | 
			
		||||
    <input type="text" placeholder="Folder name" v-model="new_folder_name" />
 | 
			
		||||
    <a href="#" @click="newFolder()">create folder</a>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineProps, inject } from 'vue';
 | 
			
		||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
			
		||||
import { defineProps, inject } from "vue";
 | 
			
		||||
import { check_token, FS, Responses, TokenInjectType } from "@/api";
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
async function del() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,7 @@
 | 
			
		||||
  <div class="hello">
 | 
			
		||||
    <h1>{{ msg }}</h1>
 | 
			
		||||
    <p>
 | 
			
		||||
			For a guide and recipes on how to configure / customize this
 | 
			
		||||
			project,<br />
 | 
			
		||||
      For a guide and recipes on how to configure / customize this project,<br />
 | 
			
		||||
      check out the
 | 
			
		||||
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
        >vue-cli documentation</a
 | 
			
		||||
@@ -55,9 +54,7 @@
 | 
			
		||||
    <h3>Essential Links</h3>
 | 
			
		||||
    <ul>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a href="https://vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
					>Core Docs</a
 | 
			
		||||
				>
 | 
			
		||||
        <a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        <a href="https://forum.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
@@ -70,33 +67,23 @@
 | 
			
		||||
        >
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a
 | 
			
		||||
					href="https://twitter.com/vuejs"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
					rel="noopener"
 | 
			
		||||
        <a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
 | 
			
		||||
          >Twitter</a
 | 
			
		||||
        >
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a href="https://news.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
					>News</a
 | 
			
		||||
				>
 | 
			
		||||
        <a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
 | 
			
		||||
      </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <h3>Ecosystem</h3>
 | 
			
		||||
    <ul>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a
 | 
			
		||||
					href="https://router.vuejs.org"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
					rel="noopener"
 | 
			
		||||
        <a href="https://router.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
          >vue-router</a
 | 
			
		||||
        >
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a href="https://vuex.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
					>vuex</a
 | 
			
		||||
				>
 | 
			
		||||
        <a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        <a
 | 
			
		||||
@@ -107,10 +94,7 @@
 | 
			
		||||
        >
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
				<a
 | 
			
		||||
					href="https://vue-loader.vuejs.org"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
					rel="noopener"
 | 
			
		||||
        <a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
 | 
			
		||||
          >vue-loader</a
 | 
			
		||||
        >
 | 
			
		||||
      </li>
 | 
			
		||||
@@ -127,13 +111,13 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
import { defineComponent } from "vue";
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	name: 'HelloWorld',
 | 
			
		||||
  name: "HelloWorld",
 | 
			
		||||
  props: {
 | 
			
		||||
		msg: String
 | 
			
		||||
	}
 | 
			
		||||
    msg: String,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineProps, defineExpose, ref } from 'vue';
 | 
			
		||||
import { isErrorResponse, FS } from '@/api';
 | 
			
		||||
import { NProgress } from 'naive-ui';
 | 
			
		||||
import filesize from 'filesize';
 | 
			
		||||
import { defineProps, defineExpose, ref } from "vue";
 | 
			
		||||
import { isErrorResponse, FS } from "@/api";
 | 
			
		||||
import { NProgress } from "naive-ui";
 | 
			
		||||
import filesize from "filesize";
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  file: File;
 | 
			
		||||
@@ -10,8 +10,8 @@ const props = defineProps<{
 | 
			
		||||
 | 
			
		||||
const progress = ref(0);
 | 
			
		||||
const percentage = ref(0);
 | 
			
		||||
const err = ref('');
 | 
			
		||||
const status = ref('info');
 | 
			
		||||
const err = ref("");
 | 
			
		||||
const status = ref("info");
 | 
			
		||||
 | 
			
		||||
async function startUpload(parent: number, token: string) {
 | 
			
		||||
  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;
 | 
			
		||||
  if (isErrorResponse(resp)) {
 | 
			
		||||
		err.value = resp.message ?? 'Error';
 | 
			
		||||
		status.value = 'error';
 | 
			
		||||
	} else status.value = 'success';
 | 
			
		||||
    err.value = resp.message ?? "Error";
 | 
			
		||||
    status.value = "error";
 | 
			
		||||
  } else status.value = "success";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
	startUpload
 | 
			
		||||
  startUpload,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineProps, defineExpose, ref, inject } from 'vue';
 | 
			
		||||
import { check_token, TokenInjectType } from '@/api';
 | 
			
		||||
import UploadEntry from '@/components/UploadDialog/UploadEntry.vue';
 | 
			
		||||
import { NCard } from 'naive-ui';
 | 
			
		||||
import { defineProps, defineExpose, ref, inject } from "vue";
 | 
			
		||||
import { check_token, TokenInjectType } from "@/api";
 | 
			
		||||
import UploadEntry from "@/components/UploadDialog/UploadEntry.vue";
 | 
			
		||||
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 done = ref(false);
 | 
			
		||||
@@ -22,7 +22,7 @@ async function startUpload(parent: number) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
	startUpload
 | 
			
		||||
  startUpload,
 | 
			
		||||
});
 | 
			
		||||
defineProps<{
 | 
			
		||||
  files: File[];
 | 
			
		||||
@@ -32,12 +32,7 @@ defineProps<{
 | 
			
		||||
<template>
 | 
			
		||||
  <n-card title="Upload Files">
 | 
			
		||||
    <div>
 | 
			
		||||
			<UploadEntry
 | 
			
		||||
				v-for="f in files"
 | 
			
		||||
				:key="f.name"
 | 
			
		||||
				ref="entries"
 | 
			
		||||
				:file="f"
 | 
			
		||||
			/>
 | 
			
		||||
      <UploadEntry v-for="f in files" :key="f.name" ref="entries" :file="f" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
      <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 { IsBase32, IsJWT, IsNotEmpty } from 'class-validator';
 | 
			
		||||
import { ValidateConstructor } from '../utils';
 | 
			
		||||
import { SuccessResponse } from "./base";
 | 
			
		||||
import { IsBase32, IsJWT, IsNotEmpty } from "class-validator";
 | 
			
		||||
import { ValidateConstructor } from "../utils";
 | 
			
		||||
 | 
			
		||||
@ValidateConstructor
 | 
			
		||||
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 router from './router';
 | 
			
		||||
import AppAsyncWrapper from './AppAsyncWrapper.vue';
 | 
			
		||||
import { createApp } from "vue";
 | 
			
		||||
import router from "./router";
 | 
			
		||||
import AppAsyncWrapper from "./AppAsyncWrapper.vue";
 | 
			
		||||
 | 
			
		||||
const app = createApp(AppAsyncWrapper);
 | 
			
		||||
app.use(router);
 | 
			
		||||
app.config.unwrapInjectedRef = true;
 | 
			
		||||
app.mount('#app');
 | 
			
		||||
app.mount("#app");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,63 +1,63 @@
 | 
			
		||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
 | 
			
		||||
import LoginView from '@/views/LoginView.vue';
 | 
			
		||||
import SignupView from '@/views/SignupView.vue';
 | 
			
		||||
import HomeView from '@/views/HomeView.vue';
 | 
			
		||||
import AboutView from '@/views/AboutView.vue';
 | 
			
		||||
import FSView from '@/views/FSView.vue';
 | 
			
		||||
import SetTokenView from '@/views/SetTokenView.vue';
 | 
			
		||||
import ProfileView from '@/views/ProfileView.vue';
 | 
			
		||||
import TFAView from '@/views/TFAView.vue';
 | 
			
		||||
import AdminView from '@/views/AdminView.vue';
 | 
			
		||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
 | 
			
		||||
import LoginView from "@/views/LoginView.vue";
 | 
			
		||||
import SignupView from "@/views/SignupView.vue";
 | 
			
		||||
import HomeView from "@/views/HomeView.vue";
 | 
			
		||||
import AboutView from "@/views/AboutView.vue";
 | 
			
		||||
import FSView from "@/views/FSView.vue";
 | 
			
		||||
import SetTokenView from "@/views/SetTokenView.vue";
 | 
			
		||||
import ProfileView from "@/views/ProfileView.vue";
 | 
			
		||||
import TFAView from "@/views/TFAView.vue";
 | 
			
		||||
import AdminView from "@/views/AdminView.vue";
 | 
			
		||||
 | 
			
		||||
const routes: Array<RouteRecordRaw> = [
 | 
			
		||||
  {
 | 
			
		||||
		path: '/',
 | 
			
		||||
		name: 'home',
 | 
			
		||||
		component: HomeView
 | 
			
		||||
    path: "/",
 | 
			
		||||
    name: "home",
 | 
			
		||||
    component: HomeView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/profile',
 | 
			
		||||
		name: 'profile',
 | 
			
		||||
		component: ProfileView
 | 
			
		||||
    path: "/profile",
 | 
			
		||||
    name: "profile",
 | 
			
		||||
    component: ProfileView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/profile/2fa-enable',
 | 
			
		||||
		name: '2fa',
 | 
			
		||||
		component: TFAView
 | 
			
		||||
    path: "/profile/2fa-enable",
 | 
			
		||||
    name: "2fa",
 | 
			
		||||
    component: TFAView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/admin',
 | 
			
		||||
		component: AdminView
 | 
			
		||||
    path: "/admin",
 | 
			
		||||
    component: AdminView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/about',
 | 
			
		||||
		component: AboutView
 | 
			
		||||
    path: "/about",
 | 
			
		||||
    component: AboutView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/login',
 | 
			
		||||
		name: 'login',
 | 
			
		||||
		component: LoginView
 | 
			
		||||
    path: "/login",
 | 
			
		||||
    name: "login",
 | 
			
		||||
    component: LoginView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/signup',
 | 
			
		||||
		name: 'signup',
 | 
			
		||||
		component: SignupView
 | 
			
		||||
    path: "/signup",
 | 
			
		||||
    name: "signup",
 | 
			
		||||
    component: SignupView,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
		path: '/fs/:node_id',
 | 
			
		||||
		name: 'fs',
 | 
			
		||||
		component: FSView
 | 
			
		||||
    path: "/fs/:node_id",
 | 
			
		||||
    name: "fs",
 | 
			
		||||
    component: FSView,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
		path: '/set_token',
 | 
			
		||||
		component: SetTokenView
 | 
			
		||||
	}
 | 
			
		||||
    path: "/set_token",
 | 
			
		||||
    component: SetTokenView,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const router = createRouter({
 | 
			
		||||
  history: createWebHistory(process.env.BASE_URL),
 | 
			
		||||
	routes
 | 
			
		||||
  routes,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default router;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { inject, onBeforeMount, ref } from 'vue';
 | 
			
		||||
import { inject, onBeforeMount, ref } from "vue";
 | 
			
		||||
import {
 | 
			
		||||
  Responses,
 | 
			
		||||
  check_token,
 | 
			
		||||
  TokenInjectType,
 | 
			
		||||
  Admin,
 | 
			
		||||
	isErrorResponse
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import { onBeforeRouteUpdate } from 'vue-router';
 | 
			
		||||
import router from '@/router';
 | 
			
		||||
  isErrorResponse,
 | 
			
		||||
} from "@/api";
 | 
			
		||||
import { onBeforeRouteUpdate } from "vue-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[]>([]);
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +25,7 @@ async function updatePanel() {
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,7 @@ async function deleteUser(user: number) {
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr v-for="user in users" :key="user.id">
 | 
			
		||||
      <td>{{ user.name }}</td>
 | 
			
		||||
			<td>{{ user.gitlab ? 'Gitlab' : 'Password' }}</td>
 | 
			
		||||
      <td>{{ user.gitlab ? "Gitlab" : "Password" }}</td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <select @change="setRole(user.id, $event.target.value)">
 | 
			
		||||
          <option value="0" :selected="user.role === 0 ? true : null">
 | 
			
		||||
@@ -93,7 +93,7 @@ async function deleteUser(user: number) {
 | 
			
		||||
      </td>
 | 
			
		||||
      <td v-if="user.gitlab"></td>
 | 
			
		||||
      <td v-else>
 | 
			
		||||
				{{ user.tfaEnabled ? 'Enabled' : 'Disabled' }}
 | 
			
		||||
        {{ user.tfaEnabled ? "Enabled" : "Disabled" }}
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <button v-if="user.tfaEnabled" @click="disableTfa(user.id)">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,21 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 | 
			
		||||
import { inject, onBeforeMount, ref } from 'vue';
 | 
			
		||||
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
 | 
			
		||||
import { inject, onBeforeMount, ref } from "vue";
 | 
			
		||||
import {
 | 
			
		||||
  check_token,
 | 
			
		||||
  FS,
 | 
			
		||||
  Responses,
 | 
			
		||||
  isErrorResponse,
 | 
			
		||||
	TokenInjectType
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import DirViewer from '@/components/FSView/DirViewer.vue';
 | 
			
		||||
import FileViewer from '@/components/FSView/FileViewer.vue';
 | 
			
		||||
  TokenInjectType,
 | 
			
		||||
} from "@/api";
 | 
			
		||||
import DirViewer from "@/components/FSView/DirViewer.vue";
 | 
			
		||||
import FileViewer from "@/components/FSView/FileViewer.vue";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
async function fetch_node(node_id: number) {
 | 
			
		||||
@@ -23,7 +23,7 @@ async function fetch_node(node_id: number) {
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
  let [p, n] = [
 | 
			
		||||
    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(n)) return gotoRoot();
 | 
			
		||||
@@ -48,8 +48,8 @@ async function gotoRoot() {
 | 
			
		||||
  if (isErrorResponse(rootRes)) return jwt.logout();
 | 
			
		||||
  const root = rootRes.rootId;
 | 
			
		||||
  await router.replace({
 | 
			
		||||
		name: 'fs',
 | 
			
		||||
		params: { node_id: root }
 | 
			
		||||
    name: "fs",
 | 
			
		||||
    params: { node_id: root },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<template><p></p></template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { onBeforeRouteUpdate, useRouter } from 'vue-router';
 | 
			
		||||
import { inject, onBeforeMount } from 'vue';
 | 
			
		||||
import { FS, check_token, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { onBeforeRouteUpdate, useRouter } from "vue-router";
 | 
			
		||||
import { inject, onBeforeMount } from "vue";
 | 
			
		||||
import { FS, check_token, isErrorResponse, TokenInjectType } from "@/api";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
async function start_redirect() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
@@ -14,8 +14,8 @@ async function start_redirect() {
 | 
			
		||||
  const root = await FS.get_root(token);
 | 
			
		||||
  if (isErrorResponse(root)) return jwt.logout();
 | 
			
		||||
  await router.replace({
 | 
			
		||||
		name: 'fs',
 | 
			
		||||
		params: { node_id: root.rootId }
 | 
			
		||||
    name: "fs",
 | 
			
		||||
    params: { node_id: root.rootId },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +1,43 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, inject } from 'vue';
 | 
			
		||||
import { Auth, FS, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { ref, inject } from "vue";
 | 
			
		||||
import { Auth, FS, isErrorResponse, TokenInjectType } from "@/api";
 | 
			
		||||
import { useRouter } from "vue-router";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 | 
			
		||||
const username = ref('');
 | 
			
		||||
const password = ref('');
 | 
			
		||||
const otp = ref('');
 | 
			
		||||
const username = ref("");
 | 
			
		||||
const password = ref("");
 | 
			
		||||
const otp = ref("");
 | 
			
		||||
 | 
			
		||||
const error = ref('');
 | 
			
		||||
const error = ref("");
 | 
			
		||||
 | 
			
		||||
const requestOtp = ref(false);
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
async function login() {
 | 
			
		||||
	error.value = '';
 | 
			
		||||
	if (username.value === '' || password.value === '') {
 | 
			
		||||
		error.value = 'Email and/or Password missing';
 | 
			
		||||
  error.value = "";
 | 
			
		||||
  if (username.value === "" || password.value === "") {
 | 
			
		||||
    error.value = "Email and/or Password missing";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  const res = await (requestOtp.value
 | 
			
		||||
    ? Auth.auth_login(username.value, password.value, otp.value)
 | 
			
		||||
    : Auth.auth_login(username.value, password.value));
 | 
			
		||||
	if (isErrorResponse(res)) error.value = 'Login failed: ' + res.message;
 | 
			
		||||
	else if ('jwt' in res) {
 | 
			
		||||
  if (isErrorResponse(res)) error.value = "Login failed: " + res.message;
 | 
			
		||||
  else if ("jwt" in res) {
 | 
			
		||||
    const root = await FS.get_root(res.jwt);
 | 
			
		||||
    if (isErrorResponse(root)) {
 | 
			
		||||
			error.value = 'Get root failed: ' + root.message;
 | 
			
		||||
      error.value = "Get root failed: " + root.message;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    jwt.setToken(res.jwt);
 | 
			
		||||
    await router.push({
 | 
			
		||||
			name: 'fs',
 | 
			
		||||
			params: { node_id: root.rootId }
 | 
			
		||||
      name: "fs",
 | 
			
		||||
      params: { node_id: root.rootId },
 | 
			
		||||
    });
 | 
			
		||||
  } else {
 | 
			
		||||
		error.value = '';
 | 
			
		||||
    error.value = "";
 | 
			
		||||
    requestOtp.value = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,22 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, inject, onBeforeMount } from 'vue';
 | 
			
		||||
import { ref, inject, onBeforeMount } from "vue";
 | 
			
		||||
import {
 | 
			
		||||
  Auth,
 | 
			
		||||
  User,
 | 
			
		||||
  check_token,
 | 
			
		||||
  isErrorResponse,
 | 
			
		||||
  TokenInjectType,
 | 
			
		||||
	Responses
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import { onBeforeRouteUpdate } from 'vue-router';
 | 
			
		||||
  Responses,
 | 
			
		||||
} from "@/api";
 | 
			
		||||
import { onBeforeRouteUpdate } from "vue-router";
 | 
			
		||||
 | 
			
		||||
const error = ref('');
 | 
			
		||||
const oldPw = ref('');
 | 
			
		||||
const newPw = ref('');
 | 
			
		||||
const newPw2 = ref('');
 | 
			
		||||
const error = ref("");
 | 
			
		||||
const oldPw = ref("");
 | 
			
		||||
const newPw = ref("");
 | 
			
		||||
const newPw2 = ref("");
 | 
			
		||||
const user = ref<Responses.User.UserInfoResponse | null>(null);
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
onBeforeRouteUpdate(async () => {
 | 
			
		||||
  await updateProfile();
 | 
			
		||||
@@ -48,8 +48,8 @@ async function logoutAll() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function changePw() {
 | 
			
		||||
	if (oldPw.value === '' || newPw.value === '' || newPw2.value === '') {
 | 
			
		||||
		error.value = 'Password missing';
 | 
			
		||||
  if (oldPw.value === "" || newPw.value === "" || newPw2.value === "") {
 | 
			
		||||
    error.value = "Password missing";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (newPw.value !== newPw2.value) {
 | 
			
		||||
@@ -60,7 +60,7 @@ async function changePw() {
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
  const res = await Auth.change_password(oldPw.value, newPw.value, token);
 | 
			
		||||
  if (isErrorResponse(res))
 | 
			
		||||
		error.value = 'Password change failed: ' + res.message;
 | 
			
		||||
    error.value = "Password change failed: " + res.message;
 | 
			
		||||
  else jwt.logout();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -76,19 +76,11 @@ async function tfaDisable() {
 | 
			
		||||
  <template v-if="user">
 | 
			
		||||
    <div v-if="error !== ''" v-text="error"></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">
 | 
			
		||||
      <div>
 | 
			
		||||
				<input
 | 
			
		||||
					type="password"
 | 
			
		||||
					placeholder="Old password"
 | 
			
		||||
					v-model="oldPw"
 | 
			
		||||
				/>
 | 
			
		||||
				<input
 | 
			
		||||
					type="password"
 | 
			
		||||
					placeholder="New password"
 | 
			
		||||
					v-model="newPw"
 | 
			
		||||
				/>
 | 
			
		||||
        <input type="password" placeholder="Old password" v-model="oldPw" />
 | 
			
		||||
        <input type="password" placeholder="New password" v-model="newPw" />
 | 
			
		||||
        <input
 | 
			
		||||
          type="password"
 | 
			
		||||
          placeholder="Repeat new password"
 | 
			
		||||
@@ -99,15 +91,11 @@ async function tfaDisable() {
 | 
			
		||||
      <div>
 | 
			
		||||
        <div>
 | 
			
		||||
          2 Factor authentication:
 | 
			
		||||
					{{ user.tfaEnabled ? 'Enabled' : 'Disabled' }}
 | 
			
		||||
          {{ user.tfaEnabled ? "Enabled" : "Disabled" }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
					<a href="#" v-if="user.tfaEnabled" @click="tfaDisable">
 | 
			
		||||
						Disable
 | 
			
		||||
					</a>
 | 
			
		||||
					<router-link to="/profile/2fa-enable" v-else>
 | 
			
		||||
						Enable
 | 
			
		||||
					</router-link>
 | 
			
		||||
          <a href="#" v-if="user.tfaEnabled" @click="tfaDisable"> Disable </a>
 | 
			
		||||
          <router-link to="/profile/2fa-enable" v-else> Enable </router-link>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { inject } from 'vue';
 | 
			
		||||
import { TokenInjectType } from '@/api';
 | 
			
		||||
import { useRoute, useRouter } from 'vue-router';
 | 
			
		||||
import { inject } from "vue";
 | 
			
		||||
import { TokenInjectType } from "@/api";
 | 
			
		||||
import { useRoute, useRouter } from "vue-router";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
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);
 | 
			
		||||
router.replace({ path: '/' });
 | 
			
		||||
if ("token" in route.query) jwt.setToken(route.query["token"] as string);
 | 
			
		||||
router.replace({ path: "/" });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { Auth, isErrorResponse } from '@/api';
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import { Auth, isErrorResponse } from "@/api";
 | 
			
		||||
 | 
			
		||||
const username = ref('');
 | 
			
		||||
const password = ref('');
 | 
			
		||||
const password2 = ref('');
 | 
			
		||||
const error = ref('');
 | 
			
		||||
const username = ref("");
 | 
			
		||||
const password = ref("");
 | 
			
		||||
const password2 = ref("");
 | 
			
		||||
const error = ref("");
 | 
			
		||||
 | 
			
		||||
async function signup() {
 | 
			
		||||
	if (username.value === '' || password.value === '') {
 | 
			
		||||
		error.value = 'Email and/or Password missing';
 | 
			
		||||
  if (username.value === "" || password.value === "") {
 | 
			
		||||
    error.value = "Email and/or Password missing";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (password.value !== password2.value) {
 | 
			
		||||
@@ -18,8 +18,8 @@ async function signup() {
 | 
			
		||||
  }
 | 
			
		||||
  const res = await Auth.auth_signup(username.value, password.value);
 | 
			
		||||
  error.value = isErrorResponse(res)
 | 
			
		||||
		? 'Signup failed: ' + res.message
 | 
			
		||||
		: 'Signup successful, please wait till an admin unlocks your account.';
 | 
			
		||||
    ? "Signup failed: " + res.message
 | 
			
		||||
    : "Signup successful, please wait till an admin unlocks your account.";
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,31 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, inject } from 'vue';
 | 
			
		||||
import { Auth, check_token, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { ref, inject } from "vue";
 | 
			
		||||
import { Auth, check_token, isErrorResponse, TokenInjectType } from "@/api";
 | 
			
		||||
 | 
			
		||||
enum state {
 | 
			
		||||
  SELECT,
 | 
			
		||||
  MAIL,
 | 
			
		||||
	TOTP
 | 
			
		||||
  TOTP,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const currentState = ref<state>(state.SELECT);
 | 
			
		||||
 | 
			
		||||
const error = ref('');
 | 
			
		||||
const qrImage = ref('');
 | 
			
		||||
const secret = ref('');
 | 
			
		||||
const code = ref('');
 | 
			
		||||
const error = ref("");
 | 
			
		||||
const qrImage = ref("");
 | 
			
		||||
const secret = ref("");
 | 
			
		||||
const code = ref("");
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const jwt = inject<TokenInjectType>("jwt") as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
async function selectMail() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
	error.value = 'Working...';
 | 
			
		||||
  error.value = "Working...";
 | 
			
		||||
  const res = await Auth.tfa_setup(true, token);
 | 
			
		||||
  if (isErrorResponse(res))
 | 
			
		||||
		error.value = 'Failed to select 2fa type: ' + res.message;
 | 
			
		||||
    error.value = "Failed to select 2fa type: " + res.message;
 | 
			
		||||
  else {
 | 
			
		||||
		error.value = '';
 | 
			
		||||
    error.value = "";
 | 
			
		||||
    currentState.value = state.MAIL;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -33,14 +33,14 @@ async function selectMail() {
 | 
			
		||||
async function selectTotp() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
	error.value = 'Working...';
 | 
			
		||||
  error.value = "Working...";
 | 
			
		||||
  const res = await Auth.tfa_setup(false, token);
 | 
			
		||||
  if (isErrorResponse(res))
 | 
			
		||||
		error.value = 'Failed to select 2fa type: ' + res.message;
 | 
			
		||||
    error.value = "Failed to select 2fa type: " + res.message;
 | 
			
		||||
  else {
 | 
			
		||||
    qrImage.value = res.qrCode;
 | 
			
		||||
    secret.value = res.secret;
 | 
			
		||||
		error.value = '';
 | 
			
		||||
    error.value = "";
 | 
			
		||||
    currentState.value = state.TOTP;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -48,14 +48,14 @@ async function selectTotp() {
 | 
			
		||||
async function submit() {
 | 
			
		||||
  const token = await check_token(jwt);
 | 
			
		||||
  if (!token) return;
 | 
			
		||||
	error.value = 'Working...';
 | 
			
		||||
  error.value = "Working...";
 | 
			
		||||
  const res = await Auth.tfa_complete(
 | 
			
		||||
    currentState.value === state.MAIL,
 | 
			
		||||
    code.value,
 | 
			
		||||
    token
 | 
			
		||||
  );
 | 
			
		||||
  if (isErrorResponse(res))
 | 
			
		||||
		error.value = 'Failed to submit code: ' + res.message;
 | 
			
		||||
    error.value = "Failed to submit code: " + res.message;
 | 
			
		||||
  else jwt.logout();
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
const { defineConfig } = require('@vue/cli-service');
 | 
			
		||||
const { defineConfig } = require("@vue/cli-service");
 | 
			
		||||
module.exports = defineConfig({
 | 
			
		||||
  transpileDependencies: true,
 | 
			
		||||
  configureWebpack: {
 | 
			
		||||
    resolve: {
 | 
			
		||||
      fallback: {
 | 
			
		||||
        crypto: false,
 | 
			
		||||
				stream: require.resolve('stream-browserify')
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
        stream: require.resolve("stream-browserify"),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								old_backend/.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								old_backend/.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
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' }
 | 
			
		||||
		]
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										401
									
								
								old_backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								old_backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,401 @@
 | 
			
		||||
# Created by .ignore support plugin (hsz.mobi)
 | 
			
		||||
### JetBrains template
 | 
			
		||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
 | 
			
		||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 | 
			
		||||
 | 
			
		||||
# User-specific stuff:
 | 
			
		||||
.idea/**/workspace.xml
 | 
			
		||||
.idea/**/tasks.xml
 | 
			
		||||
.idea/dictionaries
 | 
			
		||||
 | 
			
		||||
# Sensitive or high-churn files:
 | 
			
		||||
.idea/**/dataSources/
 | 
			
		||||
.idea/**/dataSources.ids
 | 
			
		||||
.idea/**/dataSources.xml
 | 
			
		||||
.idea/**/dataSources.local.xml
 | 
			
		||||
.idea/**/sqlDataSources.xml
 | 
			
		||||
.idea/**/dynamic.xml
 | 
			
		||||
.idea/**/uiDesigner.xml
 | 
			
		||||
 | 
			
		||||
# Gradle:
 | 
			
		||||
.idea/**/gradle.xml
 | 
			
		||||
.idea/**/libraries
 | 
			
		||||
 | 
			
		||||
# CMake
 | 
			
		||||
cmake-build-debug/
 | 
			
		||||
 | 
			
		||||
# Mongo Explorer plugin:
 | 
			
		||||
.idea/**/mongoSettings.xml
 | 
			
		||||
 | 
			
		||||
## File-based project format:
 | 
			
		||||
*.iws
 | 
			
		||||
 | 
			
		||||
## Plugin-specific files:
 | 
			
		||||
 | 
			
		||||
# IntelliJ
 | 
			
		||||
out/
 | 
			
		||||
 | 
			
		||||
# mpeltonen/sbt-idea plugin
 | 
			
		||||
.idea_modules/
 | 
			
		||||
 | 
			
		||||
# JIRA plugin
 | 
			
		||||
atlassian-ide-plugin.xml
 | 
			
		||||
 | 
			
		||||
# Cursive Clojure plugin
 | 
			
		||||
.idea/replstate.xml
 | 
			
		||||
 | 
			
		||||
# Crashlytics plugin (for Android Studio and IntelliJ)
 | 
			
		||||
com_crashlytics_export_strings.xml
 | 
			
		||||
crashlytics.properties
 | 
			
		||||
crashlytics-build.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
 | 
			
		||||
*.suo
 | 
			
		||||
*.user
 | 
			
		||||
*.userosscache
 | 
			
		||||
*.sln.docstates
 | 
			
		||||
 | 
			
		||||
# User-specific files (MonoDevelop/Xamarin Studio)
 | 
			
		||||
*.userprefs
 | 
			
		||||
 | 
			
		||||
# Build results
 | 
			
		||||
[Dd]ebug/
 | 
			
		||||
[Dd]ebugPublic/
 | 
			
		||||
[Rr]elease/
 | 
			
		||||
[Rr]eleases/
 | 
			
		||||
x64/
 | 
			
		||||
x86/
 | 
			
		||||
bld/
 | 
			
		||||
[Bb]in/
 | 
			
		||||
[Oo]bj/
 | 
			
		||||
[Ll]og/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 2015 cache/options directory
 | 
			
		||||
.vs/
 | 
			
		||||
# Uncomment if you have tasks that create the project's static files in wwwroot
 | 
			
		||||
#wwwroot/
 | 
			
		||||
 | 
			
		||||
# MSTest test Results
 | 
			
		||||
[Tt]est[Rr]esult*/
 | 
			
		||||
[Bb]uild[Ll]og.*
 | 
			
		||||
 | 
			
		||||
# NUNIT
 | 
			
		||||
*.VisualState.xml
 | 
			
		||||
TestResult.xml
 | 
			
		||||
 | 
			
		||||
# Build Results of an ATL Project
 | 
			
		||||
[Dd]ebugPS/
 | 
			
		||||
[Rr]eleasePS/
 | 
			
		||||
dlldata.c
 | 
			
		||||
 | 
			
		||||
# Benchmark Results
 | 
			
		||||
BenchmarkDotNet.Artifacts/
 | 
			
		||||
 | 
			
		||||
# .NET Core
 | 
			
		||||
project.lock.json
 | 
			
		||||
project.fragment.lock.json
 | 
			
		||||
artifacts/
 | 
			
		||||
**/Properties/launchSettings.json
 | 
			
		||||
 | 
			
		||||
*_i.c
 | 
			
		||||
*_p.c
 | 
			
		||||
*_i.h
 | 
			
		||||
*.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
 | 
			
		||||
_Chutzpah*
 | 
			
		||||
 | 
			
		||||
# Visual C++ cache files
 | 
			
		||||
ipch/
 | 
			
		||||
*.aps
 | 
			
		||||
*.ncb
 | 
			
		||||
*.opendb
 | 
			
		||||
*.opensdf
 | 
			
		||||
*.sdf
 | 
			
		||||
*.cachefile
 | 
			
		||||
*.VC.db
 | 
			
		||||
*.VC.VC.opendb
 | 
			
		||||
 | 
			
		||||
# Visual Studio profiler
 | 
			
		||||
*.psess
 | 
			
		||||
*.vsp
 | 
			
		||||
*.vspx
 | 
			
		||||
*.sap
 | 
			
		||||
 | 
			
		||||
# Visual Studio Trace Files
 | 
			
		||||
*.e2e
 | 
			
		||||
 | 
			
		||||
# TFS 2012 Local Workspace
 | 
			
		||||
$tf/
 | 
			
		||||
 | 
			
		||||
# Guidance Automation Toolkit
 | 
			
		||||
*.gpState
 | 
			
		||||
 | 
			
		||||
# ReSharper is a .NET coding add-in
 | 
			
		||||
_ReSharper*/
 | 
			
		||||
*.[Rr]e[Ss]harper
 | 
			
		||||
*.DotSettings.user
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
							
								
								
									
										114
									
								
								old_backend/.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								old_backend/.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
image: node:latest
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
    - setup
 | 
			
		||||
    - test
 | 
			
		||||
    - build
 | 
			
		||||
    - 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:
 | 
			
		||||
    stage: build
 | 
			
		||||
    needs:
 | 
			
		||||
        - *dto_artifacts_need
 | 
			
		||||
        - job: test_backend
 | 
			
		||||
          artifacts: false
 | 
			
		||||
    script:
 | 
			
		||||
        - echo This has to work till I rewrite the backend
 | 
			
		||||
        - false && echo
 | 
			
		||||
        - yarn webpack
 | 
			
		||||
    artifacts:
 | 
			
		||||
        paths:
 | 
			
		||||
            - dist/
 | 
			
		||||
        expire_in: 1h
 | 
			
		||||
 | 
			
		||||
build_frontend:
 | 
			
		||||
    stage: build
 | 
			
		||||
    needs:
 | 
			
		||||
        - *dto_artifacts_need
 | 
			
		||||
        - job: test_frontend
 | 
			
		||||
          artifacts: false
 | 
			
		||||
    script:
 | 
			
		||||
        - cd frontend
 | 
			
		||||
        - yarn build
 | 
			
		||||
    artifacts:
 | 
			
		||||
        paths:
 | 
			
		||||
            - frontend/dist/
 | 
			
		||||
        expire_in: 1h
 | 
			
		||||
 | 
			
		||||
package_server:
 | 
			
		||||
    stage: package
 | 
			
		||||
    cache: []
 | 
			
		||||
    before_script: []
 | 
			
		||||
    needs:
 | 
			
		||||
        - job: build_backend
 | 
			
		||||
          artifacts: true
 | 
			
		||||
        - job: build_frontend
 | 
			
		||||
          artifacts: true
 | 
			
		||||
    script:
 | 
			
		||||
        - TMP=$(mktemp -d)
 | 
			
		||||
        - mv dist/* "$TMP"
 | 
			
		||||
        - mkdir "$TMP/frontend"
 | 
			
		||||
        - mv frontend/dist/* "$TMP/frontend"
 | 
			
		||||
        - rm -r *
 | 
			
		||||
        - rm -r .* || true
 | 
			
		||||
        - mv "$TMP/"* .
 | 
			
		||||
    artifacts:
 | 
			
		||||
        paths:
 | 
			
		||||
            - package.json
 | 
			
		||||
            - server.js
 | 
			
		||||
            - frontend/
 | 
			
		||||
							
								
								
									
										7
									
								
								old_backend/.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								old_backend/.prettierrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
	"tabWidth": 4,
 | 
			
		||||
	"useTabs": true,
 | 
			
		||||
	"singleQuote": true,
 | 
			
		||||
	"trailingComma": "none",
 | 
			
		||||
	"endOfLine": "lf"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								old_backend/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								old_backend/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
# 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
 | 
			
		||||
````
 | 
			
		||||
							
								
								
									
										9
									
								
								old_backend/nest-cli.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								old_backend/nest-cli.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
	"$schema": "https://json.schemastore.org/nest-cli",
 | 
			
		||||
	"collection": "@nestjs/schematics",
 | 
			
		||||
	"monorepo": true,
 | 
			
		||||
	"sourceRoot": "src",
 | 
			
		||||
	"compilerOptions": {
 | 
			
		||||
		"tsConfigPath": "tsconfig.json"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										122
									
								
								old_backend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								old_backend/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
{
 | 
			
		||||
	"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"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								old_backend/requests.http
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								old_backend/requests.http
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
### 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}}
 | 
			
		||||
 | 
			
		||||
### A
 | 
			
		||||
POST https://ssh.gitlab.mattv.de/oauth/token
 | 
			
		||||
?redirect_uri=http%3A//127.0.0.1%3A1234/api/auth/gitlab_callback
 | 
			
		||||
&client_id=98bcbad78cb1f880d1d1de62291d70a791251a7bea077bfe7df111ef3c115760
 | 
			
		||||
&client_secret=7ee01d2b204aff3a05f9d028f004d169b6d381ec873e195f314b3935fa150959
 | 
			
		||||
&code=b96f91b171cf23245ea08c6f35c3831698e8683c6d0306de1396507f0f51d4c7
 | 
			
		||||
&grant_type=authorization_code
 | 
			
		||||
							
								
								
									
										28
									
								
								old_backend/src/controller/filesystem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								old_backend/src/controller/filesystem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
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) {}
 | 
			
		||||
 | 
			
		||||
	@Post('download')
 | 
			
		||||
	@Role(UserRole.USER)
 | 
			
		||||
	async download(
 | 
			
		||||
		@Request() req,
 | 
			
		||||
		@Body('id', ParseIntPipe) id
 | 
			
		||||
	): Promise<StreamableFile> {
 | 
			
		||||
		return this.fsService.downloadFile(id, req.user);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								old_backend/src/services/filesystem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								old_backend/src/services/filesystem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import {
 | 
			
		||||
	BadRequestException,
 | 
			
		||||
	Injectable,
 | 
			
		||||
	NotImplementedException,
 | 
			
		||||
	StreamableFile,
 | 
			
		||||
	UnauthorizedException
 | 
			
		||||
} from '@nestjs/common';
 | 
			
		||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
			
		||||
import { INode, User } from 'entities';
 | 
			
		||||
import { Repository } from 'typeorm';
 | 
			
		||||
import { Multipart } from '@fastify/multipart';
 | 
			
		||||
import { pipeline } from 'stream/promises';
 | 
			
		||||
import { createReadStream, createWriteStream, statSync, unlink } from 'fs';
 | 
			
		||||
import { Writable } from 'stream';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export default class FileSystemService {
 | 
			
		||||
	constructor(
 | 
			
		||||
		@InjectRepository(INode)
 | 
			
		||||
		private inodeRepo: Repository<INode>
 | 
			
		||||
	) {}
 | 
			
		||||
 | 
			
		||||
	async downloadFile(id: number, user: User): Promise<StreamableFile> {
 | 
			
		||||
		const node = await this.getNodeAndValidate(id, user);
 | 
			
		||||
		if (!node.isFile) throw new NotImplementedException();
 | 
			
		||||
		const stats = statSync(`files/${node.id}`);
 | 
			
		||||
		return new StreamableFile(createReadStream(`files/${node.id}`), {
 | 
			
		||||
			disposition: `attachment; filename="${node.name}"`,
 | 
			
		||||
			length: stats.size
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -35,10 +35,16 @@ class BetterWritable {
 | 
			
		||||
		this.indent--;
 | 
			
		||||
		this.write(data);
 | 
			
		||||
	}
 | 
			
		||||
	writeDecInc(data: string) {
 | 
			
		||||
		this.indent--;
 | 
			
		||||
		this.write(data);
 | 
			
		||||
		this.indent++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Outputable {
 | 
			
		||||
	write(s: BetterWritable);
 | 
			
		||||
	writeJsonAs(ns: string, s: BetterWritable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CppProp {
 | 
			
		||||
@@ -48,23 +54,55 @@ class CppProp {
 | 
			
		||||
	entry: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// template <> inline dto::Requests::Auth::LoginRequest Json::Value::as<dto::Requests::Auth::LoginRequest>() const { return asFloat(); }
 | 
			
		||||
 | 
			
		||||
class CppClass implements Outputable {
 | 
			
		||||
	constructor(name: string) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
	}
 | 
			
		||||
	type = 'class' as const;
 | 
			
		||||
	name: string;
 | 
			
		||||
	sub: string[] = [];
 | 
			
		||||
	entries: CppProp[] = [];
 | 
			
		||||
 | 
			
		||||
	write(s: BetterWritable) {
 | 
			
		||||
		let name = `struct ${this.name}`;
 | 
			
		||||
		if (this.sub.length > 0) name += ' : ' + this.sub.join(', ');
 | 
			
		||||
		if (this.entries.length == 0) s.write(`${name} {}`);
 | 
			
		||||
		else {
 | 
			
		||||
		if (this.sub.length > 0)
 | 
			
		||||
			name += ' : public ' + this.sub.join(', public ');
 | 
			
		||||
		s.writeInc(`${name} {`);
 | 
			
		||||
		if (this.sub.length == 0) {
 | 
			
		||||
			s.write(`${this.name}() = default;`);
 | 
			
		||||
			s.writeInc(`explicit ${this.name}(const Json::Value& j) {`);
 | 
			
		||||
		} else {
 | 
			
		||||
			s.writeInc(`${this.name}() :`);
 | 
			
		||||
			this.sub.forEach((sub) => {
 | 
			
		||||
				s.write(`${sub}()`);
 | 
			
		||||
			});
 | 
			
		||||
			s.writeDec('{}');
 | 
			
		||||
			s.writeInc(`explicit ${this.name}(const Json::Value& j) :`);
 | 
			
		||||
			this.sub.forEach((sub) => {
 | 
			
		||||
				s.write(`${sub}(j)`);
 | 
			
		||||
			});
 | 
			
		||||
			s.writeDecInc('{');
 | 
			
		||||
		}
 | 
			
		||||
		this.entries.forEach((e) => {
 | 
			
		||||
			const [type, name] = e.entry.split(' ');
 | 
			
		||||
			const opt_match = /std::optional<([a-z:<>]+)>/.exec(type);
 | 
			
		||||
			if (opt_match !== null)
 | 
			
		||||
				s.write(
 | 
			
		||||
					`this->${name} = json_get_optional<${opt_match[1]}>(j, "${name}");`
 | 
			
		||||
				);
 | 
			
		||||
			else s.write(`this->${name} = j["${name}"].as<${type}>();`);
 | 
			
		||||
		});
 | 
			
		||||
		s.writeDec('}');
 | 
			
		||||
		this.entries.forEach((e) => s.write(`${e.entry};`));
 | 
			
		||||
		s.writeDec('};');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	writeJsonAs(ns: string, s: BetterWritable) {
 | 
			
		||||
		s.write(
 | 
			
		||||
			`template <> inline ${ns}${this.name} Json::Value::as() const { return ${ns}${this.name}(*this); }`
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -72,22 +110,32 @@ class CppEnum implements Outputable {
 | 
			
		||||
	constructor(name: string) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
	}
 | 
			
		||||
	type = 'enum' as const;
 | 
			
		||||
	name: string;
 | 
			
		||||
	entries: CppProp[] = [];
 | 
			
		||||
 | 
			
		||||
	write(s: BetterWritable) {
 | 
			
		||||
		s.writeInc(`enum ${this.name} {`);
 | 
			
		||||
		return;
 | 
			
		||||
		s.writeInc(`enum ${this.name} : int {`);
 | 
			
		||||
		this.entries.forEach((e, idx, arr) =>
 | 
			
		||||
			s.write(`${e.entry}${idx === arr.length - 1 ? '' : ','}`)
 | 
			
		||||
		);
 | 
			
		||||
		s.writeDec('};');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	writeJsonAs(ns: string, s: BetterWritable) {
 | 
			
		||||
		return;
 | 
			
		||||
		s.write(
 | 
			
		||||
			`template <> inline ${ns}${this.name} Json::Value::as() const { return (${ns}${this.name})(asInt()); }`
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CppNamespace implements Outputable {
 | 
			
		||||
	constructor(name: string) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
	}
 | 
			
		||||
	type = 'ns' as const;
 | 
			
		||||
	name: string;
 | 
			
		||||
	body: (CppNamespace | CppClass | CppEnum)[] = [];
 | 
			
		||||
 | 
			
		||||
@@ -96,56 +144,57 @@ class CppNamespace implements Outputable {
 | 
			
		||||
		this.body.forEach((b) => b.write(s));
 | 
			
		||||
		s.writeDec('}');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	writeJsonAs(ns: string, s: BetterWritable) {
 | 
			
		||||
		ns += `${this.name}::`;
 | 
			
		||||
		this.body.forEach((b) => b.writeJsonAs(ns, s));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getCppType(
 | 
			
		||||
	t: TypeNode
 | 
			
		||||
): string | ((opt: boolean, name: string) => string) {
 | 
			
		||||
): null | string | ((opt: boolean, name: string) => string) {
 | 
			
		||||
	switch (t.type) {
 | 
			
		||||
		case AST_NODE_TYPES.TSStringKeyword:
 | 
			
		||||
			return 'std::string';
 | 
			
		||||
		case AST_NODE_TYPES.TSBooleanKeyword:
 | 
			
		||||
			return 'bool';
 | 
			
		||||
		case AST_NODE_TYPES.TSNumberKeyword:
 | 
			
		||||
			return 'long';
 | 
			
		||||
			return 'int';
 | 
			
		||||
		case AST_NODE_TYPES.TSTypeReference:
 | 
			
		||||
			if (t.typeName.type != AST_NODE_TYPES.Identifier) throw new Err(t);
 | 
			
		||||
			return t.typeName.name;
 | 
			
		||||
		case AST_NODE_TYPES.TSLiteralType:
 | 
			
		||||
			if (t.literal.type != AST_NODE_TYPES.Literal) throw new Err(t);
 | 
			
		||||
			return (opt, name) => `${name} = ${(t.literal as a.Literal).raw}`;
 | 
			
		||||
			return null;
 | 
			
		||||
		case AST_NODE_TYPES.TSUnionType:
 | 
			
		||||
			return _getCppType(t.types[0]);
 | 
			
		||||
		case AST_NODE_TYPES.TSArrayType:
 | 
			
		||||
			return (opt, name) =>
 | 
			
		||||
				opt
 | 
			
		||||
					? `std::optional<${_getCppType(t.elementType)}[]> ${name}`
 | 
			
		||||
					: `${getCppType(false, name, t.elementType)}[]`;
 | 
			
		||||
			return `std::vector<${_getCppType(t.elementType)}>`;
 | 
			
		||||
		default:
 | 
			
		||||
			throw new Err(t);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getCppType(opt: boolean, name: string, t: TypeNode): string {
 | 
			
		||||
function getCppType(opt: boolean, name: string, t: TypeNode): null | string {
 | 
			
		||||
	let ret = _getCppType(t);
 | 
			
		||||
	if (typeof ret === 'function') return `${ret(opt, name)}`;
 | 
			
		||||
	if (typeof ret === 'string') {
 | 
			
		||||
		if (opt) ret = `std::optional<${ret}>`;
 | 
			
		||||
		return `${ret} ${name}`;
 | 
			
		||||
	} else return `${ret(opt, name)}`;
 | 
			
		||||
	} else return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleClassProperty(
 | 
			
		||||
	prop: a.PropertyDefinitionComputedName | a.PropertyDefinitionNonComputedName
 | 
			
		||||
): CppProp {
 | 
			
		||||
): CppProp | null {
 | 
			
		||||
	if (prop.key.type != AST_NODE_TYPES.Identifier) throw new Err(prop);
 | 
			
		||||
	if (!prop.typeAnnotation) throw new Err(prop);
 | 
			
		||||
	return new CppProp(
 | 
			
		||||
		getCppType(
 | 
			
		||||
	const type = getCppType(
 | 
			
		||||
		prop.optional && prop.optional,
 | 
			
		||||
		prop.key.name,
 | 
			
		||||
		prop.typeAnnotation.typeAnnotation
 | 
			
		||||
		)
 | 
			
		||||
	);
 | 
			
		||||
	return type === null ? null : new CppProp(type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleClass(
 | 
			
		||||
@@ -160,13 +209,14 @@ function handleClass(
 | 
			
		||||
	}
 | 
			
		||||
	dec.body.body.forEach((inner) => {
 | 
			
		||||
		if (inner.type == AST_NODE_TYPES.PropertyDefinition) {
 | 
			
		||||
			cls.entries.push(handleClassProperty(inner));
 | 
			
		||||
			const prop = handleClassProperty(inner);
 | 
			
		||||
			if (prop) cls.entries.push(prop);
 | 
			
		||||
		} else if (
 | 
			
		||||
			inner.type == AST_NODE_TYPES.MethodDefinition &&
 | 
			
		||||
			inner.key.type == AST_NODE_TYPES.Identifier &&
 | 
			
		||||
			inner.key.name === 'constructor'
 | 
			
		||||
		)
 | 
			
		||||
			console.warn('Handle constructors?');
 | 
			
		||||
			return;
 | 
			
		||||
		else throw new Err(inner);
 | 
			
		||||
	});
 | 
			
		||||
	return cls;
 | 
			
		||||
@@ -240,8 +290,17 @@ function parseFile(ns: CppNamespace, file: string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const globalNamespace = new CppNamespace('dto');
 | 
			
		||||
parseFile(globalNamespace, 'dto/index.ts');
 | 
			
		||||
parseFile(globalNamespace, '../dto/index.ts');
 | 
			
		||||
 | 
			
		||||
const output_stream = fs.createWriteStream('dto.h');
 | 
			
		||||
output_stream.write('#include <string>\n#include <optional>\n\n');
 | 
			
		||||
globalNamespace.write(new BetterWritable(output_stream));
 | 
			
		||||
const output_stream = fs.createWriteStream('../backend/src/dto.h');
 | 
			
		||||
output_stream.write(
 | 
			
		||||
	'#pragma once\n\n' +
 | 
			
		||||
		'#include <string>\n' +
 | 
			
		||||
		'#include <optional>\n' +
 | 
			
		||||
		'#include <vector>\n' +
 | 
			
		||||
		'#include <json/allocator.h>\n' +
 | 
			
		||||
		'#include "dto_extras.h"\n\n'
 | 
			
		||||
);
 | 
			
		||||
const output = new BetterWritable(output_stream);
 | 
			
		||||
globalNamespace.write(output);
 | 
			
		||||
globalNamespace.writeJsonAs('', output);
 | 
			
		||||
							
								
								
									
										19
									
								
								old_backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								old_backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
	"compilerOptions": {
 | 
			
		||||
		"module": "commonjs",
 | 
			
		||||
		"declaration": false,
 | 
			
		||||
		"removeComments": true,
 | 
			
		||||
		"emitDecoratorMetadata": true,
 | 
			
		||||
		"experimentalDecorators": true,
 | 
			
		||||
		"allowSyntheticDefaultImports": true,
 | 
			
		||||
		"target": "es2017",
 | 
			
		||||
		"sourceMap": true,
 | 
			
		||||
		"outDir": "./dist",
 | 
			
		||||
		"baseUrl": "./src",
 | 
			
		||||
		"incremental": true,
 | 
			
		||||
		"skipLibCheck": true,
 | 
			
		||||
		"resolveJsonModule": true,
 | 
			
		||||
		"strictPropertyInitialization": false
 | 
			
		||||
	},
 | 
			
		||||
	"exclude": ["node_modules", "dist", "test", "**/*spec.ts", "frontend"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6014
									
								
								old_backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6014
									
								
								old_backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user