48 Commits

Author SHA1 Message Date
32c2703ef2 feat: beginning of omnia
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-12-11 20:25:06 +01:00
f009887772 feat: remove target
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-12-10 19:47:14 +01:00
392ecd88db feat: remove como
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-12-10 19:46:38 +01:00
c718124e85 feat: with beginning of omnia
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-12-10 19:45:40 +01:00
1f88524c16 feat: add basic ci
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-10-22 12:31:53 +02:00
71b5a63700 feat: with como_web
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-10-21 11:23:11 +02:00
6e16fc6b2b feat: move project to crates
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-10-21 11:14:58 +02:00
381b472eca feat: with mold
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-09-08 21:40:09 +02:00
491ec81298 feat: allow dead code
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-09-05 22:05:17 +02:00
cdeefba39a feat(auth): with basic auth options
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 21:19:54 +02:00
ec483ce875 chore(fmt): run clippy fix
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 17:23:19 +02:00
1f13172ec0 fix(auth): remove rest of todos for hot path
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 16:29:07 +02:00
7a71f9b106 chore: fmt
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 16:05:27 +02:00
57d30f2129 chore(fmt): run clippy fix
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 14:09:15 +02:00
e6084a7f4e feat(auth): add authentication integration
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 14:08:40 +02:00
48d09c8ae3 refactor(auth): dyn Introspection
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 12:08:14 +02:00
f65e85dbe1 feat(auth): add file merge as base
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 01:44:54 +02:00
acde8b17e1 refactor(auth): setup convenience for OAuth
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 01:25:46 +02:00
0bb7074334 refactor(auth): move into central auth-engine setup
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 01:15:11 +02:00
5837ee0288 chore(auth): with introspection
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-20 00:23:27 +02:00
0893f285a3 chore(auth): add config and tests
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-19 23:59:33 +02:00
a2db6ca64a chore(auth): add tests
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-19 16:43:50 +02:00
7dcd3b4efe feat(auth): add base oauth client
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-19 16:24:08 +02:00
48e9d73e6d feat(auth): add base oauth client
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-19 16:15:17 +02:00
5e879b7ef2 feat(auth): add base oauth client
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-19 15:42:29 +02:00
9d064a1287 feat: with comments and abort
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-07-27 16:58:40 +02:00
258dc8779c feat: with setup ssh
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-07-27 16:57:51 +02:00
1192f366f0 Merge pull request 'Configure Renovate' (#18) from renovate/configure into main
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build is failing
Reviewed-on: https://git.front.kjuulh.io/kjuulh/como/pulls/18
2023-07-27 14:47:39 +00:00
8ec89ed678 Add renovate.json
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2023-07-27 14:46:57 +00:00
0b966816a8 feat: with template
Some checks reported errors
continuous-integration/drone/push Build encountered an error
continuous-integration/drone Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-07-27 16:44:18 +02:00
3e5309e1e6 feat: update scripts to use new cuddle
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-10 14:35:03 +02:00
88c7acd439 feat: add deploy
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-04 14:55:22 +02:00
f9dcc59f3c chore: fix
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-04 14:43:36 +02:00
2604c5e301 feat: with updated deps
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-04 14:42:38 +02:00
534b2e4a23 feat: with items
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-04 11:02:51 +02:00
12c7c8f6ee feat: working projects and items
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 17:17:22 +02:00
6dc0c24443 feat: with create project
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 16:25:25 +02:00
c81a988061 chore: fmt all
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 15:43:29 +02:00
b3af0be6b5 feat: with persistent state
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 15:37:46 +02:00
746fb68684 feat: with persistent session state
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 15:34:36 +02:00
1e38b2838c feat: with zitadel login
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-28 15:05:16 +02:00
e991caef73 feat: with authorization
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-05-27 13:12:29 +02:00
1d4cda7c48 with memory db 2022-10-04 22:39:57 +02:00
c7f8dc6198 with graphql 2022-10-04 12:06:00 +02:00
5e9001b998 Removed unused imports 2022-10-04 11:07:14 +02:00
ae74f66c3a Added apis 2022-10-04 11:06:48 +02:00
6234cf18e8 Add initial services 2022-10-03 23:00:31 +02:00
b01b33f7d1 add core models 2022-10-03 22:20:01 +02:00
41 changed files with 512 additions and 2982 deletions

View File

@@ -1,88 +0,0 @@
kind: pipeline
name: default
type: docker
steps:
- name: load_secret
image: debian:buster-slim
volumes:
- name: ssh
path: /root/.ssh/
environment:
SSH_KEY:
from_secret: gitea_id_ed25519
commands:
- mkdir -p $HOME/.ssh/
- echo "$SSH_KEY" | base64 -d > $HOME/.ssh/id_ed25519
- name: build
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
commands:
- apk add bash git
- git remote set-url origin $DRONE_GIT_SSH_URL
- cuddle_cli x setup_ssh
- cuddle_cli x start_deployment
- cuddle_cli x render_templates
- cuddle_cli x render_como_templates
- cuddle_cli x build_release
- cuddle_cli x push_release
- cuddle_cli x deploy_release
environment:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME:
from_secret: docker_username
DOCKER_PASSWORD:
from_secret: docker_password
SSH_KEY:
from_secret: gitea_id_ed25519
- name: push_tags
image: kasperhermansen/drone-semantic-release:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
commands:
- semantic-release --no-ci
environment:
DOCKER_BUILDKIT: 1
SSH_KEY:
from_secret: gitea_id_ed25519
depends_on:
- build
- name: send telegram notification
image: appleboy/drone-telegram
settings:
token:
from_secret: telegram_token
to: 2129601481
format: markdown
depends_on:
- build
- push_tags
when:
status: [failure, success]
services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
volumes:
- name: ssh
temp: {}
- name: dockersock
temp: {}

4
.env
View File

@@ -1,4 +0,0 @@
POSTGRES_DB=como
POSTGRES_USER=como
POSTGRES_PASSWORD=somenotverysecurepassword
DATABASE_URL="postgres://como:somenotverysecurepassword@localhost:5432/como"

3
.gitignore vendored
View File

@@ -1,3 +1,2 @@
/target
target/
.cuddle/
node_modules/

View File

@@ -1,8 +0,0 @@
branches:
- "main"
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/changelog"
- "@semantic-release/git"

2547
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,3 @@
[workspace]
members = ["como_bin", "como_core", "como_domain"]
members = [ "crates/*" ]
resolver = "2"

View File

@@ -1,3 +1,76 @@
# Cibus Backend
# Omnia
Some text
`Omnia` is a tool to provide a common platform for everything. It is a tool
build to support note-taking, handling personal relationships, project
development, todos, research, up-keep of external work, and much more.
This may seem like a large list of tasks, and it is. However, Omnia is designed
to be opinionated, and provide a minimalistic approach to each of the above.
Omnia is not a general purpose tool, text editing tool, it is designed to fit
into your existing toolstack, with its opinionated project structure, and
workflow.
Alternatives to this tool:
- Notion: with second brain templates etc.
- Obsidian: with zen garden templates etc.
Omnia is a commandline tool, which functions using a terminal ui, or pure cli
commands. Called `interactive` for the former, and `prompt` for the latter. It
is designed to work with your favorite EDITOR, to keep the scope of this project
reasonable, `Omnia` doesn't try to bundle with an editor. Instead it will
respect your `OMNIA_EDITOR`, or `EDITOR` environment variables, and launch the
files using those.
## How to use `Omnia`
To launch the fully interactive view, simply type `omnia` in your shell of
choice. This will boot up the TUI, press `?` for help and it will show a brief,
menu as well as the most common keybinds.
Following each command will be shown separately, these will be available in the
UI as well, just follow along in the menu, or use the command key `:` to open
the command palette.
### Commands
In Omnia everything is designed to use Markdown files, even the templates are
markdown files, though with some special syntax to make prompting easier. This
also means that you can open your local ~/omnia directory using your favorite
editor, as everything is just markdown files.
Projects are the cornerstone of how Omnia functions. Every navigation item is a
project, be they todo lists, research items, external sites etc. Projects can be
nested, and projects can contain pages. External apps can be configured as a
project, and will need a certain interface to be functional.
This means that when you type a command:
`omnia --help`, each subcommand will be a project, some keywords are reserved:
(todo, inbox, project, etc.), each project decides which commands are available,
and this is fully customizeable, through our plugin system. Though note that we
ship the default view with a set of preconfigured plugins:
- todo
- inbox
- projects
- areas
Typing each of the commands above will open the fully interactive tui:
`omnia todo`. Todo has a set of commands available to it: `omnia todo create` as
an example. This will open the prompt view, which will interactive ask the user
to fill out a form. These questions will also be available using commandline
flags as well.
### Views
Some of the projects are built as views, this may be a list of recent
notifications on github, apis of interest, metrics and whatnot.
## Remote first
Omnia will sync remote first in nearly all cases. The only exception is in
progress forms and whatnot. This is to keep complexity down, as well as making
sure Omnia is as easily crossplatform as possible. You should be able to use
`Omnia` from all your terminal capable devices. This sprung out of my own need
for having my notes available everywhere, without having conflicts.

View File

@@ -1,32 +0,0 @@
[package]
name = "como_bin"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-graphql = "4.0.6"
async-graphql-axum = "*"
axum = "0.5.13"
axum-extra = { version = "*", features = ["cookie", "cookie-private"] }
axum-sessions = { version = "*" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.68"
tokio = { version = "1.20.1", features = ["full"] }
uuid = { version = "1.1.2", features = ["v4", "fast-rng"] }
sqlx = { version = "0.6", features = [
"runtime-tokio-rustls",
"postgres",
"migrate",
"uuid",
"offline",
] }
anyhow = "1.0.60"
dotenv = "0.15.0"
tracing = "0.1.36"
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
tower-http = { version = "0.3.4", features = ["full"] }
argon2 = "0.4"
rand_core = { version = "0.6", features = ["std"] }
cookie = { version = "0.16", features = ["secure", "percent-encode"] }

View File

@@ -1,6 +0,0 @@
// generated by `sqlx migrate build-script`
fn main() {
// trigger recompilation when a new migration is added
println!("cargo:rerun-if-changed=migrations");
}

View File

@@ -1,8 +0,0 @@
-- Add migration script here
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username varchar not null,
password_hash varchar not null
);
CREATE unique index users_username_idx on users(username)

View File

@@ -1,56 +0,0 @@
{
"db": "PostgreSQL",
"3b4484c5ccfd4dcb887c4e978fe6e45d4c9ecc2a73909be207dced79ddf17d87": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
}
],
"nullable": [
false
],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
}
},
"query": "\n INSERT INTO users (username, password_hash) \n VALUES ( $1, $2 ) \n RETURNING id\n "
},
"d3f222cf6c3d9816705426fdbed3b13cb575bb432eb1f33676c0b414e67aecaf": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
},
{
"name": "username",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "password_hash",
"ordinal": 2,
"type_info": "Varchar"
}
],
"nullable": [
false,
false,
false
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "\n SELECT * from users\n where username=$1\n "
}
}

View File

@@ -1,21 +0,0 @@
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde_json::json;
#[derive(Debug)]
pub enum AppError {
WrongCredentials,
InternalServerError,
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, err_msg) = match self {
Self::WrongCredentials => (StatusCode::BAD_REQUEST, "invalid credentials"),
Self::InternalServerError => (
StatusCode::INTERNAL_SERVER_ERROR,
"something went wrong with your request",
),
};
(status, Json(json!({ "error": err_msg }))).into_response()
}
}

View File

@@ -1,2 +0,0 @@
pub mod users;

View File

@@ -1,106 +0,0 @@
use async_graphql::{Context, EmptySubscription, Object, Schema, SimpleObject};
use uuid::Uuid;
use crate::services::users_service::UserService;
pub type CibusSchema = Schema<QueryRoot, MutationRoot, EmptySubscription>;
pub struct MutationRoot;
#[Object]
impl MutationRoot {
async fn login(
&self,
ctx: &Context<'_>,
username: String,
password: String,
) -> anyhow::Result<bool> {
let user_service = ctx.data_unchecked::<UserService>();
let valid = user_service.validate_user(username, password).await?;
let returnvalid = match valid {
Some(..) => true,
None => false,
};
Ok(returnvalid)
}
async fn register(
&self,
ctx: &Context<'_>,
username: String,
password: String,
) -> anyhow::Result<String> {
let user_service = ctx.data_unchecked::<UserService>();
let user_id = user_service.add_user(username, password).await?;
Ok(user_id)
}
}
pub struct QueryRoot;
#[Object]
impl QueryRoot {
async fn get_upcoming(&self, _ctx: &Context<'_>) -> Vec<Event> {
vec![Event::new(
None,
"Some-name".into(),
None,
None,
EventDate::new(2022, 08, 08, 23, 51),
)]
}
}
#[derive(SimpleObject)]
pub struct Event {
pub id: String,
pub name: String,
pub description: Option<Vec<String>>,
pub location: Option<String>,
pub date: EventDate,
}
impl Event {
pub fn new(
id: Option<String>,
name: String,
description: Option<Vec<String>>,
location: Option<String>,
date: EventDate,
) -> Self {
Self {
id: id.unwrap_or_else(|| Uuid::new_v4().to_string()),
name,
description,
location,
date,
}
}
}
#[derive(SimpleObject)]
pub struct EventDate {
pub year: u32,
pub month: u32,
pub day: u32,
pub hour: u32,
pub minute: u32,
}
impl EventDate {
pub fn new(year: u32, month: u32, day: u32, hour: u32, minute: u32) -> Self {
Self {
year,
month,
day,
hour,
minute,
}
}
}

View File

@@ -1,175 +0,0 @@
use std::env::{self, current_dir};
mod error;
mod gqlx;
mod graphql;
mod services;
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
use axum::{
extract::Extension,
http::{Method, StatusCode},
response::{Html, IntoResponse},
routing::{get, post},
Json, Router,
};
use axum_extra::extract::cookie::Key;
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
EmptySubscription, Schema,
};
use axum_sessions::{
async_session::MemoryStore,
extractors::{ReadableSession, WritableSession},
SessionLayer,
};
use error::AppError;
use graphql::CibusSchema;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use services::users_service;
use sqlx::PgPool;
use tower_http::{cors::CorsLayer, trace::TraceLayer};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use crate::graphql::{MutationRoot, QueryRoot};
async fn graphql_handler(
schema: Extension<CibusSchema>,
session: ReadableSession,
req: GraphQLRequest,
) -> Result<GraphQLResponse, StatusCode> {
let req = req.into_inner();
//if let Some(user_id) = session.get::<String>("userId") {
// req = req.data(user_id);
return Ok(schema.execute(req).await.into());
//} else if let Some(on) = &req.operation_name {
// if on == "IntrospectionQuery" {
// return Ok(schema.execute(req).await.into());
// }
//}
//Err(StatusCode::FORBIDDEN)
}
async fn graphql_playground() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Environment
tracing::info!("Loading dotenv");
dotenv::dotenv()?;
// Logging
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| {
"como_bin=debug,tower_http=debug,axum_extra=debug,hyper=info,mio=info,sqlx=info,async_graphql=debug"
.into()
}),
))
.with(tracing_subscriber::fmt::layer())
.init();
// Database
tracing::info!("Creating pool");
let db_url = env::var("DATABASE_URL")?;
let pool = PgPool::connect(&db_url).await?;
// Database Migrate
tracing::info!("Migrating db");
sqlx::migrate!("db/migrations").run(&pool).await?;
tracing::info!("current path: {}", current_dir()?.to_string_lossy());
// Schema
println!("Building schema");
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(users_service::UserService::new(pool.clone()))
.finish();
// CORS
let cors = vec!["http://localhost:3000".parse().unwrap()];
// Key
let key = Key::generate();
let store = MemoryStore::new();
let session_layer = SessionLayer::new(store, key.master());
// Webserver
tracing::info!("Building router");
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.route("/auth/login", post(login))
.route("/auth/register", post(register))
.layer(TraceLayer::new_for_http())
.layer(Extension(schema))
.layer(Extension(key))
.layer(Extension(pool))
.layer(session_layer)
.layer(
CorsLayer::new()
.allow_origin(cors)
.allow_headers([axum::http::header::CONTENT_TYPE])
.allow_methods([Method::GET, Method::POST, Method::OPTIONS]),
);
tracing::info!("Starting webserver");
axum::Server::bind(&"0.0.0.0:3001".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
Ok(())
}
#[derive(Serialize, Deserialize)]
pub struct Credentials {
pub username: String,
pub password: String,
}
async fn login(
Json(credentials): Json<Credentials>,
Extension(pool): Extension<PgPool>,
mut session: WritableSession,
) -> Result<Json<Value>, error::AppError> {
let us = users_service::UserService::new(pool);
match us
.validate_user(credentials.username, credentials.password)
.await
.map_err(|e| {
tracing::error!("could not validate user: {}", e);
AppError::InternalServerError
})? {
Some(user_id) => {
if let Err(e) = session.insert("userId", user_id.clone()) {
tracing::error!("could not insert session: {}", e);
return Err(AppError::InternalServerError);
}
Ok(Json(json!({ "userId": user_id })))
}
None => Err(AppError::WrongCredentials),
}
}
async fn register(
Json(credentials): Json<Credentials>,
Extension(pool): Extension<PgPool>,
) -> Result<Json<Value>, error::AppError> {
let us = users_service::UserService::new(pool)
.add_user(credentials.username, credentials.password)
.await
.map_err(|e| {
tracing::error!("could not add user: {}", e);
AppError::InternalServerError
})?;
Ok(Json(json!({ "userId": us })))
}

View File

@@ -1 +0,0 @@
pub mod users_service;

View File

@@ -1,78 +0,0 @@
use anyhow::anyhow;
use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use rand_core::OsRng;
use sqlx::{Pool, Postgres};
pub struct UserService {
pgx: Pool<Postgres>,
}
impl UserService {
pub fn new(pgx: Pool<Postgres>) -> Self {
Self { pgx }
}
pub async fn add_user(&self, username: String, password: String) -> anyhow::Result<String> {
let hashed_password = self.hash_password(password)?;
let rec = sqlx::query!(
r#"
INSERT INTO users (username, password_hash)
VALUES ( $1, $2 )
RETURNING id
"#,
username,
hashed_password
)
.fetch_one(&self.pgx)
.await?;
Ok(rec.id.to_string())
}
pub async fn validate_user(
&self,
username: String,
password: String,
) -> anyhow::Result<Option<String>> {
let rec = sqlx::query!(
r#"
SELECT * from users
where username=$1
"#,
username,
)
.fetch_optional(&self.pgx)
.await?;
match rec {
Some(user) => match self.validate_password(password, user.password_hash)? {
true => Ok(Some(user.id.to_string())),
false => Ok(None),
},
None => Ok(None),
}
}
fn hash_password(&self, password: String) -> anyhow::Result<String> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(password.as_bytes(), &salt)
.map_err(|e| anyhow!(e))?
.to_string();
Ok(password_hash)
}
fn validate_password(&self, password: String, hashed_password: String) -> anyhow::Result<bool> {
let argon2 = Argon2::default();
let parsed_hash = PasswordHash::new(&hashed_password).map_err(|e| anyhow!(e))?;
match argon2.verify_password(password.as_bytes(), &parsed_hash) {
Ok(..) => Ok(true),
Err(..) => Ok(false),
}
}
}

View File

@@ -1,33 +0,0 @@
{
"query": "\n SELECT * from users\n where username=$1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "password_hash",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "d3f222cf6c3d9816705426fdbed3b13cb575bb432eb1f33676c0b414e67aecaf"
}

View File

@@ -1,8 +0,0 @@
[package]
name = "como_core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -1,2 +0,0 @@

View File

@@ -1,12 +0,0 @@
[package]
name = "como_domain"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.60"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.68"
uuid = { version = "1.1.2", features = ["v4", "fast-rng"] }

View File

@@ -1 +0,0 @@
pub mod users;

View File

@@ -1,12 +0,0 @@
pub mod requests;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug)]
pub struct UserDto {
#[serde(skip_serializing, skip_deserializing)]
pub id: Uuid,
pub username: String,
pub email: String,
}

View File

@@ -1,8 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct CreateUserDto {
pub username: String,
pub email: String,
pub password: String,
}

1
crates/omnia/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

15
crates/omnia/Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "omnia"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.4.11", features = ["string"] }
color-eyre = "0.6.2"
eyre = "0.6.10"
tokio = { version = "1.35.0", features = ["full"] }
tracing = { version = "0.1.40", features = ["log"] }
tracing-subscriber = { version = "0.3.18", features = ["tracing"] }
uuid = { version = "1.6.1", features = ["v4"] }

94
crates/omnia/src/main.rs Normal file
View File

@@ -0,0 +1,94 @@
use cli::CliCommand;
use inbox::services::Inbox;
#[tokio::main]
async fn main() -> eyre::Result<()> {
tracing_subscriber::fmt().init();
if let Err(e) = color_eyre::install() {
tracing::error!("failed to install color_eyre: {}", e);
}
let cmd = clap::Command::new("omnia");
let inbox = Inbox::new();
let cmd = cmd.subcommand(inbox.get_command());
let matches = cmd.get_matches();
match matches.subcommand() {
Some((name, args)) => {
tracing::debug!("executing: {}", name);
if inbox.matches_command(name) {
inbox.execute_command(args).await?;
}
}
None => {
tracing::info!("executing raw command")
}
}
Ok(())
}
pub mod cli {
use clap::{ArgMatches, Command};
pub trait CliCommand {
fn get_name(&self) -> String;
fn get_command(&self) -> Command {
Command::new(self.get_name())
}
fn matches_command(&self, name: &str) -> bool {
name == self.get_name()
}
async fn execute_command(&self, args: &ArgMatches) -> eyre::Result<()>;
}
}
pub mod inbox {
pub mod cli {}
pub mod domain {
#[derive(Debug, Clone)]
pub struct InboxItem {
pub title: String,
pub description: Option<String>,
pub project: Option<String>,
}
}
pub mod services {
use crate::cli::CliCommand;
use super::domain::*;
pub struct Inbox {}
impl Inbox {
pub fn new() -> Self {
Self {}
}
pub async fn add_item(&self, inbox_item: InboxItem) -> eyre::Result<uuid::Uuid> {
Ok(uuid::Uuid::new_v4())
}
}
impl CliCommand for Inbox {
fn get_name(&self) -> String {
"inbox".to_string()
}
async fn execute_command(&self, args: &clap::ArgMatches) -> eyre::Result<()> {
self.add_item(InboxItem {
title: "something".into(),
description: Some("description".into()),
project: Some("project".into()),
})
.await?;
Ok(())
}
}
}
}

View File

@@ -1,19 +0,0 @@
# yaml-language-server: $schema=https://git.front.kjuulh.io/kjuulh/cuddle/raw/branch/main/schemas/base.json
base: "git@git.front.kjuulh.io:kjuulh/cuddle-rust-plan.git"
vars:
service: "como-backend"
deployments: "git@git.front.kjuulh.io:como/deployments.git"
scripts:
render_como_templates:
type: shell
local_up:
type: shell
local_down:
type: shell
run_como:
type: shell
migrate_como:
type: shell

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -e
cuddle_cli render_template --template-file $TMP/docker-compose.local_up.yml.tmpl --dest $TMP/docker-compose.local_up.yml
docker compose -f $TMP/docker-compose.local_up.yml down -v

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -e
cuddle_cli render_template --template-file $TMP/docker-compose.local_up.yml.tmpl --dest $TMP/docker-compose.local_up.yml
docker compose -f $TMP/docker-compose.local_up.yml up -d --remove-orphans --build

View File

@@ -1,6 +0,0 @@
#!/bin/bash
export $(cat .env | xargs)
cargo sqlx migrate run --source como_bin/db/migrations --database-url=$DATABASE_URL

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -e
deploymentrepo="$TMP/deployments"
CUDDLE_FETCH_POLICY=never cuddle_cli render_template \
--template-file "$TMP/.env.example.tmpl" \
--dest "$deploymentrepo/$SERVICE/env.example"

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -e
cargo run como_bin/

View File

@@ -1,4 +0,0 @@
POSTGRES_DB=como
POSTGRES_USER=como
POSTGRES_PASSWORD=somenotverysecurepassword
DATABASE_URL="postgres://como:somenotverysecurepassword@localhost:5432/como"

View File

@@ -1,7 +0,0 @@
target/
.git/
.cuddle/
scripts/
cuddle.yaml
local.sh
README.md

View File

@@ -1,17 +0,0 @@
version: '3.7'
services:
db:
build:
context: .
dockerfile: local_up.Dockerfile
restart: always
environment:
- POSTGRES_PASSWORD=somenotverysecurepassword
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:

View File

@@ -1,8 +0,0 @@
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER como WITH PASSWORD 'somenotverysecurepassword';
CREATE DATABASE como;
GRANT ALL PRIVILEGES ON DATABASE como TO como;
EOSQL

View File

@@ -1,3 +0,0 @@
FROM postgres:14-alpine
COPY *.sh /docker-entrypoint-initdb.d/