Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
470814c
feat: finish the rest of the guide chapters, add FAQ and Licenses pages
m4tx Feb 17, 2025
b1b7414
feat: add FAQ
m4tx Feb 18, 2025
2832577
chore: fix invalid struct name in introduction
m4tx Feb 18, 2025
ead9edf
Secure admin user creation (#8)
mandarvaze Feb 18, 2025
de9a689
feat: add a Framework comparison page stub
m4tx Feb 19, 2025
7acc764
docs: update guide introduction dynamic routes example (missing closi…
kecci Mar 13, 2025
e5f420f
chore: fix the code in admin-panel.md
m4tx Mar 25, 2025
e2feabd
feat: setup guide versioning (#17)
Mar 26, 2025
c1f4252
feat: add v0.2
m4tx Mar 26, 2025
13bb90c
docs: update migrations command to match new cli version
seqre Mar 4, 2025
f44482c
docs: Fix Urls import path in Templates guide (#21)
julienr Mar 31, 2025
6b4a12c
docs: optimize imports
m4tx Mar 31, 2025
5d00681
docs: fix command typo in `db-models.md` (#28)
ClanEver May 12, 2025
68b6672
feat: add v0.3 docs (#29)
m4tx May 13, 2025
6fc1b74
feat: cot v0.4 docs, "master" version (#44)
m4tx Sep 11, 2025
e421209
feat: v0.5 docs (#60)
m4tx Jan 21, 2026
170683b
chore: use cot::Template instead of askama::Template in guide (#62)
m4tx Jan 22, 2026
937e45e
fix: admin user retrieval code in `admin-panel.md` (#64)
mjul Jan 25, 2026
148de32
chore: remove disclaimer from introduction.md (#71)
m4tx Feb 16, 2026
e79c39c
chore: update on functionality of `cot::reverse` in docs (#73)
xelab04 Feb 22, 2026
36ed504
Add 'docs/' from commit 'e79c39c42cac3342e38638aced289b3a5b74ae44'
m4tx Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions docs/admin-panel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: Admin panel
---

The Cot admin panel provides an automatic interface for managing your models. It allows you to add, edit, delete and view records without writing any custom views or templates. This is perfect for prototyping your application and for managing your data in cases where you don't need a custom interface, as the Cot admin panel is automatically generated based on your models.

## Enabling the Admin Interface

First, add the admin app and the dependencies required to your project in `src/main.rs`:

```rust
use cot::admin::AdminApp;
use cot::auth::db::{DatabaseUser, DatabaseUserApp};
use cot::middleware::SessionMiddleware;
use cot::project::{MiddlewareContext, RegisterAppsContext, RootHandler, RootHandlerBuilder};
use cot::static_files::StaticFilesMiddleware;

struct MyProject;

impl Project for MyProject {
fn register_apps(&self, apps: &mut AppBuilder, _context: &RegisterAppsContext) {
apps.register(DatabaseUserApp::new()); // Needed for admin authentication
apps.register_with_views(AdminApp::new(), "/admin"); // Register the admin app
apps.register_with_views(MyApp, "");
}

fn middlewares(
&self,
handler: RootHandlerBuilder,
app_context: &MiddlewareContext,
) -> RootHandler {
handler
.middleware(StaticFilesMiddleware::from_context(app_context))
.middleware(SessionMiddleware::new()) // Required for admin login
.build()
}
Comment on lines +32 to +36
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SessionMiddleware::new() requires a session store argument; calling it with no args won’t compile. Also, the admin panel setup needs AuthMiddleware (see examples/admin/src/main.rs) to enforce authentication. Consider using SessionMiddleware::from_context(...) and adding AuthMiddleware::new() in the middleware chain.

Copilot uses AI. Check for mistakes.

// ...
}
```

## Admin User Creation

By default, the admin interface uses Cot's authentication system. Therefore, you need to create an admin user if it doesn't exist:

```rust
use async_trait::async_trait; // cargo add async-trait
use cot::ProjectContext;
use cot::auth::db::DatabaseUser;
use cot::common_types::Password;
use std::env;

// In your main.rs:
#[async_trait]
impl App for MyApp {
async fn init(&self, context: &mut ProjectContext) -> cot::Result<()> {
// Check if admin user exists
let admin_username = env::var("ADMIN_USER")
.unwrap_or_else(|_| "admin".to_string());
let user = DatabaseUser::get_by_username(context.database(), &admin_username).await?;
if user.is_none() {
let password = env::var("ADMIN_PASSWORD")
.unwrap_or_else(|_| "change_me".to_string());
// Create admin user
DatabaseUser::create_user(
context.database(),
&admin_username,
&Password::new(&password)
).await?;
}
Ok(())
}
}
```

## Registering Models in the Admin

To make your models appear in the admin interface, you need to implement the `AdminModel` trait. The easiest way is to use the `#[derive(AdminModel)]` macro:

```rust
use cot::admin::AdminModel;
use cot::db::{model, Auto};
use cot::form::Form;

#[derive(Debug, Form, AdminModel)]
#[model]
struct BlogPost {
#[model(primary_key)]
id: Auto<i32>,
title: String,
content: String,
published: bool,
}
```

Note however that in order to derive the `AdminModel` trait, you need to also derive the `Form` and `Model` traits (the latter is provided by the `#[model]` attribute). In addition to that, your model needs to implement the `Display` trait—for instance, in the case above, we could add it like so:

```rust
impl Display for BlogPost {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.title)
}
}
```

After adding the `AdminModel` trait, you can add your model to the admin panel using `DefaultAdminModelManager`. This is as easy as adding the following code to your `App` implementation:

```rust
impl App for MyApp {
fn admin_model_managers(&self) -> Vec<Box<dyn AdminModelManager>> {
vec![Box::new(DefaultAdminModelManager::<BlogPost>::new())]
}

// ...
}
```

Now your model can be managed through the admin interface at `http://localhost:8000/admin/`!

## Summary

In this chapter, you learned how to enable the Cot admin panel, create an admin user, and register your models in the admin interface. In the next chapter, we'll learn how to handle static assets in Cot.
112 changes: 112 additions & 0 deletions docs/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: Caching
---

Cot provides a flexible caching system that allows you to store and retrieve data quickly, reducing the load on your database and improving response times. The caching system is designed to be pluggable, meaning you can switch between different cache backends without changing your application code.

## Configuration

To use the caching system, you first need to configure it in your `ProjectConfig` (or via configuration files).

### Configuration via TOML

You can configure the cache in your `config/*.toml` files:

```toml
[cache]
prefix = "myapp" # Optional: prefix for all cache keys
max_retries = 3 # Optional: max retries for cache operations (default: 3)
timeout = "5s" # Optional: timeout for cache operations (default: 5s)

[cache.store]
type = "memory" # Options: "memory", "redis", "file" (if enabled)
```
Comment on lines +16 to +23
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache config docs state the default timeout is 5s, but the default Timeout is 300s (5 minutes) in cot::config (Timeout::default()). Please correct the documented default to avoid confusing users.

Copilot uses AI. Check for mistakes.

For Redis:

```toml
[cache.store]
type = "redis"
url = "redis://127.0.0.1:6379"
pool_size = 20 # Optional: connection pool size
```

## Usage

You can access the cache by using the `Cache` extractor. The cache interface provides standard methods like `get`, `insert`, `remove`, etc.

```rust
use cot::cache::Cache;
use cot::html::Html;

async fn cache_example(cache: Cache) -> cot::Result<Html> {
// Insert a value (uses default expiration if set in config, or infinite)
cache.insert("user_1_name", "Alice").await?;

// Get a value
let name: Option<String> = cache.get("user_1_name").await?;

if let Some(n) = name {
println!("Found user: {}", n);
}

Ok(Html::new("OK"))
}
```

### Expiration

You can set an expiration time for specific keys:

```rust
use std::time::Duration;
use cot::config::Timeout;

// Cache for 60 seconds
cache.insert_expiring(
"temp_key",
"temp_value",
Timeout::After(Duration::from_secs(60))
).await?;
```

## Advanced Topics

#### Lazy Computation

You can use `get_or_insert_with` to lazily compute and cache values:

```rust
let value: String = cache.get_or_insert_with("expensive_key", || async {
// Perform expensive computation
Ok("expensive_result".to_string())
}).await?;
```

#### Prefix

Sharing a cache instance between different environments (e.g., production and dev), or between different versions of the same
application can cause data collisions and bugs. To prevent this, you can specify a prefix for the cache keys. When a prefix
is set, all keys will be formatted as `{prefix}:{key}`, ensuring each server instance has its own isolated namespace.

The prefix can be set in the configuration file:

```toml
[cache]
prefix = "v1"
```

## Cache Backends

Cot supports the following cache backends:

- **Memory**: Stores data in memory. Fast, but data is lost when the server restarts. Good for development or short-lived cache.
- **Redis**: Stores data in a Redis instance. Persistent and shared across multiple server instances. Requires the `redis` feature.
- **File**: Stores data in files. Persistent but slower than memory/Redis. Requires configuring a path.

To use Redis, make sure to enable the `redis` feature in your `Cargo.toml`:

```toml
[dependencies]
cot = { version = "0.5", features = ["redis"] }
```
Loading
Loading