added token in common
This commit is contained in:
parent
6e66713b43
commit
50c874e542
39
docs/api.md
Normal file
39
docs/api.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# sdlan web api文档
|
||||||
|
sdlan为前端提供了若干api。其中,所有的api都使用POST方法,将数据以json消息体的方式提交给服务端。服务端根据具体处理结果,返回json数据。
|
||||||
|
|
||||||
|
## 1. 返回消息体
|
||||||
|
sdlan api的所有返回的消息体都遵循如下格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": $int,
|
||||||
|
"message": $object,
|
||||||
|
"error": $string,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中,code为0代表请求成功,返回的内容存放在`message`字段,`error`字段为空字符串;反之,如果请求失败,则`code`不为0,同时,`message`为空字符串,`error`字段为错误描述字符串。
|
||||||
|
|
||||||
|
在后面的描述中,如果请求成功,则默认返回的`code`为0,`error`为空字符串,而`message`会写明结构;如果返回失败,则`message`为空字符串,`code`和`error`会根据情况标明。
|
||||||
|
|
||||||
|
## 2. 消息请求参数说明
|
||||||
|
在实际操作过程中,用户会需要先注册,注册成功之后,需要先使用用户名和密码进行登录,密码验证成功之后,后台会返回一个token,该token有效期为一个小时,在登录成功之后所有其他的接口(即除了register和login接口),在发起请求的时候,都需要在请求头的header里面带上`token`,否则会权限热症失败,返回`code`为400,`error`为`“unauthorized”`
|
||||||
|
|
||||||
|
## 3. 实际接口
|
||||||
|
实际接口分成几个类别列举。
|
||||||
|
|
||||||
|
### 4.1. 用户操作
|
||||||
|
用户操作包括用户注册,登录,登出,创建/删除/查看register-token(用于节点登录时填写的token);修改密码等一系列关于用于的操作。
|
||||||
|
|
||||||
|
### 4.2. 网络操作
|
||||||
|
网络操作主要包括网络的创建、列举、删除、路由(给网络添加、删除一系列路由信息)等操作。
|
||||||
|
|
||||||
|
### 4.3. 节点操作
|
||||||
|
节点操作包括对节点的授权、取消授权、在网络之间移动等操作。
|
||||||
|
|
||||||
|
#### 4.3.1 授权节点
|
||||||
|
该接口用于对未授权的节点进行授权操作。
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /peer/authorize
|
||||||
|
```
|
||||||
@ -3,7 +3,7 @@ pub const FLAGS_TYPE_MASK: u16 = 0x001f;
|
|||||||
pub const FLAGS_BITS_MASK: u16 = 0xffe0;
|
pub const FLAGS_BITS_MASK: u16 = 0xffe0;
|
||||||
|
|
||||||
// common头的flags里面的flag,可以组合
|
// common头的flags里面的flag,可以组合
|
||||||
// 表示从SN发送过来的REGISTER_SUPER消息,主要用于SN注册
|
// 表示从SN主动发送到其他SN的消息
|
||||||
pub const SDLNA_FLAGS_FEDERATION: u16 = 0x0010;
|
pub const SDLNA_FLAGS_FEDERATION: u16 = 0x0010;
|
||||||
pub const SDLAN_FLAGS_FROM_SN: u16 = 0x0020;
|
pub const SDLAN_FLAGS_FROM_SN: u16 = 0x0020;
|
||||||
pub const SDLAN_FLAGS_SOCKET: u16 = 0x0040;
|
pub const SDLAN_FLAGS_SOCKET: u16 = 0x0040;
|
||||||
|
|||||||
@ -5,8 +5,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
|||||||
#[derive(Debug, PartialEq, Serialize_repr, Deserialize_repr, Copy, Clone)]
|
#[derive(Debug, PartialEq, Serialize_repr, Deserialize_repr, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum CommandType {
|
pub enum CommandType {
|
||||||
AuthorizePeer,
|
MovePeer = 1,
|
||||||
MovePeer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -15,6 +14,16 @@ pub struct Command {
|
|||||||
pub message: Value,
|
pub message: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct MovePeerCommandReq {
|
||||||
|
pub from_id: String,
|
||||||
|
pub to_id: String,
|
||||||
|
pub peer_id: String,
|
||||||
|
pub new_ip: u32,
|
||||||
|
pub net_bit_len: u8,
|
||||||
|
pub user_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CommandResp {
|
pub struct CommandResp {
|
||||||
cmd_type: CommandType,
|
cmd_type: CommandType,
|
||||||
|
|||||||
@ -11,7 +11,11 @@ use crate::utils::*;
|
|||||||
pub struct Common<'a> {
|
pub struct Common<'a> {
|
||||||
pub packet_id: u16,
|
pub packet_id: u16,
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
|
// client's uuid
|
||||||
pub id: &'a str,
|
pub id: &'a str,
|
||||||
|
|
||||||
|
pub token: u64,
|
||||||
|
|
||||||
pub ttl: u8,
|
pub ttl: u8,
|
||||||
// packet type
|
// packet type
|
||||||
pub pc: PacketType,
|
pub pc: PacketType,
|
||||||
@ -25,6 +29,8 @@ impl<'a> Common<'a> {
|
|||||||
encode_u8(&mut result, self.version);
|
encode_u8(&mut result, self.version);
|
||||||
|
|
||||||
encode_buf_without_size(&mut result, &self.id.as_bytes(), 32);
|
encode_buf_without_size(&mut result, &self.id.as_bytes(), 32);
|
||||||
|
// result.extend_from_slice(self.id.as_bytes());
|
||||||
|
result.extend_from_slice(&self.token.to_be_bytes());
|
||||||
// encode_buf_with_size_1(&mut result, self.id.as_bytes());
|
// encode_buf_with_size_1(&mut result, self.id.as_bytes());
|
||||||
|
|
||||||
encode_u8(&mut result, self.ttl);
|
encode_u8(&mut result, self.ttl);
|
||||||
@ -37,10 +43,11 @@ impl<'a> Common<'a> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_slice(value: &'a [u8]) -> Result<(Common<'a>, &'a [u8])> {
|
pub fn from_slice(value: &'a [u8]) -> Result<(Common, &'a [u8])> {
|
||||||
let id_len = 32;
|
const id_len: usize = 32;
|
||||||
|
const token_len: usize = 8;
|
||||||
|
|
||||||
if value.len() < 7 + id_len {
|
if value.len() < 7 + id_len + token_len {
|
||||||
return Err("common header length error".into());
|
return Err("common header length error".into());
|
||||||
}
|
}
|
||||||
let packet_id = u16::from_be_bytes(value[0..2].try_into().unwrap());
|
let packet_id = u16::from_be_bytes(value[0..2].try_into().unwrap());
|
||||||
@ -51,26 +58,36 @@ impl<'a> Common<'a> {
|
|||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Err(SDLanError::ConvertError(e.to_string())),
|
Err(e) => return Err(SDLanError::ConvertError(e.to_string())),
|
||||||
};
|
};
|
||||||
|
// let id = u64::from_be_bytes(v1);
|
||||||
id = id.trim_end_matches('\0');
|
id = id.trim_end_matches('\0');
|
||||||
let ttl = value[3 + id_len];
|
|
||||||
let flags = BigEndian::read_u16(&value[4 + id_len..6 + id_len]);
|
let token = u64::from_be_bytes(
|
||||||
let pc = value[6 + id_len];
|
value[3 + id_len..3 + id_len + token_len]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ttl = value[3 + id_len + token_len];
|
||||||
|
let flags = BigEndian::read_u16(&value[4 + id_len + token_len..6 + id_len + token_len]);
|
||||||
|
let pc = value[6 + id_len + token_len];
|
||||||
|
|
||||||
let common = Self {
|
let common = Self {
|
||||||
packet_id,
|
packet_id,
|
||||||
version,
|
version,
|
||||||
id,
|
id,
|
||||||
ttl,
|
ttl,
|
||||||
|
token,
|
||||||
pc: pc.into(),
|
pc: pc.into(),
|
||||||
flags: flags,
|
flags: flags,
|
||||||
};
|
};
|
||||||
Ok((common, &value[7 + id_len..]))
|
Ok((common, &value[7 + id_len + token_len..]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_old_common(cmn: &'a Common) -> Self {
|
pub fn from_old_common(cmn: &'a Common) -> Self {
|
||||||
return Common {
|
return Common {
|
||||||
packet_id: cmn.packet_id,
|
packet_id: cmn.packet_id,
|
||||||
id: cmn.id,
|
id: cmn.id,
|
||||||
|
token: cmn.token,
|
||||||
version: cmn.version,
|
version: cmn.version,
|
||||||
ttl: cmn.ttl,
|
ttl: cmn.ttl,
|
||||||
pc: cmn.pc,
|
pc: cmn.pc,
|
||||||
@ -82,6 +99,7 @@ impl<'a> Common<'a> {
|
|||||||
return Common {
|
return Common {
|
||||||
packet_id: 0,
|
packet_id: 0,
|
||||||
id,
|
id,
|
||||||
|
token: 0,
|
||||||
version: 1,
|
version: 1,
|
||||||
ttl: 2,
|
ttl: 2,
|
||||||
pc: PacketType::PKTInvalid,
|
pc: PacketType::PKTInvalid,
|
||||||
@ -169,11 +187,13 @@ mod test {
|
|||||||
packet_id: 0,
|
packet_id: 0,
|
||||||
version: 0,
|
version: 0,
|
||||||
id,
|
id,
|
||||||
|
token: 0,
|
||||||
ttl: 2,
|
ttl: 2,
|
||||||
pc: 1.into(),
|
pc: 1.into(),
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
let value1 = common.encode();
|
let value1 = common.encode();
|
||||||
|
println!("value1.len: {}", value1.len());
|
||||||
let (common2, _) = Common::from_slice(&value1).unwrap();
|
let (common2, _) = Common::from_slice(&value1).unwrap();
|
||||||
println!("common2 = {:?}", common2);
|
println!("common2 = {:?}", common2);
|
||||||
assert_eq!(common.id, common2.id);
|
assert_eq!(common.id, common2.id);
|
||||||
|
|||||||
@ -20,9 +20,8 @@ pub struct RegisterSuper<'a> {
|
|||||||
|
|
||||||
// 自身的公钥
|
// 自身的公钥
|
||||||
pub pub_key: String,
|
pub pub_key: String,
|
||||||
|
|
||||||
// user's token, can be used to specify a user
|
// user's token, can be used to specify a user
|
||||||
pub token: &'a str,
|
// pub token: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -74,7 +73,7 @@ mod test {
|
|||||||
net_bit_len: 24,
|
net_bit_len: 24,
|
||||||
},
|
},
|
||||||
pub_key: "public key".to_string(),
|
pub_key: "public key".to_string(),
|
||||||
token: "user's token",
|
// token: "user's token",
|
||||||
};
|
};
|
||||||
let res = serde_json::to_string(&pkt1).unwrap();
|
let res = serde_json::to_string(&pkt1).unwrap();
|
||||||
println!("unmarshaled as {}", res);
|
println!("unmarshaled as {}", res);
|
||||||
@ -87,6 +86,7 @@ mod test {
|
|||||||
packet_id: 0,
|
packet_id: 0,
|
||||||
version: 1,
|
version: 1,
|
||||||
id: "asxalex",
|
id: "asxalex",
|
||||||
|
token: 0,
|
||||||
ttl: 128,
|
ttl: 128,
|
||||||
pc: PacketType::PKTRegisterSuper,
|
pc: PacketType::PKTRegisterSuper,
|
||||||
flags: config::SDLAN_FLAGS_FROM_SN,
|
flags: config::SDLAN_FLAGS_FROM_SN,
|
||||||
@ -112,7 +112,7 @@ mod test {
|
|||||||
net_bit_len: 24,
|
net_bit_len: 24,
|
||||||
},
|
},
|
||||||
pub_key: "public key".to_string(),
|
pub_key: "public key".to_string(),
|
||||||
token: "user's token",
|
// token: "user's token",
|
||||||
};
|
};
|
||||||
(cmn1, pkt1)
|
(cmn1, pkt1)
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ mod test {
|
|||||||
assert_eq!(pkt1.dev_addr.net_addr(), pkt2.dev_addr.net_addr(),);
|
assert_eq!(pkt1.dev_addr.net_addr(), pkt2.dev_addr.net_addr(),);
|
||||||
assert_eq!(pkt1.dev_addr.net_bit_len(), pkt2.dev_addr.net_bit_len());
|
assert_eq!(pkt1.dev_addr.net_bit_len(), pkt2.dev_addr.net_bit_len());
|
||||||
assert_eq!(pkt1.pub_key, pkt2.pub_key);
|
assert_eq!(pkt1.pub_key, pkt2.pub_key);
|
||||||
assert_eq!(pkt1.token, pkt2.token);
|
// assert_eq!(pkt1.token, pkt2.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
100
src/peer.rs
100
src/peer.rs
@ -1,11 +1,13 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use super::utils::Result;
|
use super::utils::Result;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
|
use dashmap::DashSet;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::prelude::FromRow;
|
use sqlx::prelude::FromRow;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::hash::Hasher;
|
||||||
use std::sync::atomic::{AtomicU32, AtomicU64, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU32, AtomicU64, AtomicU8, Ordering};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
@ -197,6 +199,7 @@ use crate::utils::SDLanError;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
pub struct PeerMap {
|
pub struct PeerMap {
|
||||||
pub peers: DashMap<String, Arc<Peer>>,
|
pub peers: DashMap<String, Arc<Peer>>,
|
||||||
|
// from client's uuid, to the
|
||||||
// ip to peer's uuid, the ip is the logical ip
|
// ip to peer's uuid, the ip is the logical ip
|
||||||
// of tun
|
// of tun
|
||||||
// pub peer_ip_map: DashMap<u32, String>,
|
// pub peer_ip_map: DashMap<u32, String>,
|
||||||
@ -264,6 +267,103 @@ impl PeerMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EdgeInfo {
|
||||||
|
pub edges: DashSet<String>,
|
||||||
|
ip_to_edge_id: DashMap<u32, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EdgeInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EdgeInfo {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
edges: DashSet::new(),
|
||||||
|
ip_to_edge_id: DashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// purge peer in self and in PeerMap, together
|
||||||
|
pub fn purge_peer(&self, id: &str, pm: &PeerMap) {
|
||||||
|
if let Some((_k, v)) = pm.peers.remove(id) {
|
||||||
|
let ip = v.dev_addr.net_addr();
|
||||||
|
self.ip_to_edge_id.remove(&ip);
|
||||||
|
}
|
||||||
|
self.edges.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unbind_peer(&self, id: &str, ip: u32) {
|
||||||
|
self.edges.remove(id);
|
||||||
|
if ip != 0 {
|
||||||
|
self.ip_to_edge_id.remove(&ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_peer_with_id<'a>(&self, id: &'a str, pm: &'a PeerMap) -> Option<Arc<Peer>> {
|
||||||
|
if !self.edges.contains(id) {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
if let Some(p) = pm.peers.get(id) {
|
||||||
|
Some(p.clone())
|
||||||
|
} else {
|
||||||
|
// edge conatains, but global peermap does not
|
||||||
|
// remove it in edges
|
||||||
|
self.edges.remove(id);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_peer_with_id<'a, F>(&'a self, pm: &'a PeerMap, f: F) -> Option<(String, Arc<Peer>)>
|
||||||
|
where
|
||||||
|
F: Fn(&Arc<Peer>) -> bool,
|
||||||
|
{
|
||||||
|
for id in self.edges.iter() {
|
||||||
|
if let Some(temp) = pm.peers.get(&*id) {
|
||||||
|
if f(&temp) {
|
||||||
|
return Some((id.to_owned(), temp.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_peer_with_ip(&self, ip: u32, pm: &PeerMap) -> Option<Arc<Peer>> {
|
||||||
|
if let Some(id) = self.ip_to_edge_id.get(&ip) {
|
||||||
|
if let Some(p) = pm.peers.get(&*id) {
|
||||||
|
return Some(p.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, id: &str) -> bool {
|
||||||
|
self.edges.contains(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_peer(&self, ip: u32, id: String) {
|
||||||
|
if ip != 0 {
|
||||||
|
self.ip_to_edge_id.insert(ip, id.clone());
|
||||||
|
}
|
||||||
|
self.edges.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新绑定一个id
|
||||||
|
pub fn rebind_peer_ip(&self, ip: u32, oldid: &str, newid: &str, pm: &PeerMap) {
|
||||||
|
if let Some((_, v)) = pm.peers.remove(oldid) {
|
||||||
|
pm.peers.insert(newid.to_owned(), v);
|
||||||
|
}
|
||||||
|
self.ip_to_edge_id.remove(&ip);
|
||||||
|
self.ip_to_edge_id.insert(ip, newid.to_owned());
|
||||||
|
self.edges.remove(oldid);
|
||||||
|
self.edges.insert(newid.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::config::AF_INET;
|
use crate::config::AF_INET;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
use std::{convert::From, net::AddrParseError, string::ParseError};
|
use std::{convert::From, net::AddrParseError, string::ParseError};
|
||||||
|
|
||||||
|
use aes::cipher::typenum::IsLessOrEqual;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, SDLanError>;
|
pub type Result<T> = std::result::Result<T, SDLanError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SDLanError {
|
pub enum SDLanError {
|
||||||
IOError(std::io::Error),
|
IOError(String),
|
||||||
NormalError(&'static str),
|
NormalError(&'static str),
|
||||||
ConvertError(String),
|
ConvertError(String),
|
||||||
SerializeError(String),
|
SerializeError(String),
|
||||||
@ -12,9 +14,22 @@ pub enum SDLanError {
|
|||||||
DBError(String),
|
DBError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SDLanError {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
Self::IOError(ref e) => e,
|
||||||
|
Self::NormalError(ref s) => s,
|
||||||
|
Self::ConvertError(ref e) => e,
|
||||||
|
Self::SerializeError(ref e) => e,
|
||||||
|
Self::EncryptError(ref e) => e,
|
||||||
|
Self::DBError(ref e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for SDLanError {
|
impl From<std::io::Error> for SDLanError {
|
||||||
fn from(value: std::io::Error) -> Self {
|
fn from(value: std::io::Error) -> Self {
|
||||||
Self::IOError(value)
|
Self::IOError(value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,15 @@ impl<T> MyDashMap<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub fn get_timestamp_after_duration(d: Duration) -> u64 {
|
||||||
|
if let Some(t) = SystemTime::now().checked_add(d) {
|
||||||
|
t.duration_since(UNIX_EPOCH).unwrap().as_secs()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn get_current_timestamp() -> u64 {
|
pub fn get_current_timestamp() -> u64 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
|
|||||||
@ -3,3 +3,11 @@ use uuid::Uuid;
|
|||||||
pub fn gen_uuid() -> String {
|
pub fn gen_uuid() -> String {
|
||||||
format!("{:032x}", Uuid::new_v4().as_u128())
|
format!("{:032x}", Uuid::new_v4().as_u128())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gen_uuid_u64() -> u64 {
|
||||||
|
(Uuid::new_v4().as_u128() >> 64) as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_uuid_u128() -> u128 {
|
||||||
|
Uuid::new_v4().as_u128()
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user