Compare commits

...

2 Commits

Author SHA1 Message Date
pure_sagacity fbea598f63 made the database untracked 2026-07-13 20:10:48 -05:00
pure_sagacity 27fac75738 made adding a project work
changed the sql migration to make names unique
2026-07-13 20:10:04 -05:00
9 changed files with 171 additions and 25 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"cSpell.words": ["chrono"]
}
Generated
+1
View File
@@ -1748,6 +1748,7 @@ version = "1.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
dependencies = [
"getrandom 0.4.3",
"js-sys",
"serde_core",
"wasm-bindgen",
+1
View File
@@ -0,0 +1 @@
harbor.db
+5 -1
View File
@@ -20,5 +20,9 @@ colored = "3.1.1"
base64 = "0.22.1"
chrono = "0.4.45"
dotenvy = "0.15"
uuid = "1.23.5"
uuid = { version = "1.23.5", features = ["v4"] }
hex = "0.4"
[[bin]]
name = "harbor"
path = "src/main.rs"
Binary file not shown.
@@ -1,11 +1,13 @@
CREATE TABLE projects (
id TEXT PRIMARY KEY,
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (name)
);
CREATE TABLE secrets (
id TEXT PRIMARY KEY,
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
project_id TEXT NOT NULL,
config TEXT NOT NULL,
+7
View File
@@ -11,6 +11,13 @@ pub struct Project {
pub created_at: NaiveDateTime,
}
#[derive(Insertable)]
#[diesel(table_name = projects)]
pub struct NewProject<'a> {
pub id: &'a str,
pub name: &'a str,
}
#[derive(Debug, Queryable, Selectable)]
#[diesel(table_name = secrets)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
+78 -14
View File
@@ -1,10 +1,12 @@
use base64::{Engine, engine::general_purpose::STANDARD};
use clap::{Args, Parser, Subcommand};
use clap::{Parser, Subcommand};
use crypto::helper::gen_key;
use keyring::Entry;
use std::error::Error;
use std::io::{self, Write};
mod db;
mod store;
#[derive(Parser)]
#[clap(
name = "Harbor",
@@ -22,7 +24,13 @@ pub struct Cli {
#[derive(Subcommand)]
pub enum Commands {
Inject(InjectArgs),
Inject {
#[arg(short, long)]
verbose: bool,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
after: Vec<String>,
},
Shell {},
Add {},
Setup {},
@@ -37,30 +45,72 @@ pub enum Commands {
},
}
#[derive(Args, Debug)]
pub struct InjectArgs {
#[arg(short, long)]
pub verbose: bool,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
pub after: Vec<String>,
}
#[derive(Subcommand)]
pub enum ProjectCommands {
List {},
Create { name: String },
Create {},
Delete { name: String },
}
#[derive(Subcommand)]
pub enum ConfigCommands {
List {},
Create { project: String, name: String },
Create { project: String },
Delete { project: String, name: String },
}
fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
pub mod interactions {
use super::db::models::{NewProject, Project};
use super::db::{
establish_connection,
schema::projects::dsl::{name as project_name, projects},
};
use diesel::result::{DatabaseErrorKind, Error as DieselError};
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper};
use std::error::Error;
use uuid::Uuid;
fn construct_error(message: &str) -> Result<(), Box<dyn Error>> {
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
message.to_string(),
)))
}
pub fn create_project(proj_name: &str) -> Result<(), Box<dyn Error>> {
let mut conn = establish_connection();
// 1. Check if the project already exists
let existing_project = projects
.filter(project_name.eq(proj_name))
.select(Project::as_select())
.first::<Project>(&mut conn)
.optional()?;
if let Some(_) = existing_project {
return construct_error("Project already exists");
}
let project_id = Uuid::new_v4().to_string();
let new_project = NewProject {
id: &project_id,
name: proj_name,
};
match diesel::insert_into(projects)
.values(&new_project)
.execute(&mut conn)
{
Ok(_) => Ok(()),
Err(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) => {
construct_error("Project already exists")
}
Err(err) => Err(Box::new(err)),
}
}
}
pub fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
let entry = Entry::new("harbor", "encryption-key")?;
match entry.get_password() {
@@ -84,3 +134,17 @@ fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
Err(err) => Err(Box::new(err)),
}
}
pub fn get_input(message: &str, prompt: char, new_line: bool) -> Result<String, Box<dyn Error>> {
let mut input = String::new();
if new_line {
print!("{}\n{} ", message, prompt);
} else {
print!("{}{} ", message, prompt);
}
io::stdout().flush()?;
io::stdin().read_line(&mut input)?;
Ok(input.trim().to_string())
}
+66 -2
View File
@@ -3,6 +3,7 @@ use std::process;
use clap::Parser;
use clap::crate_version;
use cli::Cli;
use cli::get_input;
use colored::*;
const GIT_VERSION: &str = env!("GIT_VERSION");
@@ -20,12 +21,75 @@ fn main() {
process::exit(0);
}
if cli.command.is_none() {
match cli.command {
Some(c) => match c {
cli::Commands::Add {} => {
println!("Add command executed");
}
cli::Commands::Config { command } => match command {
cli::ConfigCommands::List {} => {
println!("Config list command executed");
}
cli::ConfigCommands::Create { project } => {
println!("Config create command executed for project: {}", project);
}
cli::ConfigCommands::Delete { project, name } => {
println!(
"Config delete command executed for project: {}, name: {}",
project, name
);
}
},
cli::Commands::Inject { verbose, after } => {
println!(
"Inject command executed with verbose: {}, after: {:?}",
verbose, after
);
// for harbor inject -- bun dev
// after = ["bun", "dev"]
}
cli::Commands::List {} => {
println!("List command executed");
}
cli::Commands::Project { command } => match command {
cli::ProjectCommands::List {} => {
println!("Project list command executed");
}
cli::ProjectCommands::Create {} => {
let project_name = match get_input("Project name", ':', false) {
Ok(name) => name,
Err(e) => {
eprintln!("Error getting project name: {}", e);
process::exit(1);
}
};
match cli::interactions::create_project(&project_name) {
Ok(()) => println!("{}", "Project created successfully".blue()),
Err(e) => {
eprintln!("Error creating project: {}", e);
process::exit(1);
}
}
}
cli::ProjectCommands::Delete { name } => {
println!("Project delete command executed for name: {}", name);
}
},
cli::Commands::Shell {} => {
println!("Shell command executed");
}
cli::Commands::Setup {} => {
println!("Setup command executed");
}
},
None => {
println!(
"{}\n{}",
"No command was entered.".red(),
"Exiting...".dimmed()
"Use --help for more information.".dimmed()
);
process::exit(0);
}
}
}