diff --git a/migrations/2025-03-25-081455_create_user_role_permission_table/down.sql b/migrations/2025-03-25-081455_create_user_role_permission_table/down.sql new file mode 100644 index 0000000000000000000000000000000000000000..dbea530be14a59cbc42e1c394e868dceb10a5ac3 --- /dev/null +++ b/migrations/2025-03-25-081455_create_user_role_permission_table/down.sql @@ -0,0 +1,21 @@ +-- This file should undo anything in `up.sql` + +-- First drop users tables (because they have foreign keys to roles) +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS users_replica1; +DROP TABLE IF EXISTS users_replica2; + +-- Then drop role_permissions tables (because they have foreign keys to both roles and permissions) +DROP TABLE IF EXISTS role_permissions; +DROP TABLE IF EXISTS role_permissions_replica1; +DROP TABLE IF EXISTS role_permissions_replica2; + +-- Then drop roles tables +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS roles_replica1; +DROP TABLE IF EXISTS roles_replica2; + +-- Finally drop permissions tables +DROP TABLE IF EXISTS permissions; +DROP TABLE IF EXISTS permissions_replica1; +DROP TABLE IF EXISTS permissions_replica2; \ No newline at end of file diff --git a/migrations/2025-03-25-081455_create_user_role_permission_table/up.sql b/migrations/2025-03-25-081455_create_user_role_permission_table/up.sql new file mode 100644 index 0000000000000000000000000000000000000000..93045e9c3e37991d49cbb61fa19568ede0de9c08 --- /dev/null +++ b/migrations/2025-03-25-081455_create_user_role_permission_table/up.sql @@ -0,0 +1,105 @@ +-- First create roles tables +CREATE TABLE IF NOT EXISTS roles ( + role_id TEXT NOT NULL PRIMARY KEY, + role_name TEXT NOT NULL UNIQUE, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS roles_replica1 ( + role_id TEXT NOT NULL PRIMARY KEY, + role_name TEXT NOT NULL UNIQUE, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS roles_replica2 ( + role_id TEXT NOT NULL PRIMARY KEY, + role_name TEXT NOT NULL UNIQUE, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +-- First create permissions table +CREATE TABLE IF NOT EXISTS permissions ( + permission_id TEXT NOT NULL PRIMARY KEY, + url_path TEXT NOT NULL, + http_method TEXT NOT NULL, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS permissions_replica1 ( + permission_id TEXT NOT NULL PRIMARY KEY, + url_path TEXT NOT NULL, + http_method TEXT NOT NULL, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS permissions_replica2 ( + permission_id TEXT NOT NULL PRIMARY KEY, + url_path TEXT NOT NULL, + http_method TEXT NOT NULL, + description TEXT, + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +-- Create role-permission mapping table for many-to-many relationship +CREATE TABLE IF NOT EXISTS role_permissions ( + role_id TEXT NOT NULL REFERENCES roles(role_id), + permission_id TEXT NOT NULL REFERENCES permissions(permission_id), + created_time VARCHAR(256), + updated_time VARCHAR(256), + PRIMARY KEY (role_id, permission_id) +); + +CREATE TABLE IF NOT EXISTS role_permissions_replica1 ( + role_id TEXT NOT NULL REFERENCES roles(role_id), + permission_id TEXT NOT NULL REFERENCES permissions(permission_id), + created_time VARCHAR(256), + updated_time VARCHAR(256), + PRIMARY KEY (role_id, permission_id) +); + +CREATE TABLE IF NOT EXISTS role_permissions_replica2 ( + role_id TEXT NOT NULL REFERENCES roles(role_id), + permission_id TEXT NOT NULL REFERENCES permissions(permission_id), + created_time VARCHAR(256), + updated_time VARCHAR(256), + PRIMARY KEY (role_id, permission_id) +); + +-- Then create users tables with role foreign key +CREATE TABLE IF NOT EXISTS users ( + user_id TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + role_id TEXT NOT NULL REFERENCES roles(role_id), + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS users_replica1 ( + user_id TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + role_id TEXT NOT NULL REFERENCES roles(role_id), + created_time VARCHAR(256), + updated_time VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS users_replica2 ( + user_id TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + role_id TEXT NOT NULL REFERENCES roles(role_id), + created_time VARCHAR(256), + updated_time VARCHAR(256) +); \ No newline at end of file diff --git a/migrations/2025-03-25-081455_create_user_table/down.sql b/migrations/2025-03-25-081455_create_user_table/down.sql deleted file mode 100644 index 28605e120af483307fb1a794978968988e7f8dbf..0000000000000000000000000000000000000000 --- a/migrations/2025-03-25-081455_create_user_table/down.sql +++ /dev/null @@ -1,4 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS users_replica1; -DROP TABLE IF EXISTS users_replica2; \ No newline at end of file diff --git a/migrations/2025-03-25-081455_create_user_table/up.sql b/migrations/2025-03-25-081455_create_user_table/up.sql deleted file mode 100644 index 3a2d29601ee0db6db3f51073c785039794b19831..0000000000000000000000000000000000000000 --- a/migrations/2025-03-25-081455_create_user_table/up.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Your SQL goes here -CREATE TABLE IF NOT EXISTS users ( - user_id TEXT NOT NULL PRIMARY KEY, - username TEXT NOT NULL UNIQUE, - password TEXT NOT NULL, - created_time VARCHAR(256), - updated_time VARCHAR(256) - ); - -CREATE TABLE IF NOT EXISTS users_replica1 ( - user_id TEXT NOT NULL PRIMARY KEY, - username TEXT NOT NULL UNIQUE, - password TEXT NOT NULL, - created_time VARCHAR(256), - updated_time VARCHAR(256) - ); - -CREATE TABLE IF NOT EXISTS users_replica2 ( - user_id TEXT NOT NULL PRIMARY KEY, - username TEXT NOT NULL UNIQUE, - password TEXT NOT NULL, - created_time VARCHAR(256), - updated_time VARCHAR(256) - ); \ No newline at end of file diff --git a/src/cores/handlers/datamgr/datamgr_api.rs b/src/cores/handlers/datamgr/datamgr_api.rs index 57eabfeb70a06bddaeaba8cb5c92c39fb6d17100..58318b71113f6c5827c7c7c5d99ff7eb322a5123 100644 --- a/src/cores/handlers/datamgr/datamgr_api.rs +++ b/src/cores/handlers/datamgr/datamgr_api.rs @@ -1,3 +1,4 @@ +#![allow(non_snake_case)] /* automatically generated by rust-bindgen 0.71.1 */ unsafe extern "C" { @@ -215,12 +216,17 @@ unsafe extern "C" { responseData: *const ::std::os::raw::c_char, ) -> ::std::os::raw::c_int; } +#[doc = " @brief 数据库查询结果"] #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct DbResponse { + #[doc = " @brief 行数"] pub RowCount: ::std::os::raw::c_int, + #[doc = " @brief 列数"] pub ColumnCount: ::std::os::raw::c_int, + #[doc = " @brief 列名"] pub ColumnNames: *mut *mut ::std::os::raw::c_char, + #[doc = " @brief 数据"] pub Data: *mut *mut *mut ::std::os::raw::c_char, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] @@ -236,9 +242,11 @@ const _: () = { ["Offset of field: DbResponse::Data"][::std::mem::offset_of!(DbResponse, Data) - 16usize]; }; unsafe extern "C" { + #[doc = " @brief 创建数据库查询结果\n @return 空的数据库查询结果"] pub fn CreateDbResponse() -> *mut DbResponse; } unsafe extern "C" { + #[doc = " @brief 回收数据库查询结果\n @param[in] response 数据库查询结果"] pub fn FreeDbResponse(response: *mut DbResponse); } unsafe extern "C" { @@ -324,9 +332,11 @@ unsafe extern "C" { #[doc = " @brief 元数据"] #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct Metadata { +pub struct DataInfo { #[doc = " @brief 数据大小"] pub size: ::std::os::raw::c_int, + #[doc = " @brief 更新时间"] + pub lastUpdate: ::std::os::raw::c_int, #[doc = " @brief 存储路径数目"] pub pathCount: ::std::os::raw::c_int, #[doc = " @brief 存储路径"] @@ -334,24 +344,427 @@ pub struct Metadata { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of Metadata"][::std::mem::size_of::() - 16usize]; - ["Alignment of Metadata"][::std::mem::align_of::() - 8usize]; - ["Offset of field: Metadata::size"][::std::mem::offset_of!(Metadata, size) - 0usize]; - ["Offset of field: Metadata::pathCount"][::std::mem::offset_of!(Metadata, pathCount) - 4usize]; - ["Offset of field: Metadata::paths"][::std::mem::offset_of!(Metadata, paths) - 8usize]; + ["Size of DataInfo"][::std::mem::size_of::() - 24usize]; + ["Alignment of DataInfo"][::std::mem::align_of::() - 8usize]; + ["Offset of field: DataInfo::size"][::std::mem::offset_of!(DataInfo, size) - 0usize]; + ["Offset of field: DataInfo::lastUpdate"] + [::std::mem::offset_of!(DataInfo, lastUpdate) - 4usize]; + ["Offset of field: DataInfo::pathCount"][::std::mem::offset_of!(DataInfo, pathCount) - 8usize]; + ["Offset of field: DataInfo::paths"][::std::mem::offset_of!(DataInfo, paths) - 16usize]; }; unsafe extern "C" { - #[doc = " @brief 获取元数据\n @details 注意使用 FreeMetadata 回收数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 数据唯一标识符\n @param[in] dataType 数据类型\n @param[in] owner 数据使用方\n @return 元数据"] - pub fn GetMetadata( + #[doc = " @brief 获取元数据\n @details 注意使用 FreeDataInfo 回收数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 数据唯一标识符\n @param[in] dataType 数据类型\n @param[in] owner 数据使用方\n @return 元数据"] + pub fn GetDataInfo( pluginManager: *mut ::std::os::raw::c_void, name: *const ::std::os::raw::c_char, dataType: *const ::std::os::raw::c_char, owner: *const ::std::os::raw::c_char, - ) -> *mut Metadata; + ) -> *mut DataInfo; } unsafe extern "C" { - #[doc = " @brief 回收Metadata数据结构\n @param[in] metadata"] - pub fn FreeMetadata(metadata: *mut Metadata); + #[doc = " @brief 获取指定版本数据的元数据\n @details 注意使用 FreeDataInfo 回收数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 数据唯一标识符\n @param[in] dataType 数据类型\n @param[in] owner 数据使用方\n @param[in] version 数据版本\n @return 元数据"] + pub fn GetDataInfoWithVersion( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + owner: *const ::std::os::raw::c_char, + version: *const ::std::os::raw::c_char, + ) -> *mut DataInfo; +} +unsafe extern "C" { + #[doc = " @brief 回收DataInfo数据结构\n @param[in] dataInfo"] + pub fn FreeDataInfo(dataInfo: *mut DataInfo); +} +#[doc = " @brief 存储介质"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Device { + #[doc = " @brief 名称"] + pub name: *mut ::std::os::raw::c_char, + #[doc = " @brief 描述"] + pub description: *mut ::std::os::raw::c_char, + #[doc = " @brief 设备文件"] + pub deviceFile: *mut ::std::os::raw::c_char, + #[doc = " @brief 文件系统中的路径"] + pub directory: *mut ::std::os::raw::c_char, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Device"][::std::mem::size_of::() - 32usize]; + ["Alignment of Device"][::std::mem::align_of::() - 8usize]; + ["Offset of field: Device::name"][::std::mem::offset_of!(Device, name) - 0usize]; + ["Offset of field: Device::description"][::std::mem::offset_of!(Device, description) - 8usize]; + ["Offset of field: Device::deviceFile"][::std::mem::offset_of!(Device, deviceFile) - 16usize]; + ["Offset of field: Device::directory"][::std::mem::offset_of!(Device, directory) - 24usize]; +}; +#[doc = " @brief 存储介质列表"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DeviceList { + #[doc = " @brief 元数数量"] + pub count: ::std::os::raw::c_ulong, + #[doc = " @brief 存储介质列表"] + pub devices: *mut *mut Device, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of DeviceList"][::std::mem::size_of::() - 16usize]; + ["Alignment of DeviceList"][::std::mem::align_of::() - 8usize]; + ["Offset of field: DeviceList::count"][::std::mem::offset_of!(DeviceList, count) - 0usize]; + ["Offset of field: DeviceList::devices"][::std::mem::offset_of!(DeviceList, devices) - 8usize]; +}; +unsafe extern "C" { + #[doc = " @brief 回收存储介质结构体\n @param[in] device 存储介质"] + pub fn FreeDevice(device: *mut Device); +} +unsafe extern "C" { + #[doc = " @brief 回收存储介质列表结构体\n @param[in] deviceList 存储介质列表"] + pub fn FreeDeviceList(deviceList: *mut DeviceList); +} +unsafe extern "C" { + #[doc = " @brief 创建存储介质\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @param[in] description 描述\n @param[in] deviceFile 设备文件\n @param[in] directory 文件系统中的路径\n @return 指示操作是否成功"] + pub fn CreateDevice( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + description: *const ::std::os::raw::c_char, + deviceFile: *const ::std::os::raw::c_char, + directory: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 更新存储介质\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @param[in] newDescription 新的描述\n @param[in] newDeviceFile 新的设备文件\n @param[in] newDirectory 新的路径\n @return 指示操作是否成功"] + pub fn UpdateDevice( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + newDescription: *const ::std::os::raw::c_char, + newDeviceFile: *const ::std::os::raw::c_char, + newDirectory: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除存储介质\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @return 指示操作是否成功"] + pub fn RemoveDevice( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 获取存储介质\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @return 存储介质"] + pub fn GetDeviceByName( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + ) -> *mut Device; +} +unsafe extern "C" { + #[doc = " @brief 获取默认存储介质\n @param[in] pluginManager 插件管理器实例指针\n @return 默认存储介质"] + pub fn GetDefaultDevice(pluginManager: *mut ::std::os::raw::c_void) -> *mut Device; +} +unsafe extern "C" { + #[doc = " @brief 获取所有存储介质\n @param[in] pluginManager 插件管理器实例指针\n @return 存储介质列表"] + pub fn GetAllDevices(pluginManager: *mut ::std::os::raw::c_void) -> *mut DeviceList; +} +#[doc = " @brief 存储策略"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Strategy { + #[doc = " @brief 名称"] + pub name: *mut ::std::os::raw::c_char, + #[doc = " @brief 描述"] + pub description: *mut ::std::os::raw::c_char, + #[doc = " @brief 存储路径数目"] + pub locationCount: ::std::os::raw::c_ulong, + #[doc = " @brief 每个存储路径对应的存储介质名称"] + pub locationDeviceNames: *mut *mut ::std::os::raw::c_char, + #[doc = " @brief 每个存储路径对应的相对路径"] + pub locationRelativePaths: *mut *mut ::std::os::raw::c_char, + #[doc = " @brief 容错纠错算法"] + pub errorCorrectingAlgorithm: *mut ::std::os::raw::c_char, + #[doc = " @brief 完整性校验算法"] + pub integrityCheckAlgorithm: *mut ::std::os::raw::c_char, + #[doc = " @brief 数据保存时长"] + pub lifeTimeInSecond: ::std::os::raw::c_ulong, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Strategy"][::std::mem::size_of::() - 64usize]; + ["Alignment of Strategy"][::std::mem::align_of::() - 8usize]; + ["Offset of field: Strategy::name"][::std::mem::offset_of!(Strategy, name) - 0usize]; + ["Offset of field: Strategy::description"] + [::std::mem::offset_of!(Strategy, description) - 8usize]; + ["Offset of field: Strategy::locationCount"] + [::std::mem::offset_of!(Strategy, locationCount) - 16usize]; + ["Offset of field: Strategy::locationDeviceNames"] + [::std::mem::offset_of!(Strategy, locationDeviceNames) - 24usize]; + ["Offset of field: Strategy::locationRelativePaths"] + [::std::mem::offset_of!(Strategy, locationRelativePaths) - 32usize]; + ["Offset of field: Strategy::errorCorrectingAlgorithm"] + [::std::mem::offset_of!(Strategy, errorCorrectingAlgorithm) - 40usize]; + ["Offset of field: Strategy::integrityCheckAlgorithm"] + [::std::mem::offset_of!(Strategy, integrityCheckAlgorithm) - 48usize]; + ["Offset of field: Strategy::lifeTimeInSecond"] + [::std::mem::offset_of!(Strategy, lifeTimeInSecond) - 56usize]; +}; +#[doc = " @brief 存储策略列表"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct StrategyList { + #[doc = " @brief 元素数量"] + pub count: ::std::os::raw::c_ulong, + #[doc = " @brief 存储策略列表"] + pub strategies: *mut *mut Strategy, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of StrategyList"][::std::mem::size_of::() - 16usize]; + ["Alignment of StrategyList"][::std::mem::align_of::() - 8usize]; + ["Offset of field: StrategyList::count"][::std::mem::offset_of!(StrategyList, count) - 0usize]; + ["Offset of field: StrategyList::strategies"] + [::std::mem::offset_of!(StrategyList, strategies) - 8usize]; +}; +unsafe extern "C" { + #[doc = " @brief 回收存储策略结构体\n @param[in] strategy 存储策略"] + pub fn FreeStrategy(strategy: *mut Strategy); +} +unsafe extern "C" { + #[doc = " @brief 回收存储策略列表\n @param[in] strategyList 存储策略列表"] + pub fn FreeStrategyList(strategyList: *mut StrategyList); +} +unsafe extern "C" { + #[doc = " @brief 回收字符串\n @param string 字符串"] + pub fn FreeString(string: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + #[doc = " @brief 创建存储介质\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @param[in] description 描述\n @param[in] locationCount 存储路径数目\n @param[in] locationDeviceNames 每个存储路径对应的存储介质名称\n @param[in] locationRelativePaths 每个存储路径对应的相对路径\n @param[in] errorCorrectingAlgorithm 容错纠错算法\n @param[in] integrityCheckAlgorithm 完整性校验算法\n @param[in] lifeTimeInSecond 数据保存时长\n @return 指示操作是否成功"] + pub fn CreateStrategy( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + description: *const ::std::os::raw::c_char, + locationCount: ::std::os::raw::c_int, + locationDeviceNames: *mut *const ::std::os::raw::c_char, + locationRelativePaths: *mut *const ::std::os::raw::c_char, + errorCorrectingAlgorithm: *const ::std::os::raw::c_char, + integrityCheckAlgorithm: *const ::std::os::raw::c_char, + lifeTimeInSecond: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 更新存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @param[in] newDescription 描述\n @param[in] newLocationCount 存储路径数目\n @param[in] newLocationDeviceNames 每个存储路径对应的存储介质名称\n @param[in] newLocationRelativePaths 每个存储路径对应的相对路径\n @param[in] newErrorCorrectingAlgorithm 容错纠错算法\n @param[in] newIntegrityCheckAlgorithm 完整性校验算法\n @param[in] newLifeTimeInSecond 数据保存时长\n @return 指示操作是否成功"] + pub fn UpdateStrategy( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + newDescription: *const ::std::os::raw::c_char, + newLocationCount: ::std::os::raw::c_int, + newLocationDeviceNames: *mut *const ::std::os::raw::c_char, + newLocationRelativePaths: *mut *const ::std::os::raw::c_char, + newErrorCorrectingAlgorithm: *const ::std::os::raw::c_char, + newIntegrityCheckAlgorithm: *const ::std::os::raw::c_char, + newLifeTimeInSecond: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @return 指示操作是否成功"] + pub fn RemoveStrategy( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 获取存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] name 名称\n @return 存储策略"] + pub fn GetStrategyByName( + pluginManager: *mut ::std::os::raw::c_void, + name: *const ::std::os::raw::c_char, + ) -> *mut Strategy; +} +unsafe extern "C" { + #[doc = " @brief 获取默认存储策略\n @param[in] pluginManager 插件管理器实例指针\n @return 默认存储策略"] + pub fn GetDefaultStrategy(pluginManager: *mut ::std::os::raw::c_void) -> *mut Strategy; +} +unsafe extern "C" { + #[doc = " @brief 获取所有存储策略\n @param[in] pluginManager 插件管理器实例指针\n @return 存储策略列表"] + pub fn GetAllStrategies(pluginManager: *mut ::std::os::raw::c_void) -> *mut StrategyList; +} +unsafe extern "C" { + #[doc = " @brief 配置存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用名称\n @param[in] dataType 数据类型\n @param[in] strategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn CreateProfile( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + strategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 更新存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用名称\n @param[in] dataType 数据类型\n @param[in] newStrategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn UpdateProfile( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + newStrategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @return 指示操作是否成功"] + pub fn RemoveProfile( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 获取存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @return 一个字符串,关联到特定应用和数据类型的存储策略名称"] + pub fn GetProfile( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + #[doc = " @brief 为应用的所有数据类型设置存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用名称\n @param[in] strategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn CreateProfileForApplication( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + strategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 更新应用的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用名称\n @param[in] newStrategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn UpdateProfileForApplication( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + newStrategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除应用的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @return 指示操作是否成功"] + pub fn RemoveProfileForApplication( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 获取应用的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @return 一个字符串,关联到特定应用的存储策略名称"] + pub fn GetProfileForApplication( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + #[doc = " @brief 为特定数据类型设置存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] dataType 数据类型\n @param[in] strategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn CreateProfileForDataType( + pluginManager: *mut ::std::os::raw::c_void, + dataType: *const ::std::os::raw::c_char, + strategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 更新特定数据类型的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] dataType 数据类型\n @param[in] newStrategyName 存储策略名称\n @return 指示操作是否成功"] + pub fn UpdateProfileForDataType( + pluginManager: *mut ::std::os::raw::c_void, + dataType: *const ::std::os::raw::c_char, + newStrategyName: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除特定数据类型的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] dataType 数据类型\n @return 指示操作是否成功"] + pub fn RemoveProfileForDataType( + pluginManager: *mut ::std::os::raw::c_void, + dataType: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 获取特定数据类型的存储策略配置\n @param[in] pluginManager 插件管理器实例指针\n @param[in] dataType 数据类型\n @return 一个字符串,关联到特定数据类型的存储策略名称"] + pub fn GetProfileForDataType( + pluginManager: *mut ::std::os::raw::c_void, + dataType: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + #[doc = " @brief 查找存储策略配置\n @details 优先检索“应用+数据类型”的配置信息,之后检索特定应用的配置信息,之后检索特定数据类型的配置信息,都未找到时返回默认存储策略\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @return 一个字符串,存储策略名称"] + pub fn FindProfile( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +#[doc = " @brief 数据块"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DataBlock { + #[doc = " @brief 数据大小"] + pub size: ::std::os::raw::c_ulong, + #[doc = " @brief 数据内容"] + pub data: *mut ::std::os::raw::c_char, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of DataBlock"][::std::mem::size_of::() - 16usize]; + ["Alignment of DataBlock"][::std::mem::align_of::() - 8usize]; + ["Offset of field: DataBlock::size"][::std::mem::offset_of!(DataBlock, size) - 0usize]; + ["Offset of field: DataBlock::data"][::std::mem::offset_of!(DataBlock, data) - 8usize]; +}; +unsafe extern "C" { + #[doc = " @brief 回收数据块\n @param dataBlock 数据块"] + pub fn FreeDataBlock(dataBlock: *mut DataBlock); +} +unsafe extern "C" { + #[doc = " @brief 读取数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @return 数据块"] + pub fn ReadData( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + ) -> *mut DataBlock; +} +unsafe extern "C" { + #[doc = " @brief 读取指定版本的数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @param[in] version 版本\n @return 数据块"] + pub fn ReadDataWithVersion( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + version: *const ::std::os::raw::c_char, + ) -> *mut DataBlock; +} +unsafe extern "C" { + #[doc = " @brief 写入数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @param[in] size 数据大小\n @param[in] data 数据内容\n @return 指示操作是否成功"] + pub fn WriteData( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + size: ::std::os::raw::c_ulong, + data: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 写入指定版本的数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @param[in] version 版本\n @param[in] size 数据大小\n @param[in] data 数据内容\n @return 指示操作是否成功"] + pub fn WriteDataWithVersion( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + version: *const ::std::os::raw::c_char, + size: ::std::os::raw::c_ulong, + data: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @return 指示操作是否成功"] + pub fn RemoveData( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 删除指定版本的数据\n @param[in] pluginManager 插件管理器实例指针\n @param[in] application 应用\n @param[in] dataType 数据类型\n @param[in] name 数据唯一标识符\n @param[in] version 版本\n @return 指示操作是否成功"] + pub fn RemoveDataWithVersion( + pluginManager: *mut ::std::os::raw::c_void, + application: *const ::std::os::raw::c_char, + dataType: *const ::std::os::raw::c_char, + name: *const ::std::os::raw::c_char, + version: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; } @@ -570,3 +983,78 @@ unsafe extern "C" { values: *mut *const ::std::os::raw::c_char, ) -> ::std::os::raw::c_int; } +// pub type MessageCallback = ::std::option::Option< +// unsafe extern "C" fn( +// topic: *const ::std::os::raw::c_char, +// uuid: *const ::std::os::raw::c_char, +// size: ::std::os::raw::c_int, +// data: *const ::std::os::raw::c_char, +// closure: *mut ::std::os::raw::c_void, +// ), +// >; +unsafe extern "C" { + #[doc = " @brief 断点续传消息发布\n @details 发布支持断点续传的消息,自动处理大消息的分片传输和网络中断后的续传\n @param [in] pluginManager 插件管理器\n @param [in] topic 消息主题\n @param [in] data 消息数据\n @param [in] length 数据长度\n @return 1: success. else: error\n @note 底层自动处理消息分片、重发和断点续传机制"] + pub fn PublishResumableMessage( + pluginManager: *mut ::std::os::raw::c_void, + topic: *const ::std::os::raw::c_char, + data: *const ::std::os::raw::c_char, + length: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 断点续传消息订阅\n @details 订阅支持断点续传的消息,自动处理消息分片的接收、去重和合并\n @param [in] pluginManager 插件管理器\n @param [in] topic 消息主题\n @param [in] callback 消息回调函数\n @return 1: success. else: error\n @note 回调函数将在消息完整接收并合并后被调用"] + pub fn SubscribeResumableMessage( + pluginManager: *mut ::std::os::raw::c_void, + topic: *const ::std::os::raw::c_char, + callback: MessageCallback, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 可靠消息发布(失效重传)\n @details 发布具有失效重传机制的可靠消息,确保消息可靠传输\n @param [in] pluginManager 插件管理器\n @param [in] topic 消息主题\n @param [in] data 消息数据\n @param [in] length 数据长度\n @return 1: success. else: error\n @note 底层自动处理消息重传,直到收到确认或超时"] + pub fn PublishReliableMessage( + pluginManager: *mut ::std::os::raw::c_void, + topic: *const ::std::os::raw::c_char, + data: *const ::std::os::raw::c_char, + length: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 可靠消息订阅\n @details 订阅具有失效重传机制的可靠消息\n @param [in] pluginManager 插件管理器\n @param [in] topic 消息主题\n @param [in] callback 消息回调函数\n @return 1: success. else: error\n @note 确保消息可靠接收,自动发送确认"] + pub fn SubscribeReliableMessage( + pluginManager: *mut ::std::os::raw::c_void, + topic: *const ::std::os::raw::c_char, + callback: MessageCallback, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 发送控制命令\n @details 向指定节点发送控制命令消息\n @param [in] pluginManager 插件管理器\n @param [in] targetNode 目标节点标识\n @param [in] command 控制命令数据\n @param [in] length 命令数据长度\n @return 1: success. else: error\n @note 控制命令通过固定主题 \"control.command\" 传输"] + pub fn SendControlCommand( + pluginManager: *mut ::std::os::raw::c_void, + targetNode: *const ::std::os::raw::c_char, + command: *const ::std::os::raw::c_char, + length: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 订阅控制命令\n @details 订阅来自其他节点的控制命令消息\n @param [in] pluginManager 插件管理器\n @param [in] callback 消息回调函数\n @return 1: success. else: error\n @note 监听固定主题 \"control.command\" 上的消息"] + pub fn SubscribeControlCommand( + pluginManager: *mut ::std::os::raw::c_void, + callback: MessageCallback, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 发送状态数据\n @details 向指定节点发送状态数据消息\n @param [in] pluginManager 插件管理器\n @param [in] targetNode 目标节点标识\n @param [in] statusData 状态数据\n @param [in] length 数据长度\n @return 1: success. else: error\n @note 状态数据通过固定主题 \"status.data\" 传输"] + pub fn SendStatusData( + pluginManager: *mut ::std::os::raw::c_void, + targetNode: *const ::std::os::raw::c_char, + statusData: *const ::std::os::raw::c_char, + length: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[doc = " @brief 订阅状态数据\n @details 订阅来自其他节点的状态数据消息\n @param [in] pluginManager 插件管理器\n @param [in] callback 消息回调函数\n @return 1: success. else: error\n @note 监听固定主题 \"status.data\" 上的消息"] + pub fn SubscribeStatusData( + pluginManager: *mut ::std::os::raw::c_void, + callback: MessageCallback, + ) -> ::std::os::raw::c_int; +} diff --git a/src/cores/handlers/mod.rs b/src/cores/handlers/mod.rs index 43f480e81da29bf6f2d55c5033b2dacbe00ad5bc..24a784f69896352634445678e6146b4bac3886f2 100644 --- a/src/cores/handlers/mod.rs +++ b/src/cores/handlers/mod.rs @@ -8,4 +8,6 @@ pub mod router_topo; pub mod security; pub mod test; pub mod users; -pub mod payload; \ No newline at end of file +pub mod payload; +pub mod roles; +pub mod permissions; \ No newline at end of file diff --git a/src/cores/handlers/permissions.rs b/src/cores/handlers/permissions.rs new file mode 100644 index 0000000000000000000000000000000000000000..37992dac2c91803bb3eda9b3b403ffb0ad81e1d1 --- /dev/null +++ b/src/cores/handlers/permissions.rs @@ -0,0 +1,261 @@ +use std::sync::Arc; +use serde::Deserialize; +use serde_json::{json, Value}; +use uuid::Uuid; +use crate::cores::state::AppState; +use crate::db::check_exist::{check_permissions_by_id}; +use crate::db::delete::{delete_from_permissions_by_id, delete_role_permission}; +use crate::db::get::{get_all_data_from_permissions, get_data_from_permissions_by_id, get_roles_by_permission_id}; +use crate::db::insert::{insert_permissions}; +use crate::db::update::update_permission_in_permissions_by_id; +use fleetmodv2::api_server::{ServerError, ServerRequest, ServerResult}; + +/// 将 Diesel 数据库错误转换为服务器错误 +fn map_diesel_err(error: diesel::result::Error) -> ServerError { + ServerError::internal_error(error.to_string().as_str()) +} + +/// 创建权限的请求结构体 +#[derive(Debug, Deserialize)] +struct PermissionCreateRequest { + url_path: String, // API 路径 + http_method: String, // HTTP 方法 + description: Option, // 权限描述(可选) +} + +/// 创建权限的处理函数 +/// +/// # 功能 +/// - 验证请求数据的合法性 +/// - 生成新的权限ID +/// - 在数据库中创建新的权限记录 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含请求数据的服务器请求对象 +pub async fn create_permission_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); + } + let mut db_conn = db_conn.unwrap(); + + // 解析请求体中的JSON数据 + let body = server_request.body.as_ref().unwrap().as_slice(); + let data_content = body.to_vec(); + + // 将JSON数据反序列化为PermissionCreateRequest结构体 + let create_req: PermissionCreateRequest = match serde_json::from_slice(&data_content) { + Ok(req) => req, + Err(_) => return Err(ServerError::bad_request("无效的JSON格式")), + }; + + // 验证HTTP方法是否合法 + let valid_methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]; + if !valid_methods.contains(&create_req.http_method.to_uppercase().as_str()) { + return Err(ServerError::bad_request("无效的HTTP方法")); + } + + // 生成新的权限ID(UUID) + let permission_id = Uuid::new_v4().to_string(); + + // 在数据库中插入新权限 + insert_permissions( + &mut db_conn, + &permission_id, + &create_req.url_path, + &create_req.http_method.to_uppercase(), + create_req.description.as_deref(), + ).await.map_err(map_diesel_err)?; + + // 返回成功响应,包含新创建的权限信息 + Ok(json!({ + "permission_id": permission_id, + "url_path": create_req.url_path, + "http_method": create_req.http_method, + })) +} + +/// 更新权限的请求结构体 +#[derive(Debug, Deserialize)] +struct UpdatePermissionRequest { + permission_id: String, // 权限ID + url_path: String, // API路径 + http_method: String, // HTTP方法 + description: Option, // 权限描述(可选) +} + +/// 更新权限的处理函数 +/// +/// # 功能 +/// - 验证请求数据的合法性 +/// - 检查权限是否存在 +/// - 更新权限信息 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含请求数据的服务器请求对象 +pub async fn update_permission_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); + } + let mut db_conn = db_conn.unwrap(); + + // 解析请求体中的JSON数据 + let body = server_request.body.as_ref().unwrap().as_slice(); + let data_content = body.to_vec(); + + // 将JSON数据反序列化为UpdatePermissionRequest结构体 + let update_req: UpdatePermissionRequest = match serde_json::from_slice(&data_content) { + Ok(req) => req, + Err(_) => return Err(ServerError::bad_request("无效的JSON格式")), + }; + + // 验证HTTP方法是否合法 + let valid_methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]; + if !valid_methods.contains(&update_req.http_method.to_uppercase().as_str()) { + return Err(ServerError::bad_request("无效的HTTP方法")); + } + + // 检查权限是否存在 + let permission_exists = check_permissions_by_id( + &mut db_conn, + &update_req.permission_id, + ).await.map_err(map_diesel_err)?; + + if !permission_exists { + return Err(ServerError::not_found("权限不存在")); + } + + // 更新权限信息 + update_permission_in_permissions_by_id( + &mut db_conn, + &update_req.permission_id, + &update_req.url_path, + &update_req.http_method.to_uppercase(), + update_req.description.as_deref(), + ).await.map_err(map_diesel_err)?; + + // 返回成功响应,包含更新的权限ID + Ok(json!({ + "permission_id": update_req.permission_id, + })) +} + +/// 删除权限的处理函数 +/// +/// # 功能 +/// - 检查权限是否存在 +/// - 删除所有与该权限相关的角色-权限关联 +/// - 删除权限记录 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含权限ID的服务器请求对象 +pub async fn delete_permission_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 从请求参数中获取权限ID + let permission_id = server_request.params.get("permission_id").unwrap(); + + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); + } + let mut db_conn = db_conn.unwrap(); + + // 检查权限是否存在 + let permission_exists = check_permissions_by_id( + &mut db_conn, + &permission_id, + ).await.map_err(map_diesel_err)?; + + if !permission_exists { + return Err(ServerError::not_found("权限不存在")); + } + + // 获取所有拥有此权限的角色 + let roles_with_permission = get_roles_by_permission_id( + &mut db_conn, + &permission_id, + ).await.map_err(map_diesel_err)?; + + // 删除所有角色-权限关联记录 + for role in roles_with_permission { + delete_role_permission( + &mut db_conn, + &role.role_id, + &permission_id, + ).await.map_err(map_diesel_err)?; + } + + // 删除权限本身 + delete_from_permissions_by_id( + &mut db_conn, + &permission_id, + ).await.map_err(map_diesel_err)?; + + // 返回被删除的权限ID + Ok(json!({ + "permission_id": permission_id, + })) +} + +/// 获取权限信息的处理函数 +/// +/// # 功能 +/// - 如果提供了权限ID,获取单个权限的详细信息 +/// - 如果没有提供权限ID,获取所有权限的列表 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 服务器请求对象,可能包含权限ID参数 +pub async fn get_permission_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); + } + let mut db_conn = db_conn.unwrap(); + + if let Some(permission_id) = server_request.params.get("permission_id") { + // 如果提供了权限ID,获取单个权限信息 + let res = get_data_from_permissions_by_id( + &mut db_conn, + &permission_id, + ).await.map_err(map_diesel_err)?; + + if res.is_none() { + return Err(ServerError::not_found("权限不存在")) + } + Ok(serde_json::to_value(res)?) + } else { + // 如果没有提供权限ID,获取所有权限列表 + let res = get_all_data_from_permissions( + &mut db_conn + ).await.map_err(map_diesel_err)?; + Ok(serde_json::to_value(res)?) + } +} \ No newline at end of file diff --git a/src/cores/handlers/roles.rs b/src/cores/handlers/roles.rs new file mode 100644 index 0000000000000000000000000000000000000000..293147c19f632c69827f3e7503aa36f003d020e9 --- /dev/null +++ b/src/cores/handlers/roles.rs @@ -0,0 +1,347 @@ +use std::sync::Arc; +use serde::Deserialize; +use serde_json::{json, Value}; +use uuid::Uuid; +use crate::cores::state::AppState; +use crate::db::check_exist::{check_roles_by_id, check_permissions_by_id}; +use crate::db::delete::{delete_from_roles_by_id, delete_role_permission}; +use crate::db::get::{get_all_data_from_roles, get_data_from_roles_by_id, get_permissions_by_role_id}; +use crate::db::insert::{insert_roles, insert_role_permission}; +use crate::db::update::update_role_name_in_roles_by_id; +use fleetmodv2::api_server::{ServerError, ServerRequest, ServerResult}; + +fn map_diesel_err(error: diesel::result::Error) -> ServerError { + ServerError::internal_error(error.to_string().as_str()) +} + +#[derive(Debug, Deserialize)] +struct RoleCreateRequest { + role_name: String, + description: Option, + permission_ids: Option>, +} + +/// 创建角色的处理函数 +pub async fn create_role_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("error getting db conn: {}", e); + return Err(ServerError::internal_error("DB pool error")); + } + let mut db_conn = db_conn.unwrap(); + + // 解析请求体中的JSON数据 + let body = server_request.body.as_ref().unwrap().as_slice(); + let data_content = body.to_vec(); + + // 将JSON数据反序列化为RoleCreateRequest结构体 + let create_req: RoleCreateRequest = match serde_json::from_slice(&data_content) { + Ok(req) => req, + Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), + }; + + // 验证角色名称长度不超过32个字符 + if create_req.role_name.len() > 32 { + return Err(ServerError::bad_request("Role name too long")); + } + + // 如果请求中包含权限ID列表,验证所有权限是否存在 + if let Some(permission_ids) = &create_req.permission_ids { + for permission_id in permission_ids { + let permission_exists = check_permissions_by_id( + &mut db_conn, + permission_id, + ).await.map_err(map_diesel_err)?; + + // 如果有任何权限不存在,返回错误 + if !permission_exists { + return Err(ServerError::bad_request(format!("Permission {} not found", permission_id).as_str())); + } + } + } + + // 生成新的角色ID(UUID) + let role_id = Uuid::new_v4().to_string(); + + // 在数据库中插入新角色 + insert_roles( + &mut db_conn, + &role_id, + &create_req.role_name, + create_req.description.as_deref(), + ).await.map_err(map_diesel_err)?; + + // 如果请求中包含权限ID列表,为角色分配这些权限 + if let Some(permission_ids) = create_req.permission_ids { + for permission_id in permission_ids { + // 在role_permissions表中创建角色-权限关联 + insert_role_permission( + &mut db_conn, + &role_id, + &permission_id, + ).await.map_err(map_diesel_err)?; + } + } + + // 返回成功响应,包含新创建的角色ID和角色名称 + Ok(json!({ + "role_id": role_id, + "role_name": create_req.role_name, + })) +} + +#[derive(Debug, Deserialize)] +struct UpdateRoleRequest { + role_id: String, + role_name: Option, + description: Option, + permission_ids: Option>, +} + +pub async fn update_role_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("error getting db conn: {}", e); + return Err(ServerError::internal_error("DB pool error")); + } + let mut db_conn = db_conn.unwrap(); + + let body = server_request.body.as_ref().unwrap().as_slice(); + let data_content = body.to_vec(); + + let update_req: UpdateRoleRequest = match serde_json::from_slice(&data_content) { + Ok(req) => req, + Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), + }; + + // 如果提供了role_name,验证其长度 + if let Some(ref role_name) = update_req.role_name { + if role_name.len() > 32 { + return Err(ServerError::bad_request("Role name too long")); + } + } + + // 检查角色是否存在 + let role_exists = check_roles_by_id( + &mut db_conn, + &update_req.role_id, + ).await.map_err(map_diesel_err)?; + + if !role_exists { + return Err(ServerError::not_found("Role not found")); + } + + // 验证所有提供的权限是否存在 + if let Some(permission_ids) = &update_req.permission_ids { + for permission_id in permission_ids { + let permission_exists = check_permissions_by_id( + &mut db_conn, + permission_id, + ).await.map_err(map_diesel_err)?; + + if !permission_exists { + return Err(ServerError::bad_request(format!("Permission {} not found", permission_id).as_str())); + } + } + } + + // 如果提供了role_name或description,则更新角色信息 + if update_req.role_name.is_some() || update_req.description.is_some() { + // 从数据库获取当前角色信息 + let current_role = get_data_from_roles_by_id( + &mut db_conn, + &update_req.role_id, + ).await.map_err(map_diesel_err)?; + + let current_role = current_role.ok_or_else(|| ServerError::not_found("Role not found"))?; + + // 如果role_name未提供,使用当前的role_name + let role_name = update_req.role_name.unwrap_or(current_role.role_name); + + // 如果description未提供,保留当前的description + let description = if update_req.description.is_some() { + update_req.description.as_deref() + } else { + current_role.description.as_deref() + }; + + update_role_name_in_roles_by_id( + &mut db_conn, + &update_req.role_id, + &role_name, + description, + ).await.map_err(map_diesel_err)?; + } + + // 获取当前权限 + let current_permissions = get_permissions_by_role_id( + &mut db_conn, + &update_req.role_id, + ).await.map_err(map_diesel_err)?; + + // 如果提供了新的权限列表,更新权限 + if let Some(new_permission_ids) = update_req.permission_ids { + // 删除不在新列表中的权限 + let current_permission_ids: Vec = current_permissions.iter() + .map(|p| p.permission_id.clone()) + .collect(); + + for current_perm_id in current_permission_ids { + if !new_permission_ids.contains(¤t_perm_id) { + delete_role_permission( + &mut db_conn, + &update_req.role_id, + ¤t_perm_id, + ).await.map_err(map_diesel_err)?; + } + } + + // 添加新的权限 + for permission_id in new_permission_ids { + let exists = current_permissions.iter().any(|p| p.permission_id == permission_id); + if !exists { + insert_role_permission( + &mut db_conn, + &update_req.role_id, + &permission_id, + ).await.map_err(map_diesel_err)?; + } + } + } + + Ok(json!({ + "role_id": update_req.role_id, + })) +} + +/// 删除角色的处理函数 +pub async fn delete_role_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 从请求参数中获取角色ID + let role_id = server_request.params.get("role_id").unwrap(); + + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("error getting db conn: {}", e); + return Err(ServerError::internal_error("DB pool error")); + } + let mut db_conn = db_conn.unwrap(); + + // 检查角色是否存在 + let role_exists = check_roles_by_id( + &mut db_conn, + &role_id, + ).await.map_err(map_diesel_err)?; + + if !role_exists { + return Err(ServerError::not_found("Role not found")); + } + + // 获取角色当前的所有权限,以便清理 + let current_permissions = get_permissions_by_role_id( + &mut db_conn, + &role_id, + ).await.map_err(map_diesel_err)?; + + // 删除角色的所有权限关联 + for permission in current_permissions { + delete_role_permission( + &mut db_conn, + &role_id, + &permission.permission_id, + ).await.map_err(map_diesel_err)?; + } + + // 删除角色本身 + delete_from_roles_by_id( + &mut db_conn, + &role_id, + ).await.map_err(map_diesel_err)?; + + // 返回被删除的角色ID + Ok(json!({ + "role_id": role_id, + })) +} + +/// 获取角色信息的处理函数 +pub async fn get_role_handler( + app_state: Arc, + server_request: ServerRequest, +) -> ServerResult { + // 获取数据库连接 + let db_conn = app_state.db_pool.get_connection(); + + if let Err(e) = db_conn { + log::error!("error getting db conn: {}", e); + return Err(ServerError::internal_error("DB pool error")); + } + let mut db_conn = db_conn.unwrap(); + + if let Some(role_id) = server_request.params.get("role_id") { + // 获取单个角色信息 + let role = get_data_from_roles_by_id( + &mut db_conn, + &role_id, + ).await.map_err(map_diesel_err)?; + + if role.is_none() { + return Err(ServerError::not_found("Role not found")) + } + let role = role.unwrap(); + + // 获取该角色的权限列表 + let permissions = get_permissions_by_role_id( + &mut db_conn, + &role_id, + ).await.map_err(map_diesel_err)?; + + // 组合角色信息和权限信息 + Ok(json!({ + "role_id": role.role_id, + "role_name": role.role_name, + "description": role.description, + "created_time": role.created_time, + "updated_time": role.updated_time, + "permissions": permissions + })) + } else { + // 获取所有角色列表 + let roles = get_all_data_from_roles( + &mut db_conn + ).await.map_err(map_diesel_err)?; + + // 为每个角色获取其权限列表 + let mut roles_with_permissions = Vec::new(); + for role in roles { + let permissions = get_permissions_by_role_id( + &mut db_conn, + &role.role_id, + ).await.map_err(map_diesel_err)?; + + roles_with_permissions.push(json!({ + "role_id": role.role_id, + "role_name": role.role_name, + "description": role.description, + "created_time": role.created_time, + "updated_time": role.updated_time, + "permissions": permissions + })); + } + + Ok(json!(roles_with_permissions)) + } +} \ No newline at end of file diff --git a/src/cores/handlers/users.rs b/src/cores/handlers/users.rs index 3327a55c7c5fb0e196c2f875373210dea78e96d2..a363e80fa9d0165ab85f98f07fb6481059ab0d78 100644 --- a/src/cores/handlers/users.rs +++ b/src/cores/handlers/users.rs @@ -3,11 +3,11 @@ use serde::Deserialize; use serde_json::{json, Value}; use uuid::Uuid; use crate::cores::state::AppState; -use crate::db::check_exist::{check_users_by_id, check_users_by_name}; +use crate::db::check_exist::{check_users_by_id, check_users_by_name, check_roles_by_id}; use crate::db::delete::delete_from_users_by_id; use crate::db::get::{get_all_data_from_users, get_data_from_users_by_id, get_id_from_users_by_name, get_password_from_users_by_name}; use crate::db::insert::insert_users; -use crate::db::update::{update_password_in_users_by_id, update_username_in_users_by_id}; +use crate::db::update::{update_password_in_users_by_id, update_username_in_users_by_id, update_role_in_users_by_id}; use fleetmodv2::api_server::{ServerError, ServerRequest, ServerResult}; use crate::utils::password::{hash_password, verify_password}; use crate::utils::token::generate_token; @@ -20,276 +20,374 @@ fn map_diesel_err(error: diesel::result::Error) -> ServerError { struct UserInfoRequest { username: String, password: String, + role_id: String, } +/// 用户注册处理函数 +/// +/// # 功能 +/// - 验证用户名和密码的合法性 +/// - 检查用户名是否已存在 +/// - 验证角色ID是否存在 +/// - 对密码进行哈希处理 +/// - 创建新用户记录 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含用户注册信息的服务器请求对象 pub async fn register_user_handler( app_state: Arc, server_request: ServerRequest, ) -> ServerResult { + // 获取数据库连接 let db_conn = app_state.db_pool.get_connection(); if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); } let mut db_conn = db_conn.unwrap(); + // 解析请求体中的JSON数据 let body = server_request.body.as_ref().unwrap().as_slice(); let data_content = body.to_vec(); + // 将JSON数据反序列化为UserInfoRequest结构体 let register_req: UserInfoRequest = match serde_json::from_slice(&data_content) { Ok(req) => req, - Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), + Err(_) => return Err(ServerError::bad_request("无效的JSON格式")), }; + // 验证用户名和密码长度 if register_req.username.len() > 32 || register_req.password.len() > 32 { - return Err(ServerError::bad_request("Username or password too long")); + return Err(ServerError::bad_request("用户名或密码过长")); } + // 检查用户名是否已存在 let user_exists = check_users_by_name( &mut db_conn, ®ister_req.username, ).await.map_err(map_diesel_err)?; if user_exists { - return Err(ServerError::duplicated("This user is exist, please change the username and try again")); + return Err(ServerError::duplicated("用户名已存在,请更换用户名后重试")); } + // 验证角色ID + let role_exists = check_roles_by_id( + &mut db_conn, + ®ister_req.role_id, + ).await.map_err(map_diesel_err)?; + + if !role_exists { + return Err(ServerError::not_found("角色不存在")); + } + + // 对密码进行哈希处理 let password_hash = match hash_password(®ister_req.password) { Ok(h) => h, - Err(_) => return Err(ServerError::internal_error("Failed to hash password")), + Err(_) => return Err(ServerError::internal_error("密码哈希处理失败")), }; + // 生成用户ID并创建新用户 let user_id = Uuid::new_v4().to_string(); insert_users( &mut db_conn, &user_id, ®ister_req.username, &password_hash, + ®ister_req.role_id, ).await.map_err(map_diesel_err)?; + // 返回成功响应,包含用户信息 Ok(json!({ "user_id": user_id, "user_name": register_req.username, + "role_id": register_req.role_id, })) } +/// 用户登录处理函数 +/// +/// # 功能 +/// - 验证用户名和密码的合法性 +/// - 检查用户是否存在 +/// - 验证密码是否正确 +/// - 生成用户认证令牌(Token) +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池和令牌密钥等资源 +/// - server_request: 包含用户登录信息的服务器请求对象 pub async fn login_user_handler( app_state: Arc, server_request: ServerRequest, ) -> ServerResult { + // 获取数据库连接 let db_conn = app_state.db_pool.get_connection(); if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); } let mut db_conn = db_conn.unwrap(); + // 解析请求体中的JSON数据 let body = server_request.body.as_ref().unwrap().as_slice(); let data_content = body.to_vec(); + // 将JSON数据反序列化为UserInfoRequest结构体 let login_req: UserInfoRequest = match serde_json::from_slice(&data_content) { Ok(req) => req, - Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), + Err(_) => return Err(ServerError::bad_request("无效的JSON格式")), }; + // 验证用户名和密码长度 if login_req.username.len() > 32 || login_req.password.len() > 32 { - return Err(ServerError::bad_request("Username or password too long")); + return Err(ServerError::bad_request("用户名或密码过长")); } + // 检查用户是否存在 let user_exists = check_users_by_name( &mut db_conn, &login_req.username, ).await.map_err(map_diesel_err)?; if !user_exists { - return Err(ServerError::duplicated("This user is not exist, please check username again")); + return Err(ServerError::duplicated("用户不存在,请检查用户名")); } + // 获取数据库中存储的密码哈希值 let password_hash = get_password_from_users_by_name( &mut db_conn, &login_req.username, ).await.map_err(map_diesel_err)?.unwrap(); + // 验证密码是否正确 let verify_password = verify_password(&login_req.password, &password_hash).unwrap(); if verify_password { + // 获取用户ID let user_id = get_id_from_users_by_name( &mut db_conn, &login_req.username, ).await.map_err(map_diesel_err)?.unwrap(); + // 生成用户认证令牌 let user_token = generate_token(&user_id, &login_req.username, &app_state.token_secret).unwrap(); + // 返回用户ID和认证令牌 Ok(json!({ "user_id": user_id, "token": user_token, })) } else { - Err(ServerError::internal_error("Password error, please check username or password again")) + Err(ServerError::internal_error("密码错误,请检查用户名或密码")) } } +/// 更新用户信息请求结构体 #[derive(Debug, Deserialize)] -struct UpdatePasswordRequest { +struct UpdateUserRequest { user_id: String, - password: String, + username: Option, // 可选的新用户名 + password: Option, // 可选的新密码 + role_id: Option, // 可选的新角色ID } -pub async fn update_password_handler( + +/// 更新用户信息处理函数 +/// +/// # 功能 +/// - 支持同时更新用户名、密码和角色ID +/// - 只更新请求中包含的字段 +/// - 验证所有更新字段的合法性 +/// - 检查用户是否存在 +/// - 检查新角色是否存在(如果要更新角色) +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含用户更新信息的服务器请求对象 +pub async fn update_user_handler( app_state: Arc, server_request: ServerRequest, ) -> ServerResult { + // 获取数据库连接 let db_conn = app_state.db_pool.get_connection(); if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); } let mut db_conn = db_conn.unwrap(); + // 解析请求体中的JSON数据 let body = server_request.body.as_ref().unwrap().as_slice(); let data_content = body.to_vec(); - let update_req: UpdatePasswordRequest = match serde_json::from_slice(&data_content) { + // 将JSON数据反序列化为UpdateUserRequest结构体 + let update_req: UpdateUserRequest = match serde_json::from_slice(&data_content) { Ok(req) => req, - Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), + Err(_) => return Err(ServerError::bad_request("无效的JSON格式")), }; - if update_req.password.len() > 32 { - return Err(ServerError::bad_request("password too long")); - } - + // 检查用户是否存在 let user_exists = check_users_by_id( &mut db_conn, &update_req.user_id, ).await.map_err(map_diesel_err)?; if !user_exists { - return Err(ServerError::duplicated("This user is not exist, please check user_id again")); + return Err(ServerError::not_found("用户不存在")); } - let password_hash = match hash_password(&update_req.password) { - Ok(h) => h, - Err(_) => return Err(ServerError::internal_error("Failed to hash password")), - }; - - update_password_in_users_by_id( - &mut db_conn, - &update_req.user_id, - &password_hash, - ).await.map_err(map_diesel_err)?; - Ok(json!({ - "user_id": update_req.user_id, - })) -} - -#[derive(Debug, Deserialize)] -struct UpdateUsernameRequest { - user_id: String, - username: String, -} -pub async fn update_username_handler( - app_state: Arc, - server_request: ServerRequest, -) -> ServerResult { - let db_conn = app_state.db_pool.get_connection(); - - if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + // 如果要更新用户名 + if let Some(new_username) = &update_req.username { + // 验证用户名长度 + if new_username.len() > 32 { + return Err(ServerError::bad_request("用户名过长")); + } + // 更新用户名 + update_username_in_users_by_id( + &mut db_conn, + &update_req.user_id, + new_username, + ).await.map_err(map_diesel_err)?; } - let mut db_conn = db_conn.unwrap(); - - let body = server_request.body.as_ref().unwrap().as_slice(); - let data_content = body.to_vec(); - let update_req: UpdateUsernameRequest = match serde_json::from_slice(&data_content) { - Ok(req) => req, - Err(_) => return Err(ServerError::bad_request("Invalid JSON format")), - }; - - if update_req.username.len() > 32 { - return Err(ServerError::bad_request("username too long")); + // 如果要更新密码 + if let Some(new_password) = &update_req.password { + // 验证密码长度 + if new_password.len() > 32 { + return Err(ServerError::bad_request("密码过长")); + } + // 对新密码进行哈希处理 + let password_hash = match hash_password(new_password) { + Ok(h) => h, + Err(_) => return Err(ServerError::internal_error("密码哈希处理失败")), + }; + // 更新密码 + update_password_in_users_by_id( + &mut db_conn, + &update_req.user_id, + &password_hash, + ).await.map_err(map_diesel_err)?; } - let user_exists = check_users_by_id( - &mut db_conn, - &update_req.user_id, - ).await.map_err(map_diesel_err)?; + // 如果要更新角色 + if let Some(new_role_id) = &update_req.role_id { + // 检查新角色是否存在 + let role_exists = check_roles_by_id( + &mut db_conn, + new_role_id, + ).await.map_err(map_diesel_err)?; - if !user_exists { - return Err(ServerError::duplicated("This user is not exist, please check user_id again")); + if !role_exists { + return Err(ServerError::not_found("角色不存在")); + } + // 更新角色 + update_role_in_users_by_id( + &mut db_conn, + &update_req.user_id, + new_role_id, + ).await.map_err(map_diesel_err)?; } - update_username_in_users_by_id( + // 获取更新后的用户信息 + let updated_user = get_data_from_users_by_id( &mut db_conn, &update_req.user_id, - &update_req.username, ).await.map_err(map_diesel_err)?; - Ok(json!({ - "user_id": update_req.user_id, - })) + match updated_user { + Some(user_data) => { + // 直接序列化整个结构体,因为它实现了 Serialize trait + Ok(serde_json::to_value(user_data)?) + } + None => Err(ServerError::internal_error("获取更新后的用户信息失败")) + } } +/// 删除用户处理函数 +/// +/// # 功能 +/// - 检查用户是否存在 +/// - 删除用户记录 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 包含用户ID的服务器请求对象 pub async fn delete_user_handler( app_state: Arc, server_request: ServerRequest, ) -> ServerResult { + // 从请求参数中获取用户ID let user_id = server_request.params.get("user_id").unwrap(); + // 获取数据库连接 let db_conn = app_state.db_pool.get_connection(); if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); } let mut db_conn = db_conn.unwrap(); + // 检查用户是否存在 let user_exists = check_users_by_id( &mut db_conn, &user_id, ).await.map_err(map_diesel_err)?; if !user_exists { - return Err(ServerError::duplicated("This user is not exist, please check user_id again")); + return Err(ServerError::duplicated("用户不存在,请检查用户ID")); } + // 删除用户记录 delete_from_users_by_id( &mut db_conn, &user_id, ).await.map_err(map_diesel_err)?; + // 返回被删除的用户ID Ok(json!({ "user_id": user_id, })) } +/// 获取用户信息处理函数 +/// +/// # 功能 +/// - 如果提供了用户ID,获取单个用户的详细信息 +/// - 如果没有提供用户ID,获取所有用户的列表 +/// +/// # 参数 +/// - app_state: 应用程序状态,包含数据库连接池等资源 +/// - server_request: 服务器请求对象,可能包含用户ID参数 pub async fn get_user_handler( app_state: Arc, server_request: ServerRequest, ) -> ServerResult { - + // 获取数据库连接 let db_conn = app_state.db_pool.get_connection(); if let Err(e) = db_conn { - log::error!("error getting db conn: {}", e); - return Err(ServerError::internal_error("DB pool error")); + log::error!("获取数据库连接失败: {}", e); + return Err(ServerError::internal_error("数据库连接池错误")); } let mut db_conn = db_conn.unwrap(); if let Some(user_id) = server_request.params.get("user_id") { + // 如果提供了用户ID,获取单个用户信息 let res = get_data_from_users_by_id( &mut db_conn, &user_id, ).await.map_err(map_diesel_err)?; if res.is_none() { - return Err(ServerError::not_found("Can not find this user")) + return Err(ServerError::not_found("找不到该用户")) } Ok(serde_json::to_value(res)?) } else { + // 如果没有提供用户ID,获取所有用户列表 let res = get_all_data_from_users( &mut db_conn ).await.map_err(map_diesel_err)?; diff --git a/src/cores/router.rs b/src/cores/router.rs index c01ae978bb33b469ab79e9c18d0311e4fa78d13f..9fc45af32dc9af4a9683532c01e86d3e3446e29d 100644 --- a/src/cores/router.rs +++ b/src/cores/router.rs @@ -28,11 +28,23 @@ pub enum RouterKey { // users router key UsersRegister, UsersLogin, - UsersUpdatePassword, - UsersUpdateUsername, + UsersUpdate, UsersDeleteUser, UsersGetUser, + // roles router key + RolesCreate, + RolesUpdate, + RolesDelete, + RolesGet, + RolesUpdatePermissions, + + // permissions router key + PermissionsCreate, + PermissionsUpdate, + PermissionsDelete, + PermissionsGet, + // security router key SecurityInsertMetric, SecurityListMetric, @@ -234,14 +246,8 @@ impl Router { add_route!( router, - RouterKey::UsersUpdatePassword, - handlers::users::update_password_handler - ); - - add_route!( - router, - RouterKey::UsersUpdateUsername, - handlers::users::update_username_handler + RouterKey::UsersUpdate, + handlers::users::update_user_handler ); add_route!( @@ -257,6 +263,66 @@ impl Router { ); } + // roles routers + { + add_route!( + router, + RouterKey::RolesCreate, + handlers::roles::create_role_handler + ); + + add_route!( + router, + RouterKey::RolesUpdate, + handlers::roles::update_role_handler + ); + + add_route!( + router, + RouterKey::RolesDelete, + handlers::roles::delete_role_handler + ); + + add_route!( + router, + RouterKey::RolesGet, + handlers::roles::get_role_handler + ); + + add_route!( + router, + RouterKey::RolesUpdatePermissions, + handlers::roles::update_role_handler + ); + } + + // permissions routers + { + add_route!( + router, + RouterKey::PermissionsCreate, + handlers::permissions::create_permission_handler + ); + + add_route!( + router, + RouterKey::PermissionsUpdate, + handlers::permissions::update_permission_handler + ); + + add_route!( + router, + RouterKey::PermissionsDelete, + handlers::permissions::delete_permission_handler + ); + + add_route!( + router, + RouterKey::PermissionsGet, + handlers::permissions::get_permission_handler + ); + } + // security routers { add_route!( diff --git a/src/cores/servers/actix_web/mod.rs b/src/cores/servers/actix_web/mod.rs index e6e78800d1e7d21646d790e9a7244786b394f8e3..74a1e431639d352db16e3cae89a27246456ff722 100644 --- a/src/cores/servers/actix_web/mod.rs +++ b/src/cores/servers/actix_web/mod.rs @@ -99,6 +99,16 @@ macro_rules! init_app { name: "Users".to_string(), description: Some("Users routes".to_string()), external_docs: None, + }, + Tag { + name: "Roles".to_string(), + description: Some("Role management routes".to_string()), + external_docs: None, + }, + Tag { + name: "Permissions".to_string(), + description: Some("Permission management routes".to_string()), + external_docs: None, } ]; App::new() @@ -118,6 +128,8 @@ macro_rules! init_app { .configure(consensus::configure_routes) .configure(payload::configure_routes) .configure(network_status::configure_routes) + .configure(roles::configure_routes) + .configure(permissions::configure_routes) .with_json_spec_at("/api/spec/v2") .with_swagger_ui_at("/api/doc") .default_service(actix_web::web::route().to(default_service)) @@ -506,14 +518,13 @@ pub mod users { pub fn configure_routes(app: &mut web::ServiceConfig) { app.service( - web::scope("/users") - .service(register_new_user) + web::scope("/auth/users") + .service(register_user) .service(login_user) - .service(update_user_password) - .service(update_user_username) + .service(update_user) .service(delete_user) .service(get_user) - .service(list_user), + .service(list_users), ); } @@ -522,9 +533,10 @@ pub mod users { pub user_id: String, } + // 注册用户 define_json_response_method!( body_required - register_new_user, + register_user, post, "/register", (), @@ -533,6 +545,7 @@ pub mod users { "Users" ); + // 用户登录 define_json_response_method!( body_required login_user, @@ -544,50 +557,43 @@ pub mod users { "Users" ); + // 更新用户 define_json_response_method!( body_required - update_user_password, - put, - "update/password", - (), - (), - RouterKey::UsersUpdatePassword, - "Users" - ); - - define_json_response_method!( - body_required - update_user_username, + update_user, put, - "/update/username", - (), + "/{user_id}", + UserIdPath, (), - RouterKey::UsersUpdateUsername, + RouterKey::UsersUpdate, "Users" ); + // 删除用户 define_json_response_method!( body_unrequired delete_user, delete, - "/delete/{user_id}", + "/{user_id}", UserIdPath, (), RouterKey::UsersDeleteUser, "Users" ); + // 获取用户列表 define_json_response_method!( body_unrequired - list_user, + list_users, get, - "/get", + "/list", (), (), RouterKey::UsersGetUser, "Users" ); + // 获取单个用户详细信息 define_json_response_method!( body_unrequired get_user, @@ -599,6 +605,173 @@ pub mod users { "Users" ); } +pub mod roles { + use super::*; + + pub fn configure_routes(app: &mut web::ServiceConfig) { + app.service( + web::scope("/auth/roles") + .service(create_role) + .service(update_role) + .service(delete_role) + .service(get_role) + .service(list_roles) + .service(update_role_permissions), + ); + } + + #[derive(Serialize, Deserialize, Apiv2Schema)] + pub struct RoleIdPath { + pub role_id: String, + } + + // 创建角色 + define_json_response_method!( + body_required + create_role, + post, + "/create", + (), + (), + RouterKey::RolesCreate, + "Roles" + ); + + // 更新角色 + define_json_response_method!( + body_required + update_role, + put, + "/{role_id}", + RoleIdPath, + (), + RouterKey::RolesUpdate, + "Roles" + ); + + // 删除角色 + define_json_response_method!( + body_unrequired + delete_role, + delete, + "/{role_id}", + RoleIdPath, + (), + RouterKey::RolesDelete, + "Roles" + ); + + // 获取单个角色详细信息 + define_json_response_method!( + body_unrequired + get_role, + get, + "/get/{role_id}", + RoleIdPath, + (), + RouterKey::RolesGet, + "Roles" + ); + + // 获取角色列表 + define_json_response_method!( + body_unrequired + list_roles, + get, + "/list", + (), + (), + RouterKey::RolesGet, + "Roles" + ); + + // 更新角色权限 + define_json_response_method!( + body_required + update_role_permissions, + put, + "/{role_id}/permissions", + RoleIdPath, + (), + RouterKey::RolesUpdatePermissions, + "Roles" + ); +} + +pub mod permissions { + use super::*; + + pub fn configure_routes(app: &mut web::ServiceConfig) { + app.service( + web::scope("/auth/permissions") + .service(create_permission) + .service(update_permission) + .service(delete_permission) + .service(get_permission) + .service(list_permissions), + ); + } + + #[derive(Serialize, Deserialize, Apiv2Schema)] + pub struct PermissionIdPath { + pub permission_id: String, + } + // 创建权限 + define_json_response_method!( + body_required + create_permission, + post, + "/create", + (), + (), + RouterKey::PermissionsCreate, + "Permissions" + ); + // 更新权限 + define_json_response_method!( + body_required + update_permission, + put, + "/{permission_id}", + PermissionIdPath, + (), + RouterKey::PermissionsUpdate, + "Permissions" + ); + // 删除权限 + define_json_response_method!( + body_unrequired + delete_permission, + delete, + "/{permission_id}", + PermissionIdPath, + (), + RouterKey::PermissionsDelete, + "Permissions" + ); + // 获取单个权限详细信息 + define_json_response_method!( + body_unrequired + get_permission, + get, + "/get/{permission_id}", + PermissionIdPath, + (), + RouterKey::PermissionsGet, + "Permissions" + ); + // 获取权限列表 + define_json_response_method!( + body_unrequired + list_permissions, + get, + "/list", + (), + (), + RouterKey::PermissionsGet, + "Permissions" + ); +} pub mod security { use super::*; @@ -1077,7 +1250,7 @@ pub mod payload { web::scope("/payload") .service(insert_payload) .service(get_payload) - .service(delete_payload) + .service(delete_payload), ); } @@ -1113,7 +1286,6 @@ pub mod payload { RouterKey::DeletePayload, "Payload" ); - } pub mod network_status { diff --git a/src/db/check_exist.rs b/src/db/check_exist.rs index 2c94976b3a80279102fbcdf0d30af03f67599233..0538e7d0ec4db864cc0df0bee400ecec757e06c7 100644 --- a/src/db/check_exist.rs +++ b/src/db/check_exist.rs @@ -7,6 +7,15 @@ use crate::db::db::DbConnection; **/ use diesel::{ExpressionMethods, QueryDsl, QueryResult, RunQueryDsl}; use crate::schema::routermgr; +use diesel::sql_types::Text; +use diesel::deserialize::QueryableByName; +use diesel::sql_types::{BigInt}; + +#[derive(QueryableByName, Debug)] +struct CountResult { + #[diesel(sql_type = BigInt)] + count: i64, +} // 查询 metadata 表中的 plural 是否存在,并检查 namespace 要求是否满足 pub async fn check_metadata( @@ -362,4 +371,146 @@ pub async fn check_routermgr( .filter(|&&x| x > 0) .count(); Ok(positive_count >= 2) +} + +pub async fn check_roles_by_id( + conn: &mut DbConnection, + role_id: &str, +) -> QueryResult { + use crate::schema::roles::dsl as roles_dsl; + use crate::schema::roles_replica1::dsl as replica1_dsl; + use crate::schema::roles_replica2::dsl as replica2_dsl; + use diesel::dsl::count_star; + + let count; + let count_replica1; + let count_replica2; + match conn { + DbConnection::Pg(pg_conn) => { + count = roles_dsl::roles + .filter(roles_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica1 = replica1_dsl::roles_replica1 + .filter(replica1_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica2 = replica2_dsl::roles_replica2 + .filter(replica2_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + count = roles_dsl::roles + .filter(roles_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica1 = replica1_dsl::roles_replica1 + .filter(replica1_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica2 = replica2_dsl::roles_replica2 + .filter(replica2_dsl::role_id.eq(role_id)) + .select(count_star()) + .first::(sqlite_conn)?; + } + } + let positive_count = [count, count_replica1, count_replica2] + .iter() + .filter(|&&x| x > 0) + .count(); + Ok(positive_count >= 2) +} + +pub async fn check_permissions_by_id( + conn: &mut DbConnection, + permission_id: &str, +) -> QueryResult { + use crate::schema::permissions::dsl as permissions_dsl; + use crate::schema::permissions_replica1::dsl as replica1_dsl; + use crate::schema::permissions_replica2::dsl as replica2_dsl; + use diesel::dsl::count_star; + + let count; + let count_replica1; + let count_replica2; + match conn { + DbConnection::Pg(pg_conn) => { + count = permissions_dsl::permissions + .filter(permissions_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica1 = replica1_dsl::permissions_replica1 + .filter(replica1_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica2 = replica2_dsl::permissions_replica2 + .filter(replica2_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + count = permissions_dsl::permissions + .filter(permissions_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica1 = replica1_dsl::permissions_replica1 + .filter(replica1_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica2 = replica2_dsl::permissions_replica2 + .filter(replica2_dsl::permission_id.eq(permission_id)) + .select(count_star()) + .first::(sqlite_conn)?; + } + } + let positive_count = [count, count_replica1, count_replica2] + .iter() + .filter(|&&x| x > 0) + .count(); + Ok(positive_count >= 2) +} + +pub async fn check_role_permission_exists( + conn: &mut DbConnection, + role_id: &str, + permission_id: &str, +) -> QueryResult { + let tables = ["role_permissions", "role_permissions_replica1", "role_permissions_replica2"]; + let mut counts = Vec::new(); + + for table in tables { + let query = format!( + "SELECT COUNT(*) as count FROM {} WHERE role_id = $1 AND permission_id = $2", + table + ); + + let count: CountResult = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .get_result(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + let query = query.replace("$1", "?").replace("$2", "?"); + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .get_result(sqlite_conn)? + } + }; + counts.push(count.count); + } + + let positive_count = counts.iter().filter(|&&x| x > 0).count(); + Ok(positive_count >= 2) } \ No newline at end of file diff --git a/src/db/delete.rs b/src/db/delete.rs index 81a8b2667d009dd9a88225eaa17e977499ea170c..6cadeac43bea21daa7d8388d98301784e308ff95 100644 --- a/src/db/delete.rs +++ b/src/db/delete.rs @@ -230,4 +230,103 @@ pub async fn delete_from_payload( // 如果至少有两个表进行了删除,则返回 true Ok(total_rows_affected > 1) +} + +pub async fn delete_from_roles_by_id( + conn: &mut DbConnection, + role_id: &str, +) -> QueryResult<()> { + let tables = ["roles", "roles_replica1", "roles_replica2"]; + + for table in tables { + let query = format!( + "DELETE FROM {} WHERE role_id = $1", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "DELETE FROM {} WHERE role_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(role_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn delete_from_permissions_by_id( + conn: &mut DbConnection, + permission_id: &str, +) -> QueryResult<()> { + let tables = ["permissions", "permissions_replica1", "permissions_replica2"]; + + for table in tables { + let query = format!( + "DELETE FROM {} WHERE permission_id = $1", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(permission_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "DELETE FROM {} WHERE permission_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(permission_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn delete_role_permission( + conn: &mut DbConnection, + role_id: &str, + permission_id: &str, +) -> QueryResult<()> { + let tables = ["role_permissions", "role_permissions_replica1", "role_permissions_replica2"]; + + for table in tables { + let query = format!( + "DELETE FROM {} WHERE role_id = $1 AND permission_id = $2", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "DELETE FROM {} WHERE role_id = ? AND permission_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) } \ No newline at end of file diff --git a/src/db/get.rs b/src/db/get.rs index 3f26c8e760720a830e11bcded9a1f5caeb0fb854..48bfe10c92e875f82c7ab1756f90cd33abb27fce 100644 --- a/src/db/get.rs +++ b/src/db/get.rs @@ -516,13 +516,13 @@ fn get_id_from_users_primary_by_name( #[derive(Debug, QueryableByName, Serialize, Clone, PartialEq, Eq, Hash)] pub struct UserInfoResult { #[diesel(sql_type = Text)] - user_id: String, + pub user_id: String, #[diesel(sql_type = Text)] - username: String, + pub username: String, #[diesel(sql_type = Nullable)] - created_time: Option, + pub created_time: Option, #[diesel(sql_type = Nullable)] - updated_time: Option, + pub updated_time: Option, } pub async fn get_all_data_from_users(conn: &mut DbConnection) -> QueryResult> { @@ -1161,4 +1161,304 @@ fn get_all_from_payload_primary( .get_results::(sqlite_conn) } } +} +#[derive(QueryableByName, Debug, Hash, Eq, PartialEq, Serialize)] +pub struct RoleInfoResult { + #[diesel(sql_type = Text)] + pub role_id: String, + #[diesel(sql_type = Text)] + pub role_name: String, + #[diesel(sql_type = Nullable)] + pub description: Option, + #[diesel(sql_type = Nullable)] + pub created_time: Option, + #[diesel(sql_type = Nullable)] + pub updated_time: Option, +} + +pub async fn get_all_data_from_roles(conn: &mut DbConnection) -> QueryResult> { + let tables = ["roles", "roles_replica1", "roles_replica2"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT role_id, role_name, description, created_time, updated_time FROM {}", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query).get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + diesel::sql_query(&query).get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + if counter.values().any(|&count| count >= 2) { + let majority: Vec<_> = counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .collect(); + return Ok(majority); + } + + get_all_data_from_roles_primary(conn) +} + +fn get_all_data_from_roles_primary( + conn: &mut DbConnection, +) -> QueryResult> { + let query = "SELECT role_id, role_name, description, created_time, updated_time FROM roles"; + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(query) + .get_results::(pg_conn) + } + DbConnection::Sqlite(sqlite_conn) => { + diesel::sql_query(query) + .get_results::(sqlite_conn) + } + } +} + +pub async fn get_data_from_roles_by_id( + conn: &mut DbConnection, + role_id: &str, +) -> QueryResult> { + let tables = ["roles", "roles_replica1", "roles_replica2"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT role_id, role_name, description, created_time, updated_time FROM {} WHERE role_id = $1", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + let query = query.replace("$1", "?"); + diesel::sql_query(&query) + .bind::(role_id) + .get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + Ok(counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .next()) +} + +#[derive(QueryableByName, Debug, Hash, Eq, PartialEq, Serialize)] +pub struct PermissionInfoResult { + #[diesel(sql_type = Text)] + pub permission_id: String, + #[diesel(sql_type = Text)] + pub url_path: String, + #[diesel(sql_type = Text)] + pub http_method: String, + #[diesel(sql_type = Nullable)] + pub description: Option, + #[diesel(sql_type = Nullable)] + pub created_time: Option, + #[diesel(sql_type = Nullable)] + pub updated_time: Option, +} + +pub async fn get_all_data_from_permissions(conn: &mut DbConnection) -> QueryResult> { + let tables = ["permissions", "permissions_replica1", "permissions_replica2"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT permission_id, url_path, http_method, description, created_time, updated_time FROM {}", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query).get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + diesel::sql_query(&query).get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + if counter.values().any(|&count| count >= 2) { + let majority: Vec<_> = counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .collect(); + return Ok(majority); + } + + get_all_data_from_permissions_primary(conn) +} + +fn get_all_data_from_permissions_primary( + conn: &mut DbConnection, +) -> QueryResult> { + let query = "SELECT permission_id, url_path, http_method, description, created_time, updated_time FROM permissions"; + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(query) + .get_results::(pg_conn) + } + DbConnection::Sqlite(sqlite_conn) => { + diesel::sql_query(query) + .get_results::(sqlite_conn) + } + } +} +pub async fn get_data_from_permissions_by_id( + conn: &mut DbConnection, + permission_id: &str, +) -> QueryResult> { + let tables = ["permissions", "permissions_replica1", "permissions_replica2"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT permission_id, url_path, http_method, description, created_time, updated_time FROM {} WHERE permission_id = $1", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(permission_id) + .get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + let query = query.replace("$1", "?"); + diesel::sql_query(&query) + .bind::(permission_id) + .get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + Ok(counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .next()) +} + +pub async fn get_permissions_by_role_id( + conn: &mut DbConnection, + role_id: &str, +) -> QueryResult> { + let tables = ["role_permissions rp", "role_permissions_replica1 rp", "role_permissions_replica2 rp"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT p.permission_id, p.url_path, p.http_method, p.description, p.created_time, p.updated_time + FROM {} + JOIN permissions p ON rp.permission_id = p.permission_id + WHERE rp.role_id = $1", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + let query = query.replace("$1", "?"); + diesel::sql_query(&query) + .bind::(role_id) + .get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + Ok(counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .collect()) +} + +#[derive(QueryableByName, Debug, Hash, Eq, PartialEq)] +pub struct RolePermissionResult { + #[diesel(sql_type = Text)] + pub role_id: String, +} + +/// 获取拥有指定权限的所有角色 +pub async fn get_roles_by_permission_id( + conn: &mut DbConnection, + permission_id: &str, +) -> QueryResult> { + let tables = ["role_permissions rp", "role_permissions_replica1 rp", "role_permissions_replica2 rp"]; + let mut counter: HashMap = HashMap::new(); + + for table in tables { + let query = format!( + "SELECT rp.role_id FROM {} WHERE rp.permission_id = $1", + table + ); + + let rows: Vec = match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(permission_id) + .get_results(pg_conn)? + } + DbConnection::Sqlite(sqlite_conn) => { + let query = query.replace("$1", "?"); + diesel::sql_query(&query) + .bind::(permission_id) + .get_results(sqlite_conn)? + } + }; + + for row in rows { + *counter.entry(row).or_insert(0) += 1; + } + } + + Ok(counter + .into_iter() + .filter(|(_, count)| *count >= 2) + .map(|(data, _)| data) + .collect()) } \ No newline at end of file diff --git a/src/db/insert.rs b/src/db/insert.rs index b74988d58cd52eb9bbd24e7ebd8980059e8cec71..04a8123421481c4ed86a267f4beeb4e47eb80b11 100644 --- a/src/db/insert.rs +++ b/src/db/insert.rs @@ -6,9 +6,11 @@ use crate::db::db::DbConnection; * **/ use chrono::Utc; -use diesel::sql_types::{Integer, Text}; +use diesel::sql_types::{Integer, Text, Nullable}; use diesel::{sql_query, Connection, PgConnection, QueryResult, RunQueryDsl, SqliteConnection}; use serde_json::Value; +use crate::db::check_exist::check_role_permission_exists; + fn insert_metadata_in_transaction_pg( transaction: &mut PgConnection, @@ -215,6 +217,7 @@ fn insert_users_in_transaction_pg( user_id: &str, username: &str, password: &str, + role_id: &str, ) -> QueryResult<()> { // 表列表 let table_array: [&str; 3] = ["users", "users_replica1", "users_replica2"]; @@ -222,8 +225,8 @@ fn insert_users_in_transaction_pg( for table_name in table_array { // 使用参数绑定构建插入查询 let insert_users_query = format!( - "INSERT INTO {} (user_id, username, password, created_time, updated_time) - VALUES ($1, $2, $3, $4, $5) + "INSERT INTO {} (user_id, username, password, role_id, created_time, updated_time) + VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT DO NOTHING;", table_name ); @@ -233,6 +236,7 @@ fn insert_users_in_transaction_pg( .bind::(user_id) .bind::(username) .bind::(password) + .bind::(role_id) .bind::(Utc::now().naive_utc().to_string()) // 创建时间 .bind::(Utc::now().naive_utc().to_string()) // 更新时间 .execute(transaction)?; @@ -246,13 +250,14 @@ fn insert_users_in_transaction_sqlite( user_id: &str, username: &str, password: &str, + role_id: &str, ) -> QueryResult<()> { let table_array: [&str; 3] = ["users", "users_replica1", "users_replica2"]; for table_name in table_array { let insert_users_query = format!( - "INSERT OR IGNORE INTO {} (user_id, username, password, created_time, updated_time) - VALUES (?, ?, ?, ?, ?);", + "INSERT OR IGNORE INTO {} (user_id, username, password, role_id, created_time, updated_time) + VALUES (?, ?, ?, ?, ?, ?);", table_name ); @@ -260,6 +265,7 @@ fn insert_users_in_transaction_sqlite( .bind::(user_id) .bind::(username) .bind::(password) + .bind::(role_id) .bind::(Utc::now().naive_utc().to_string()) .bind::(Utc::now().naive_utc().to_string()) .execute(transaction)?; @@ -273,6 +279,7 @@ pub async fn insert_users( user_id: &str, username: &str, password: &str, + role_id: &str, ) -> QueryResult<()> { match conn { DbConnection::Pg(pg_conn) => pg_conn.transaction(|transaction| { @@ -281,6 +288,7 @@ pub async fn insert_users( user_id, username, password, + role_id, ) }), DbConnection::Sqlite(sqlite_conn) => sqlite_conn.transaction(|transaction| { @@ -289,6 +297,7 @@ pub async fn insert_users( user_id, username, password, + role_id, ) }), } @@ -574,4 +583,133 @@ pub async fn insert_payload( } .expect("unknow conn in insert_payload"); Ok(()) +} + +pub async fn insert_roles( + conn: &mut DbConnection, + role_id: &str, + role_name: &str, + description: Option<&str>, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["roles", "roles_replica1", "roles_replica2"]; + + for table in tables { + let query = format!( + "INSERT INTO {} (role_id, role_name, description, created_time, updated_time) VALUES ($1, $2, $3, $4, $5)", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .bind::(role_name) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "INSERT INTO {} (role_id, role_name, description, created_time, updated_time) VALUES (?, ?, ?, ?, ?)", + table + ); + diesel::sql_query(&query) + .bind::(role_id) + .bind::(role_name) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn insert_permissions( + conn: &mut DbConnection, + permission_id: &str, + url_path: &str, + http_method: &str, + description: Option<&str>, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["permissions", "permissions_replica1", "permissions_replica2"]; + + for table in tables { + let query = format!( + "INSERT INTO {} (permission_id, url_path, http_method, description, created_time, updated_time) VALUES ($1, $2, $3, $4, $5, $6)", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(permission_id) + .bind::(url_path) + .bind::(http_method) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "INSERT INTO {} (permission_id, url_path, http_method, description, created_time, updated_time) VALUES (?, ?, ?, ?, ?, ?)", + table + ); + diesel::sql_query(&query) + .bind::(permission_id) + .bind::(url_path) + .bind::(http_method) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn insert_role_permission( + conn: &mut DbConnection, + role_id: &str, + permission_id: &str, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["role_permissions", "role_permissions_replica1", "role_permissions_replica2"]; + + for table in tables { + let query = format!( + "INSERT INTO {} (role_id, permission_id, created_time, updated_time) VALUES ($1, $2, $3, $4)", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "INSERT INTO {} (role_id, permission_id, created_time, updated_time) VALUES (?, ?, ?, ?)", + table + ); + diesel::sql_query(&query) + .bind::(role_id) + .bind::(permission_id) + .bind::(¤t_time) + .bind::(¤t_time) + .execute(sqlite_conn)?; + } + } + } + Ok(()) } \ No newline at end of file diff --git a/src/db/update.rs b/src/db/update.rs index 5583eb449ff1401912c19f0c3cec83df55dfbf53..30c3bb2491aa1ab3673537d8309fdf122b505dd5 100644 --- a/src/db/update.rs +++ b/src/db/update.rs @@ -7,7 +7,7 @@ use crate::db::db::DbConnection; **/ use diesel::{QueryResult, RunQueryDsl}; use serde_json::Value; -use diesel::sql_types::{Integer, Text}; +use diesel::sql_types::{Integer, Text, Nullable}; use chrono::Utc; pub async fn update_data_in_kine( @@ -248,4 +248,128 @@ pub async fn update_routermgr( // 如果至少有两个表更新成功,则返回 true Ok(total_rows_affected > 1) +} + + +pub async fn update_role_name_in_roles_by_id( + conn: &mut DbConnection, + role_id: &str, + role_name: &str, + description: Option<&str>, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["roles", "roles_replica1", "roles_replica2"]; + + for table in tables { + let query = format!( + "UPDATE {} SET role_name = $1, description = $2, updated_time = $3 WHERE role_id = $4", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_name) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(role_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "UPDATE {} SET role_name = ?, description = ?, updated_time = ? WHERE role_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(role_name) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(role_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn update_permission_in_permissions_by_id( + conn: &mut DbConnection, + permission_id: &str, + url_path: &str, + http_method: &str, + description: Option<&str>, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["permissions", "permissions_replica1", "permissions_replica2"]; + + for table in tables { + let query = format!( + "UPDATE {} SET url_path = $1, http_method = $2, description = $3, updated_time = $4 WHERE permission_id = $5", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(url_path) + .bind::(http_method) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(permission_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "UPDATE {} SET url_path = ?, http_method = ?, description = ?, updated_time = ? WHERE permission_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(url_path) + .bind::(http_method) + .bind::, _>(description) + .bind::(¤t_time) + .bind::(permission_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) +} + +pub async fn update_role_in_users_by_id( + conn: &mut DbConnection, + user_id: &str, + role_id: &str, +) -> QueryResult<()> { + let current_time = Utc::now().naive_utc().to_string(); + let tables = ["users", "users_replica1", "users_replica2"]; + + for table in tables { + let query = format!( + "UPDATE {} SET role_id = $1, updated_time = $2 WHERE user_id = $3", + table + ); + + match conn { + DbConnection::Pg(pg_conn) => { + diesel::sql_query(&query) + .bind::(role_id) + .bind::(¤t_time) + .bind::(user_id) + .execute(pg_conn)?; + } + DbConnection::Sqlite(sqlite_conn) => { + let query = format!( + "UPDATE {} SET role_id = ?, updated_time = ? WHERE user_id = ?", + table + ); + diesel::sql_query(&query) + .bind::(role_id) + .bind::(¤t_time) + .bind::(user_id) + .execute(sqlite_conn)?; + } + } + } + Ok(()) } \ No newline at end of file diff --git a/src/schema.rs b/src/schema.rs index 0f94c5f99069852f7345d6482307626c3a8d1efd..f481005eae0e8ce9c939f128eeb27e083356fa7f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -164,6 +164,96 @@ diesel::table! { updated_at -> Text, } } +diesel::table! { + permissions (permission_id) { + permission_id -> Text, + url_path -> Text, + http_method -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + permissions_replica1 (permission_id) { + permission_id -> Text, + url_path -> Text, + http_method -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + permissions_replica2 (permission_id) { + permission_id -> Text, + url_path -> Text, + http_method -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + role_permissions (role_id, permission_id) { + role_id -> Text, + permission_id -> Text, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + role_permissions_replica1 (role_id, permission_id) { + role_id -> Text, + permission_id -> Text, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + role_permissions_replica2 (role_id, permission_id) { + role_id -> Text, + permission_id -> Text, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + roles (role_id) { + role_id -> Text, + role_name -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + roles_replica1 (role_id) { + role_id -> Text, + role_name -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + +diesel::table! { + roles_replica2 (role_id) { + role_id -> Text, + role_name -> Text, + description -> Nullable, + created_time -> Nullable, + updated_time -> Nullable, + } +} + diesel::table! { routermgr (msg_from, dst_ip, prefixLen) { @@ -239,6 +329,7 @@ diesel::table! { user_id -> Text, username -> Text, password -> Text, + role_id -> Text, created_time -> Nullable, updated_time -> Nullable, } @@ -249,6 +340,7 @@ diesel::table! { user_id -> Text, username -> Text, password -> Text, + role_id -> Text, created_time -> Nullable, updated_time -> Nullable, } @@ -259,11 +351,23 @@ diesel::table! { user_id -> Text, username -> Text, password -> Text, + role_id -> Text, created_time -> Nullable, updated_time -> Nullable, } } +diesel::joinable!(role_permissions -> permissions (permission_id)); +diesel::joinable!(role_permissions -> roles (role_id)); +diesel::joinable!(role_permissions_replica1 -> permissions (permission_id)); +diesel::joinable!(role_permissions_replica1 -> roles (role_id)); +diesel::joinable!(role_permissions_replica2 -> permissions (permission_id)); +diesel::joinable!(role_permissions_replica2 -> roles (role_id)); +diesel::joinable!(users -> roles (role_id)); +diesel::joinable!(users_replica1 -> roles (role_id)); +diesel::joinable!(users_replica2 -> roles (role_id)); + + diesel::allow_tables_to_appear_in_same_query!( connectivity_tests, connectivity_tests_replica1, @@ -280,6 +384,15 @@ diesel::allow_tables_to_appear_in_same_query!( network_interfaces, network_interfaces_replica1, network_interfaces_replica2, + permissions, + permissions_replica1, + permissions_replica2, + role_permissions, + role_permissions_replica1, + role_permissions_replica2, + roles, + roles_replica1, + roles_replica2, routermgr, routermgr_replica1, routermgr_replica2,