mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-31 22:43:11 +02:00
[WIP] Encryption for secure values (#183)
This commit is contained in:
@@ -53,9 +53,11 @@ impl<'a> DbContext<'a> {
|
||||
where
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
let (order_by_col, order_by_dir) = M::order_by();
|
||||
let (sql, params) = Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = self.conn.resolve().prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), M::from_row)?;
|
||||
@@ -72,18 +74,21 @@ impl<'a> DbContext<'a> {
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
// TODO: Figure out how to do this conditional builder better
|
||||
let (order_by_col, order_by_dir) = M::order_by();
|
||||
let (sql, params) = if let Some(limit) = limit {
|
||||
Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(col).eq(value))
|
||||
.limit(limit)
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder)
|
||||
} else {
|
||||
Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(col).eq(value))
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder)
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum Error {
|
||||
|
||||
#[error("Model error: {0}")]
|
||||
GenericError(String),
|
||||
|
||||
|
||||
#[error("Row not found")]
|
||||
RowNotFound,
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use tokio::sync::mpsc;
|
||||
mod commands;
|
||||
|
||||
mod connection_or_tx;
|
||||
mod db_context;
|
||||
pub mod db_context;
|
||||
pub mod error;
|
||||
pub mod models;
|
||||
pub mod queries;
|
||||
|
||||
@@ -3,10 +3,11 @@ use crate::models::HttpRequestIden::{
|
||||
Authentication, AuthenticationType, Body, BodyType, CreatedAt, Description, FolderId, Headers,
|
||||
Method, Name, SortPriority, UpdatedAt, Url, UrlParameters, WorkspaceId,
|
||||
};
|
||||
use crate::util::{generate_prefixed_id, UpdateSource};
|
||||
use crate::util::{UpdateSource, generate_prefixed_id};
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use rusqlite::Row;
|
||||
use sea_query::{enum_def, IntoIden, IntoTableRef, SimpleExpr};
|
||||
use sea_query::Order::Desc;
|
||||
use sea_query::{IntoColumnRef, IntoIden, IntoTableRef, Order, SimpleExpr, enum_def};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -124,6 +125,10 @@ impl UpsertModelInfo for Settings {
|
||||
panic!("Settings does not have unique IDs")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(SettingsIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -209,6 +214,7 @@ pub struct Workspace {
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub encryption_key_challenge: Option<String>,
|
||||
|
||||
// Settings
|
||||
#[serde(default = "default_true")]
|
||||
@@ -231,6 +237,10 @@ impl UpsertModelInfo for Workspace {
|
||||
generate_prefixed_id("wk")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WorkspaceIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -245,6 +255,7 @@ impl UpsertModelInfo for Workspace {
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(Name, self.name.trim().into()),
|
||||
(Description, self.description.into()),
|
||||
(EncryptionKeyChallenge, self.encryption_key_challenge.into()),
|
||||
(SettingFollowRedirects, self.setting_follow_redirects.into()),
|
||||
(SettingRequestTimeout, self.setting_request_timeout.into()),
|
||||
(SettingValidateCertificates, self.setting_validate_certificates.into()),
|
||||
@@ -256,6 +267,7 @@ impl UpsertModelInfo for Workspace {
|
||||
WorkspaceIden::UpdatedAt,
|
||||
WorkspaceIden::Name,
|
||||
WorkspaceIden::Description,
|
||||
WorkspaceIden::EncryptionKeyChallenge,
|
||||
WorkspaceIden::SettingRequestTimeout,
|
||||
WorkspaceIden::SettingFollowRedirects,
|
||||
WorkspaceIden::SettingRequestTimeout,
|
||||
@@ -274,6 +286,7 @@ impl UpsertModelInfo for Workspace {
|
||||
updated_at: row.get("updated_at")?,
|
||||
name: row.get("name")?,
|
||||
description: row.get("description")?,
|
||||
encryption_key_challenge: row.get("encryption_key_challenge")?,
|
||||
setting_follow_redirects: row.get("setting_follow_redirects")?,
|
||||
setting_request_timeout: row.get("setting_request_timeout")?,
|
||||
setting_validate_certificates: row.get("setting_validate_certificates")?,
|
||||
@@ -281,16 +294,11 @@ impl UpsertModelInfo for Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
model: "workspace".to_string(),
|
||||
setting_validate_certificates: true,
|
||||
setting_follow_redirects: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct EncryptedKey {
|
||||
pub encrypted_key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
@@ -304,6 +312,7 @@ pub struct WorkspaceMeta {
|
||||
pub workspace_id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub encryption_key: Option<EncryptedKey>,
|
||||
pub setting_sync_dir: Option<String>,
|
||||
}
|
||||
|
||||
@@ -320,6 +329,10 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
generate_prefixed_id("wm")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WorkspaceMetaIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -333,6 +346,7 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
(CreatedAt, upsert_date(source, self.created_at)),
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(WorkspaceId, self.workspace_id.into()),
|
||||
(EncryptionKey, self.encryption_key.map(|e| serde_json::to_string(&e).unwrap()).into()),
|
||||
(SettingSyncDir, self.setting_sync_dir.into()),
|
||||
])
|
||||
}
|
||||
@@ -340,6 +354,7 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
fn update_columns() -> Vec<impl IntoIden> {
|
||||
vec![
|
||||
WorkspaceMetaIden::UpdatedAt,
|
||||
WorkspaceMetaIden::EncryptionKey,
|
||||
WorkspaceMetaIden::SettingSyncDir,
|
||||
]
|
||||
}
|
||||
@@ -348,12 +363,14 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let encryption_key: Option<String> = row.get("encryption_key")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
workspace_id: row.get("workspace_id")?,
|
||||
model: row.get("model")?,
|
||||
created_at: row.get("created_at")?,
|
||||
updated_at: row.get("updated_at")?,
|
||||
encryption_key: encryption_key.map(|e| serde_json::from_str(&e).unwrap()),
|
||||
setting_sync_dir: row.get("setting_sync_dir")?,
|
||||
})
|
||||
}
|
||||
@@ -413,6 +430,10 @@ impl UpsertModelInfo for CookieJar {
|
||||
generate_prefixed_id("cj")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(CookieJarIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -486,6 +507,10 @@ impl UpsertModelInfo for Environment {
|
||||
generate_prefixed_id("ev")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(EnvironmentIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -575,6 +600,10 @@ impl UpsertModelInfo for Folder {
|
||||
generate_prefixed_id("fl")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(FolderIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -673,7 +702,7 @@ pub struct HttpRequest {
|
||||
#[serde(default = "default_http_method")]
|
||||
pub method: String,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
pub sort_priority: f64,
|
||||
pub url: String,
|
||||
pub url_parameters: Vec<HttpUrlParameter>,
|
||||
}
|
||||
@@ -691,6 +720,10 @@ impl UpsertModelInfo for HttpRequest {
|
||||
generate_prefixed_id("rq")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(HttpResponseIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
@@ -745,21 +778,21 @@ impl UpsertModelInfo for HttpRequest {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
url: r.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
method: r.get("method")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
body: serde_json::from_str(body.as_str()).unwrap_or_default(),
|
||||
body_type: r.get("body_type")?,
|
||||
description: r.get("description")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
folder_id: r.get("folder_id")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
method: r.get("method")?,
|
||||
name: r.get("name")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
url: r.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -814,6 +847,10 @@ impl UpsertModelInfo for WebsocketConnection {
|
||||
generate_prefixed_id("wc")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketConnectionIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -924,6 +961,10 @@ impl UpsertModelInfo for WebsocketRequest {
|
||||
generate_prefixed_id("wr")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketRequestIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1045,6 +1086,10 @@ impl UpsertModelInfo for WebsocketEvent {
|
||||
generate_prefixed_id("we")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketEventIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1158,6 +1203,10 @@ impl UpsertModelInfo for HttpResponse {
|
||||
generate_prefixed_id("rs")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(HttpResponseIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1286,6 +1335,10 @@ impl UpsertModelInfo for GrpcRequest {
|
||||
generate_prefixed_id("gr")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcRequestIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1409,6 +1462,10 @@ impl UpsertModelInfo for GrpcConnection {
|
||||
generate_prefixed_id("gc")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcConnectionIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1525,6 +1582,10 @@ impl UpsertModelInfo for GrpcEvent {
|
||||
generate_prefixed_id("ge")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcEventIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1612,6 +1673,10 @@ impl UpsertModelInfo for Plugin {
|
||||
generate_prefixed_id("pg")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(PluginIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1690,6 +1755,10 @@ impl UpsertModelInfo for SyncState {
|
||||
generate_prefixed_id("ss")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(SyncStateIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1769,6 +1838,10 @@ impl UpsertModelInfo for KeyValue {
|
||||
generate_prefixed_id("kv")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(KeyValueIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1919,13 +1992,18 @@ impl<'de> Deserialize<'de> for AnyModel {
|
||||
Some(m) if m == "key_value" => AnyModel::KeyValue(fv(value).unwrap()),
|
||||
Some(m) if m == "plugin" => AnyModel::Plugin(fv(value).unwrap()),
|
||||
Some(m) if m == "settings" => AnyModel::Settings(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_connection" => AnyModel::WebsocketConnection(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_connection" => {
|
||||
AnyModel::WebsocketConnection(fv(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "websocket_event" => AnyModel::WebsocketEvent(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_request" => AnyModel::WebsocketRequest(fv(value).unwrap()),
|
||||
Some(m) if m == "workspace" => AnyModel::Workspace(fv(value).unwrap()),
|
||||
Some(m) if m == "workspace_meta" => AnyModel::WorkspaceMeta(fv(value).unwrap()),
|
||||
Some(m) => {
|
||||
return Err(serde::de::Error::custom(format!("Failed to deserialize AnyModel {}", m)));
|
||||
return Err(serde::de::Error::custom(format!(
|
||||
"Failed to deserialize AnyModel {}",
|
||||
m
|
||||
)));
|
||||
}
|
||||
None => {
|
||||
return Err(serde::de::Error::custom("Missing or invalid model"));
|
||||
@@ -1963,6 +2041,7 @@ pub trait UpsertModelInfo {
|
||||
fn table_name() -> impl IntoTableRef;
|
||||
fn id_column() -> impl IntoIden + Eq + Clone;
|
||||
fn generate_id() -> String;
|
||||
fn order_by() -> (impl IntoColumnRef, Order);
|
||||
fn get_id(&self) -> String;
|
||||
fn insert_values(
|
||||
self,
|
||||
|
||||
@@ -26,7 +26,6 @@ impl<'a> DbContext<'a> {
|
||||
pub fn get_or_create_workspace_meta(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
source: &UpdateSource,
|
||||
) -> Result<WorkspaceMeta> {
|
||||
let workspace_meta = self.get_workspace_meta(workspace_id);
|
||||
if let Some(workspace_meta) = workspace_meta {
|
||||
@@ -38,7 +37,7 @@ impl<'a> DbContext<'a> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.upsert_workspace_meta(&workspace_meta, source)
|
||||
self.upsert_workspace_meta(&workspace_meta, &UpdateSource::Background)
|
||||
}
|
||||
|
||||
pub fn upsert_workspace_meta(
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use crate::connection_or_tx::ConnectionOrTx;
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Error::GenericError;
|
||||
use crate::error::Result;
|
||||
use crate::util::ModelPayload;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::TransactionBehavior;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::{Manager, Runtime};
|
||||
use tauri::{Manager, Runtime, State};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub trait QueryManagerExt<'a, R> {
|
||||
fn db_manager(&'a self) -> State<'a, QueryManager>;
|
||||
fn db(&'a self) -> DbContext<'a>;
|
||||
fn with_db<F, T>(&'a self, func: F) -> T
|
||||
where
|
||||
@@ -20,6 +22,10 @@ pub trait QueryManagerExt<'a, R> {
|
||||
}
|
||||
|
||||
impl<'a, R: Runtime, M: Manager<R>> QueryManagerExt<'a, R> for M {
|
||||
fn db_manager(&'a self) -> State<'a, QueryManager> {
|
||||
self.state::<QueryManager>()
|
||||
}
|
||||
|
||||
fn db(&'a self) -> DbContext<'a> {
|
||||
let qm = self.state::<QueryManager>();
|
||||
qm.inner().connect()
|
||||
@@ -42,7 +48,7 @@ impl<'a, R: Runtime, M: Manager<R>> QueryManagerExt<'a, R> for M {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryManager {
|
||||
pool: Arc<Mutex<Pool<SqliteConnectionManager>>>,
|
||||
events_tx: mpsc::Sender<ModelPayload>,
|
||||
@@ -91,9 +97,12 @@ impl QueryManager {
|
||||
func(&db_context)
|
||||
}
|
||||
|
||||
pub fn with_tx<F, T>(&self, func: F) -> Result<T>
|
||||
pub fn with_tx<T, E>(
|
||||
&self,
|
||||
func: impl FnOnce(&DbContext) -> std::result::Result<T, E>,
|
||||
) -> std::result::Result<T, E>
|
||||
where
|
||||
F: FnOnce(&DbContext) -> Result<T>,
|
||||
E: From<crate::error::Error>,
|
||||
{
|
||||
let mut conn = self
|
||||
.pool
|
||||
@@ -112,11 +121,13 @@ impl QueryManager {
|
||||
|
||||
match func(&db_context) {
|
||||
Ok(val) => {
|
||||
tx.commit()?;
|
||||
tx.commit()
|
||||
.map_err(|e| GenericError(format!("Failed to commit transaction {e:?}")))?;
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
tx.rollback()?;
|
||||
tx.rollback()
|
||||
.map_err(|e| GenericError(format!("Failed to rollback transaction {e:?}")))?;
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeMap;
|
||||
use crate::error::Result;
|
||||
use crate::models::{AnyModel, Environment, Folder, GrpcRequest, HttpRequest, UpsertModelInfo, WebsocketRequest, Workspace, WorkspaceIden};
|
||||
use crate::query_manager::QueryManagerExt;
|
||||
@@ -6,6 +5,7 @@ use chrono::{NaiveDateTime, Utc};
|
||||
use log::warn;
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use tauri::{AppHandle, Listener, Runtime, WebviewWindow};
|
||||
use ts_rs::TS;
|
||||
|
||||
@@ -14,6 +14,10 @@ pub fn generate_prefixed_id(prefix: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn generate_id() -> String {
|
||||
generate_id_of_length(10)
|
||||
}
|
||||
|
||||
pub fn generate_id_of_length(n: usize) -> String {
|
||||
let alphabet: [char; 57] = [
|
||||
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C',
|
||||
@@ -21,7 +25,7 @@ pub fn generate_id() -> String {
|
||||
'X', 'Y', 'Z',
|
||||
];
|
||||
|
||||
nanoid!(10, &alphabet)
|
||||
nanoid!(n, &alphabet)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
@@ -101,8 +105,9 @@ pub struct WorkspaceExport {
|
||||
pub resources: BatchUpsertResult,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||
#[derive(Default, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_util.ts")]
|
||||
pub struct BatchUpsertResult {
|
||||
pub workspaces: Vec<Workspace>,
|
||||
pub environments: Vec<Environment>,
|
||||
|
||||
Reference in New Issue
Block a user