feat: configurar Supabase via UI no modal (sem mexer em config.json)
- Adiciona ler_config_supabase() e salvar_config_supabase() no app.rs
- App abre mesmo sem config (modo offline-only); Supabase é configurável depois
- Banner amarelo no topo alerta quando Supabase não está configurado
- Nova seção '☁️ Supabase' no modal de Configurações com campos mascarados
- Botões: Salvar chaves, Testar conexão, Revelar/Esconder
This commit is contained in:
+19
-5
@@ -57,11 +57,22 @@ fn main() {
|
||||
fs::create_dir_all(&data_dir).expect("não foi possível criar diretório de dados");
|
||||
log::info!("Data dir: {:?}", data_dir);
|
||||
|
||||
// Carrega chaves do Supabase — obrigatório vir do config.json
|
||||
let (anon_key, service_key) = load_config().expect("falha ao carregar configuracao do Supabase");
|
||||
log::info!("Supabase: keys carregadas (anon={}..., service={}...)",
|
||||
&anon_key[..8.min(anon_key.len())],
|
||||
&service_key[..8.min(service_key.len())]);
|
||||
// Carrega chaves do Supabase — opcional. App abre mesmo sem config (modo offline-only).
|
||||
// O usuário pode configurar depois via modal ⚙️.
|
||||
let (anon_key, service_key) = match load_config() {
|
||||
Ok(keys) => {
|
||||
log::info!("Supabase: keys carregadas (anon={}..., service={}...)",
|
||||
&keys.0[..8.min(keys.0.len())],
|
||||
&keys.1[..8.min(keys.1.len())]);
|
||||
keys
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Supabase: configuracao ausente — app abrindo em modo offline-only.");
|
||||
log::warn!("Motivo: {}", e);
|
||||
log::warn!("Use o botao Configurar no app pra definir as chaves.");
|
||||
(String::new(), String::new())
|
||||
}
|
||||
};
|
||||
|
||||
// Inicializa plugins
|
||||
let storage = storage::init(data_dir.clone())
|
||||
@@ -111,6 +122,9 @@ fn main() {
|
||||
plugins::app::set_loja,
|
||||
plugins::app::get_config,
|
||||
plugins::app::set_config,
|
||||
// Supabase config (gerenciado pela UI)
|
||||
plugins::app::ler_config_supabase,
|
||||
plugins::app::salvar_config_supabase,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("erro ao inicializar Tauri");
|
||||
|
||||
@@ -111,3 +111,87 @@ pub fn set_config(state: State<'_, AppState>, cfg: AppConfig) -> Result<(), Stri
|
||||
pub fn init(data_dir: PathBuf) -> AppState {
|
||||
AppState::new(data_dir)
|
||||
}
|
||||
|
||||
/// Lê o config.json cru e devolve as chaves do Supabase.
|
||||
/// Retorna masked:true se as chaves existirem (sem expor os valores completos).
|
||||
/// Retorna masked:false se o arquivo não existir ou estiver vazio.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SupabaseConfigStatus {
|
||||
pub tem_config: bool,
|
||||
pub anon_masked: String,
|
||||
pub service_masked: String,
|
||||
pub config_path: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn ler_config_supabase() -> SupabaseConfigStatus {
|
||||
let config_path = crate::plugins::app::data_dir_for_status()
|
||||
.join("config.json");
|
||||
|
||||
let (anon, service) = match fs::read_to_string(&config_path) {
|
||||
Ok(content) => {
|
||||
let cfg: serde_json::Value = serde_json::from_str(&content).unwrap_or_default();
|
||||
(
|
||||
cfg.get("SUPABASE_ANON_KEY").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
||||
cfg.get("SUPABASE_SERVICE_KEY").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
||||
)
|
||||
}
|
||||
Err(_) => (String::new(), String::new()),
|
||||
};
|
||||
|
||||
SupabaseConfigStatus {
|
||||
tem_config: !anon.is_empty() && !service.is_empty(),
|
||||
anon_masked: mask_key(&anon),
|
||||
service_masked: mask_key(&service),
|
||||
config_path: config_path.to_string_lossy().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mask_key(key: &str) -> String {
|
||||
if key.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
if key.len() <= 12 {
|
||||
return format!("{}...", &key[..4.min(key.len())]);
|
||||
}
|
||||
format!("{}...{}", &key[..6], &key[key.len()-4..])
|
||||
}
|
||||
|
||||
/// Salva (ou atualiza) as chaves do Supabase no config.json.
|
||||
/// Cria o diretório se necessário. Mantém os outros campos do config.json intactos.
|
||||
#[tauri::command]
|
||||
pub fn salvar_config_supabase(anon_key: String, service_key: String) -> Result<SupabaseConfigStatus, String> {
|
||||
if anon_key.trim().is_empty() || service_key.trim().is_empty() {
|
||||
return Err("As duas chaves são obrigatórias.".to_string());
|
||||
}
|
||||
|
||||
let config_path = crate::plugins::app::data_dir_for_status().join("config.json");
|
||||
if let Some(parent) = config_path.parent() {
|
||||
fs::create_dir_all(parent).map_err(|e| format!("Falha ao criar diretório: {}", e))?;
|
||||
}
|
||||
|
||||
// Lê config existente pra preservar outros campos (loja, pdv_nome, etc.)
|
||||
let mut cfg: serde_json::Value = fs::read_to_string(&config_path)
|
||||
.ok()
|
||||
.and_then(|c| serde_json::from_str(&c).ok())
|
||||
.unwrap_or_else(|| serde_json::json!({}));
|
||||
|
||||
cfg["SUPABASE_ANON_KEY"] = serde_json::Value::String(anon_key.trim().to_string());
|
||||
cfg["SUPABASE_SERVICE_KEY"] = serde_json::Value::String(service_key.trim().to_string());
|
||||
|
||||
let json = serde_json::to_string_pretty(&cfg)
|
||||
.map_err(|e| format!("Falha ao serializar config: {}", e))?;
|
||||
fs::write(&config_path, json).map_err(|e| format!("Falha ao salvar config: {}", e))?;
|
||||
|
||||
log::info!("Supabase config salva em {:?}", config_path);
|
||||
|
||||
Ok(ler_config_supabase())
|
||||
}
|
||||
|
||||
/// Helper pra obter o data_dir sem precisar importar de main.rs.
|
||||
/// Mantém o mesmo cálculo de `main::data_dir_path()`.
|
||||
pub fn data_dir_for_status() -> PathBuf {
|
||||
dirs::data_local_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
.join("FechamentoCaixa")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user