feat: release build bundles assets
66
Cargo.lock
generated
@@ -1046,6 +1046,10 @@ dependencies = [
|
||||
"futures",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"minijinja-embed",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2014,6 +2018,7 @@ dependencies = [
|
||||
"common",
|
||||
"composite-retrieval",
|
||||
"futures",
|
||||
"include_dir",
|
||||
"json-stream-parser",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
@@ -2027,6 +2032,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tower-serve-static",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -2368,6 +2374,25 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||
dependencies = [
|
||||
"include_dir_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir_macros"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -3421,6 +3446,26 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
@@ -5284,6 +5329,27 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-serve-static"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "148022c3d604d85a3b558ef7154aa90aaec5f9506beae64f6ad4e856306d287f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"include_dir",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::types::{analytics::Analytics, system_settings::SystemSettings, Stored
|
||||
use axum_session::{SessionConfig, SessionError, SessionStore};
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use futures::Stream;
|
||||
use std::ops::Deref;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
use surrealdb::{
|
||||
engine::any::{connect, Any},
|
||||
opt::auth::Root,
|
||||
@@ -15,6 +15,9 @@ use surrealdb::{
|
||||
pub struct SurrealDbClient {
|
||||
pub client: Surreal<Any>,
|
||||
}
|
||||
pub trait ProvidesDb {
|
||||
fn db(&self) -> &Arc<SurrealDbClient>;
|
||||
}
|
||||
|
||||
impl SurrealDbClient {
|
||||
/// # Initialize a new datbase client
|
||||
|
||||
@@ -4,6 +4,10 @@ pub use minijinja_contrib;
|
||||
pub use minijinja_embed;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait ProvidesTemplateEngine {
|
||||
fn template_engine(&self) -> &Arc<TemplateEngine>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TemplateEngine {
|
||||
// Use AutoReload for debug builds (debug_assertions is true)
|
||||
|
||||
@@ -30,6 +30,8 @@ plotly = "0.12.1"
|
||||
surrealdb = "2.0.4"
|
||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||
chrono-tz = "0.10.1"
|
||||
tower-serve-static = "0.1.1"
|
||||
include_dir = "0.7.4"
|
||||
|
||||
common = { path = "../common" }
|
||||
composite-retrieval = { path = "../composite-retrieval" }
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
5974
crates/html-router/assets/style.css
Normal file
@@ -1,48 +1,43 @@
|
||||
use axum_session::SessionStore;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use common::create_template_engine;
|
||||
use common::storage::db::SurrealDbClient;
|
||||
use common::utils::config::AppConfig;
|
||||
use common::utils::template_engine::TemplateEngine;
|
||||
use common::utils::template_engine::{ProvidesTemplateEngine, TemplateEngine};
|
||||
use common::{create_template_engine, storage::db::ProvidesDb};
|
||||
use std::sync::Arc;
|
||||
use surrealdb::engine::any::Any;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{OpenAIClientType, SessionStoreType};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HtmlState {
|
||||
pub db: Arc<SurrealDbClient>,
|
||||
pub openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
|
||||
pub openai_client: Arc<OpenAIClientType>,
|
||||
pub templates: Arc<TemplateEngine>,
|
||||
pub session_store: Arc<SessionStore<SessionSurrealPool<Any>>>,
|
||||
pub session_store: Arc<SessionStoreType>,
|
||||
}
|
||||
|
||||
impl HtmlState {
|
||||
pub async fn new(config: &AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
pub fn new_with_resources(
|
||||
db: Arc<SurrealDbClient>,
|
||||
openai_client: Arc<OpenAIClientType>,
|
||||
session_store: Arc<SessionStoreType>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let template_engine = create_template_engine!("templates");
|
||||
debug!("Template engine created for html_router.");
|
||||
|
||||
let surreal_db_client = Arc::new(
|
||||
SurrealDbClient::new(
|
||||
&config.surrealdb_address,
|
||||
&config.surrealdb_username,
|
||||
&config.surrealdb_password,
|
||||
&config.surrealdb_namespace,
|
||||
&config.surrealdb_database,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
surreal_db_client.ensure_initialized().await?;
|
||||
|
||||
let openai_client = Arc::new(async_openai::Client::new());
|
||||
|
||||
let session_store = Arc::new(surreal_db_client.create_session_store().await?);
|
||||
|
||||
let app_state = HtmlState {
|
||||
db: surreal_db_client.clone(),
|
||||
templates: Arc::new(template_engine),
|
||||
openai_client: openai_client.clone(),
|
||||
Ok(Self {
|
||||
db,
|
||||
openai_client,
|
||||
session_store,
|
||||
};
|
||||
|
||||
Ok(app_state)
|
||||
templates: Arc::new(template_engine),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ProvidesDb for HtmlState {
|
||||
fn db(&self) -> &Arc<SurrealDbClient> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
impl ProvidesTemplateEngine for HtmlState {
|
||||
fn template_engine(&self) -> &Arc<TemplateEngine> {
|
||||
&self.templates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ pub mod router_factory;
|
||||
pub mod routes;
|
||||
|
||||
use axum::{extract::FromRef, Router};
|
||||
use axum_session::Session;
|
||||
use axum_session::{Session, SessionStore};
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use common::storage::types::user::User;
|
||||
@@ -14,6 +14,8 @@ use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
pub type AuthSessionType = AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>;
|
||||
pub type SessionType = Session<SessionSurrealPool<Any>>;
|
||||
pub type SessionStoreType = SessionStore<SessionSurrealPool<Any>>;
|
||||
pub type OpenAIClientType = async_openai::Client<async_openai::config::OpenAIConfig>;
|
||||
|
||||
/// Html routes
|
||||
pub fn html_routes<S>(app_state: &HtmlState) -> Router<S>
|
||||
|
||||
@@ -3,31 +3,28 @@ use axum::{
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
};
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::engine::any::Any;
|
||||
|
||||
use common::storage::types::analytics::Analytics;
|
||||
use common::storage::{db::ProvidesDb, types::analytics::Analytics};
|
||||
|
||||
use crate::html_state::HtmlState;
|
||||
use crate::SessionType;
|
||||
|
||||
pub async fn analytics_middleware(
|
||||
State(state): State<HtmlState>,
|
||||
session: axum_session::Session<SessionSurrealPool<Any>>,
|
||||
/// Middleware to count unique visitors and page loads
|
||||
pub async fn analytics_middleware<S>(
|
||||
State(state): State<S>,
|
||||
session: SessionType,
|
||||
request: Request,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
// Get the path from the request
|
||||
) -> Response
|
||||
where
|
||||
S: ProvidesDb + Clone + Send + Sync + 'static,
|
||||
{
|
||||
let path = request.uri().path();
|
||||
|
||||
// Only count if it's a main page request (not assets or other resources)
|
||||
if !path.starts_with("/assets") && !path.starts_with("/_next") && !path.contains('.') {
|
||||
if !path.starts_with("/assets") && !path.contains('.') {
|
||||
if !session.get::<bool>("counted_visitor").unwrap_or(false) {
|
||||
let _ = Analytics::increment_visitors(&state.db).await;
|
||||
let _ = Analytics::increment_visitors(state.db()).await;
|
||||
session.set("counted_visitor", true);
|
||||
}
|
||||
|
||||
let _ = Analytics::increment_page_loads(&state.db).await;
|
||||
let _ = Analytics::increment_page_loads(state.db()).await;
|
||||
}
|
||||
|
||||
next.run(request).await
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::html_state::HtmlState;
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse, Response},
|
||||
Extension,
|
||||
};
|
||||
use common::error::AppError;
|
||||
use common::{error::AppError, utils::template_engine::ProvidesTemplateEngine};
|
||||
use minijinja::{context, Value};
|
||||
use serde::Serialize;
|
||||
use tracing::error;
|
||||
@@ -106,58 +105,45 @@ impl IntoResponse for TemplateResponse {
|
||||
}
|
||||
}
|
||||
|
||||
struct TemplateStateWrapper {
|
||||
state: HtmlState,
|
||||
template_response: TemplateResponse,
|
||||
}
|
||||
pub async fn with_template_response<S>(State(state): State<S>, response: Response) -> Response
|
||||
where
|
||||
S: ProvidesTemplateEngine + Clone + Send + Sync + 'static,
|
||||
{
|
||||
if let Some(template_response) = response.extensions().get::<TemplateResponse>().cloned() {
|
||||
let template_engine = state.template_engine();
|
||||
|
||||
impl IntoResponse for TemplateStateWrapper {
|
||||
fn into_response(self) -> Response {
|
||||
let template_engine = &self.state.templates;
|
||||
|
||||
match &self.template_response.template_kind {
|
||||
match &template_response.template_kind {
|
||||
TemplateKind::Full(name) => {
|
||||
match template_engine.render(name, &self.template_response.context) {
|
||||
match template_engine.render(name, &template_response.context) {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(e) => {
|
||||
error!("Failed to render template: {:?}", e);
|
||||
error!("Failed to render template '{}': {:?}", name, e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, fallback_error()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
TemplateKind::Partial(template, block) => {
|
||||
match template_engine.render_block(template, block, &self.template_response.context)
|
||||
{
|
||||
match template_engine.render_block(template, block, &template_response.context) {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(e) => {
|
||||
error!("Failed to render block: {:?}", e);
|
||||
error!("Failed to render block '{}/{}': {:?}", template, block, e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, fallback_error()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
TemplateKind::Error(status) => {
|
||||
match template_engine.render("errors/error.html", &self.template_response.context) {
|
||||
match template_engine.render("errors/error.html", &template_response.context) {
|
||||
Ok(html) => (*status, Html(html)).into_response(),
|
||||
Err(_) => (*status, fallback_error()).into_response(),
|
||||
Err(e) => {
|
||||
error!("Failed to render error template: {:?}", e);
|
||||
(*status, fallback_error()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
TemplateKind::Redirect(path) => {
|
||||
(StatusCode::OK, [(axum_htmx::HX_REDIRECT, path.clone())], "").into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn with_template_response(
|
||||
State(state): State<HtmlState>,
|
||||
response: Response,
|
||||
) -> Response {
|
||||
if let Some(template_response) = response.extensions().get::<TemplateResponse>().cloned() {
|
||||
TemplateStateWrapper {
|
||||
state,
|
||||
template_response,
|
||||
}
|
||||
.into_response()
|
||||
} else {
|
||||
response
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use axum_session_auth::{AuthConfig, AuthSessionLayer};
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use common::storage::types::user::User;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use crate::{
|
||||
html_state::HtmlState,
|
||||
@@ -18,6 +17,27 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_asset_service {
|
||||
// Takes the relative path to the asset directory
|
||||
($relative_path:expr) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let crate_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let assets_path = crate_dir.join($relative_path);
|
||||
tracing::debug!("Assets: Serving from filesystem: {:?}", assets_path);
|
||||
tower_http::services::ServeDir::new(assets_path)
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
tracing::debug!("Assets: Serving embedded directory");
|
||||
static ASSETS_DIR: include_dir::Dir<'static> =
|
||||
include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets");
|
||||
tower_serve_static::ServeDir::new(&ASSETS_DIR)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub struct RouterFactory<S> {
|
||||
app_state: HtmlState,
|
||||
public_routers: Vec<Router<S>>,
|
||||
@@ -108,27 +128,35 @@ where
|
||||
}
|
||||
|
||||
// Add public assets to public router
|
||||
if let Some(assets) = self.public_assets_config {
|
||||
public_router =
|
||||
public_router.nest_service(&assets.path, ServeDir::new(assets.directory));
|
||||
if let Some(assets_config) = self.public_assets_config {
|
||||
// Call the macro using the stored relative directory path
|
||||
let asset_service = create_asset_service!(&assets_config.directory);
|
||||
// Nest the resulting service under the stored URL path
|
||||
public_router = public_router.nest_service(&assets_config.path, asset_service);
|
||||
}
|
||||
|
||||
// Start with an empty protected router
|
||||
let mut protected_router = Router::new();
|
||||
|
||||
// Merge all protected routers
|
||||
// Check if there are any protected routers
|
||||
let has_protected_routes =
|
||||
!self.protected_routers.is_empty() || !self.nested_protected_routes.is_empty();
|
||||
|
||||
// Merge root-level protected routers
|
||||
for router in self.protected_routers {
|
||||
protected_router = protected_router.merge(router);
|
||||
}
|
||||
|
||||
// Add nested protected routes
|
||||
// Nest protected routers
|
||||
for (path, router) in self.nested_protected_routes {
|
||||
protected_router = protected_router.nest(&path, router);
|
||||
}
|
||||
|
||||
// Apply auth middleware to all protected routes
|
||||
let protected_router =
|
||||
protected_router.route_layer(from_fn_with_state(self.app_state.clone(), require_auth));
|
||||
// Apply auth middleware
|
||||
if has_protected_routes {
|
||||
protected_router = protected_router
|
||||
.route_layer(from_fn_with_state(self.app_state.clone(), require_auth));
|
||||
}
|
||||
|
||||
// Combine public and protected routes
|
||||
let mut router = Router::new().merge(public_router).merge(protected_router);
|
||||
@@ -142,11 +170,11 @@ where
|
||||
router
|
||||
.layer(from_fn_with_state(
|
||||
self.app_state.clone(),
|
||||
analytics_middleware,
|
||||
analytics_middleware::<HtmlState>,
|
||||
))
|
||||
.layer(map_response_with_state(
|
||||
self.app_state.clone(),
|
||||
with_template_response,
|
||||
with_template_response::<HtmlState>,
|
||||
))
|
||||
.layer(
|
||||
AuthSessionLayer::<User, String, SessionSurrealPool<Any>, Surreal<Any>>::new(Some(
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{% extends "body_base.html" %}
|
||||
{% block main %}
|
||||
<main class="flex justify-center grow mt-2 sm:mt-4 pb-10">
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-4">
|
||||
<!-- Documentation Menu -->
|
||||
<aside class="bg-base-200 rounded-lg p-4">
|
||||
{% include "documentation/menu.html" %}
|
||||
</aside>
|
||||
<!-- Main Content -->
|
||||
|
||||
<article
|
||||
class="prose prose-sm md:prose-base prose-h1:mb-2 prose-h2:my-2 prose-p:my-2 prose-ul:my-2 prose-pre:my-2 flex mx-auto justify-center flex-col">
|
||||
{% block article %}
|
||||
{% endblock %}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -1,58 +0,0 @@
|
||||
{% extends 'documentation/base.html' %}
|
||||
{% block article %}
|
||||
<h1>Get Started with Minne</h1>
|
||||
<p>Minne offers two installation options to suit your needs:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<strong>Hosted Version:</strong> Enjoy a hassle‐free experience by signing up for the ready‐to‐use service.
|
||||
Simply navigate to <code>/signup</code> to create an account.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Self-Hosted:</strong> Gain full control by running Minne on your own infrastructure. Visit
|
||||
<a href="https://github.com/perstarkse/minne">GitHub</a> to download the latest release. After extracting the
|
||||
release, open the <code>config.yaml</code> file and set the following configurations:
|
||||
</li>
|
||||
</ol>
|
||||
<pre class="overflow-x-auto text-sm">
|
||||
<code class="break-words whitespace-pre-wrap">
|
||||
OPENAI_API_KEY: your_api_key
|
||||
DB_ADDRESS: your_db_address
|
||||
DB_USER: your_db_user
|
||||
DB_PASSWORD: your_db_password
|
||||
</code>
|
||||
</pre>
|
||||
<p>The database settings relate to a running instance of SurrealDB. You can opt for their cloud solution or run your
|
||||
own instance.</p>
|
||||
|
||||
<p>Once your configuration is complete, start both the server and the worker. They can be hosted on separate
|
||||
machines, with different resource requirements:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Server:</strong> Lightweight. A minimum of 1 core and 256MB of RAM is recommended.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Worker:</strong> Handles content parsing and creation of database entities. It's recommended to allocate at
|
||||
least two cores and 1024 MB RAM. It will run on less but might run into constraints depending on the content being
|
||||
parsed.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>After launching the services, navigate to <code><your_url>:3000/signup</code> to register. The first
|
||||
account created will automatically receive admin permissions, allowing you to later disable further registrations
|
||||
via the <code>/admin</code> page if desired.</p>
|
||||
|
||||
<p>From the homepage (<code>/</code>), you can:</p>
|
||||
|
||||
<ul>
|
||||
<li>Submit content, including files, videos, and URLs for ingestion.</li>
|
||||
<li>Monitor job statuses and manage your existing content.</li>
|
||||
<li>Search your content or start a chat conversation for assistance.</li>
|
||||
</ul>
|
||||
|
||||
<p>Visit the <code>/knowledge</code> page to view your content organized by different sections. This page also
|
||||
provides a visual demonstration of the graph database structure, enhancing your understanding of content
|
||||
relationships.</p>
|
||||
|
||||
<p>This streamlined setup ensures intuitive onboarding while offering robust customization options. Whether you are
|
||||
a novice or an advanced user, Minne is designed to deliver a smooth experience and reliable performance.</p>
|
||||
{% endblock %}
|
||||
@@ -1,26 +0,0 @@
|
||||
{% extends "documentation/base.html" %}
|
||||
{% block article %}
|
||||
<h1 class="text-3xl mb-2">Documentation</h1>
|
||||
<p>
|
||||
Personalised Knowledge Management (PKM) is a system designed to help individuals organise, store, and retrieve
|
||||
information effectively. It empowers users to create a personalised workflow for managing knowledge, enabling
|
||||
better decision-making and productivity.
|
||||
</p>
|
||||
<p>
|
||||
This documentation will guide you through the core concepts, tools, and best practices for building and
|
||||
maintaining your own PKM system.
|
||||
</p>
|
||||
<div class="card bg-base-200 rounded-lg shadow-md">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title not-prose">Getting Started</h3>
|
||||
<p>
|
||||
To begin, explore the sections in the navigation menu. Each section provides detailed insights into
|
||||
different
|
||||
aspects of PKM, from foundational principles to advanced techniques.
|
||||
</p>
|
||||
<div class="card-actions">
|
||||
<a href="/documentation/quick-start" class="btn btn-primary text-primary-content" hx-boost="true">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,28 +0,0 @@
|
||||
<ul class="menu bg-base-200 rounded-box w-full ">
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/index' }}" href="/documentation">Start</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/get-started' }}"
|
||||
href="/documentation/get-started">Get Started</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/mobile-friendly' }}"
|
||||
href="/documentation/mobile-friendly">Mobile friendly</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/privacy-policy' }}"
|
||||
href="/documentation/privacy-policy">Privacy Policy</a></li>
|
||||
<li>
|
||||
<details open>
|
||||
<summary>Core Concepts</summary>
|
||||
<ul>
|
||||
<li><a hx-boost="true" href="/documentation/submenu1">What is PKM?</a></li>
|
||||
<li><a hx-boost="true" href="/documentation/submenu2">Benefits of PKM</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
<details>
|
||||
<summary>Tools & Techniques</summary>
|
||||
<ul>
|
||||
<li><a hx-boost="true" href="/documentation/tools">Tools for PKM</a></li>
|
||||
<li><a hx-boost="true" href="/documentation/techniques">Effective Techniques</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li><a hx-boost="true" href="/documentation/faq">FAQ</a></li>
|
||||
</ul>
|
||||
@@ -1,20 +0,0 @@
|
||||
{% extends 'documentation/base.html' %}
|
||||
{% block article %}
|
||||
<h1>Mobile Friendly Ingression: How to Submit Content from iOS to Minne</h1>
|
||||
<p>Minne is built with simplicity in mind. Whether you wish to save a file, capture a thought, or share a page,
|
||||
submitting content is effortless. Our server provides API access that enables users to perform actions using a
|
||||
personalized API key.</p>
|
||||
|
||||
<p>An iOS shortcut has been developed to streamline the process of sending content. To begin, navigate to
|
||||
<code>/account</code> and generate an API key. Once created, you will see an option to download the iOS shortcut.
|
||||
</p>
|
||||
|
||||
<p>After downloading the shortcut, update the "Get response from URL" authentication headers with your API key. If
|
||||
you are self-hosting, ensure the URL is adjusted accordingly.</p>
|
||||
|
||||
<p>The shortcut integrates seamlessly with iOS. When you "share with Minne," you will be prompted to provide
|
||||
instructions to the AI and to either choose an existing category or create a new one for your submission.</p>
|
||||
|
||||
<p>While an Android solution is in the works, for now you can add the web app to your home screen as a Progressive
|
||||
Web App (PWA) for a similar mobile-friendly experience.</p>
|
||||
{% endblock %}
|
||||
@@ -1,27 +0,0 @@
|
||||
{% extends 'documentation/base.html' %}
|
||||
{% block article %}
|
||||
<h1>Privacy Policy</h1>
|
||||
<p>We value your privacy and are committed to protecting your personal information. This policy
|
||||
outlines how we handle your data and ensures transparency in our practices.</p>
|
||||
|
||||
<h2>Data Collection</h2>
|
||||
<p>We only collect data that is necessary for the functionality of our services. Any data you upload to
|
||||
our site remains your property and will not be shared with third parties unless required by law.</p>
|
||||
|
||||
<h2>Cookies</h2>
|
||||
<p>We do not use cookies for tracking or analytics. The cookies we employ are strictly for session
|
||||
management, ensuring a smooth and secure user experience.</p>
|
||||
|
||||
<h2>No Unnecessary Data Extraction</h2>
|
||||
<p>We believe that unnecessary data extraction is unethical and a poor practice. We only collect the
|
||||
minimum amount of data required to provide our services effectively, ensuring your privacy is respected at all
|
||||
times.</p>
|
||||
|
||||
<h2>Your Rights</h2>
|
||||
<p>You have the right to access, modify, or delete your data at any time. If you have any concerns
|
||||
about how your data is handled, please contact us.</p>
|
||||
|
||||
<h2>Changes to This Policy</h2>
|
||||
<p>We may update this privacy policy from time to time. Any changes will be posted on this page, and we
|
||||
encourage you to review it periodically.</p>
|
||||
{% endblock %}
|
||||
@@ -21,8 +21,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Get config
|
||||
let config = get_config()?;
|
||||
|
||||
// Set up server components
|
||||
let html_state = HtmlState::new(&config).await?;
|
||||
// Set up router states
|
||||
let db = Arc::new(
|
||||
SurrealDbClient::new(
|
||||
&config.surrealdb_address,
|
||||
&config.surrealdb_username,
|
||||
&config.surrealdb_password,
|
||||
&config.surrealdb_namespace,
|
||||
&config.surrealdb_database,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
// Ensure db is initialized
|
||||
db.ensure_initialized().await?;
|
||||
|
||||
let session_store = Arc::new(db.create_session_store().await?);
|
||||
let openai_client = Arc::new(async_openai::Client::new());
|
||||
|
||||
let html_state = HtmlState::new_with_resources(db, openai_client, session_store)?;
|
||||
|
||||
let api_state = ApiState {
|
||||
db: html_state.db.clone(),
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use api_router::{api_routes_v1, api_state::ApiState};
|
||||
use axum::{extract::FromRef, Router};
|
||||
use common::utils::config::get_config;
|
||||
use common::{storage::db::SurrealDbClient, utils::config::get_config};
|
||||
use html_router::{html_routes, html_state::HtmlState};
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
@@ -18,7 +20,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = get_config()?;
|
||||
|
||||
// Set up router states
|
||||
let html_state = HtmlState::new(&config).await?;
|
||||
let db = Arc::new(
|
||||
SurrealDbClient::new(
|
||||
&config.surrealdb_address,
|
||||
&config.surrealdb_username,
|
||||
&config.surrealdb_password,
|
||||
&config.surrealdb_namespace,
|
||||
&config.surrealdb_database,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
// Ensure db is initialized
|
||||
db.ensure_initialized().await?;
|
||||
|
||||
let session_store = Arc::new(db.create_session_store().await?);
|
||||
let openai_client = Arc::new(async_openai::Client::new());
|
||||
|
||||
let html_state = HtmlState::new_with_resources(db, openai_client, session_store)?;
|
||||
|
||||
let api_state = ApiState {
|
||||
db: html_state.db.clone(),
|
||||
};
|
||||
|
||||
2
todo.md
@@ -2,7 +2,7 @@
|
||||
\[\] configs primarily get envs
|
||||
\[\] filtering on categories
|
||||
\[\] integrate assets folder in release build
|
||||
\[\] integrate templates in release build
|
||||
\[x] integrate templates in release build
|
||||
\[\] markdown rendering in client
|
||||
\[\] openai api key in config
|
||||
\[\] three js graph explorer
|
||||
|
||||