A starter template for actix-web projects that feels very Django-esque. Avoid the boring stuff and move faster.

Overview

Jelly

A.K.A, the actix-web starter you probably wish you had. This is provided as-is, and anyone is free to extend it or rework it as they desire - just give some credit if you base a web framework off of it. :)

A disclaimer: this is used internally, and while it's very usable, it might not be perfect. You might need to tweak a thing or two. Don't be shocked if you need to alter jelly for your own needs. Pull requests for things that should be "standard" are welcome.

What's This?

If you've ever written a web service in Rust that's needed some of the following:

  • User accounts and authentication
  • Form handling
  • Background jobs
  • Transactional emailing
  • Flash messages
  • Async Postgres database (via SQLx)

Then Jelly may be of interest to you. It's explicitly not a framework; it's modeled after Python's Django but tries to not hide the underlying actix-web framework too much. This is done because web frameworks traditionally fall into two categories:

  • The kitchen sink: it has literally everything, and once you need to scale it, you start ripping things out and getting slightly annoyed.
  • The micro framework: Works great for an API service. Absolutely sucks when you start reimplementing the kitchen sink.

Jelly tries to sit in-between those two ideas; think of it as a meta-framework for actix-web. It helps you structure the app and spend less up-front time configuring and tweaking things, and brings important ("table stakes") pieces to the Rust web dev experience.

tl;dr: CRUD web applications are boring, so consider using this to get to the interesting parts.

Getting Started

  • Clone this repository.
  • Edit Cargo.toml to configure your project name, as well as any other settings you need.
  • Ensure you have Postgresql installed.
  • Install sqlx-cli, with:
cargo install --version=0.2.0 sqlx-cli --no-default-features --features postgres
  • Edit .env.example to use your settings.
  • Run the account migrations with sqlx migrate run.
  • Run the server:
cargo run

# Optionally, if you use cargo-watch:
cargo-watch -i templates -i static -i migrations -x run

If you're ready to push a release build, you probably want to run:

cargo build --release --no-default-features --features production

For configuring email dispatch, see the README in templates/email.

Accounts

Accounts is modeled to provide the most common features you would expect from a user system. It provides the following:

  • Registration
    • On signup, will dispatch an email for account verification.
  • Login
    • Password reset functionality is also provided.

Both password reset and account verification implement a one-time-use URL pattern. The flow is a mirror of what Django does; the URL is hashed based on account information, so once the password changes, the URL becomes invalid.

Registration and Login, by default, try to not leak that an existing user account might exist. If a user attempts to register with an already registered email address, the following will happen:

  • The person attempting to register will be shown the "normal" flow, as if they successfully signed up, being told to check their email to verify.
  • The already registered user is sent an email notifying that this happened, and includes a link to password reset - e.g, maybe they're a confused user who just needs to get back in.

Templates

Templates are written in Tera. If you've written templates in Django or Jinja2, they should be very familiar.

The provided templates has a top-level layout.html, which should be your global public layout. The templates/dashboard folder is what a user sees upon logging in.

In development, your templates are automatically reloaded on edit. Jelly also provides a stock "an error happened" view, similar to what Django does.

In production, both of these are disabled.

Static

The static folder is where you can place any static things. In development, actix-files is preconfigured to serve content from that directory, in order to make life easier for just running on your machine. This is disabled in the production build, mostly because we tend to shove this behind Nginx. You can swap this as needed.

Forms

Writing the same email/password/etc verification logic is a chore, and one of the nicer things Django has is Form helpers for this type of thing. If you miss that, Jelly has a forms-ish module that you can use.

For instance, you could do:

forms.rs

use serde::{Deserialize, Serialize};
use jelly::forms::{EmailField, PasswordField, Validation};

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct LoginForm {
    pub email: EmailField,
    pub password: PasswordField
}

impl Validation for LoginForm {
    fn is_valid(&mut self) -> bool {
        self.email.is_valid() && !self.password.value.is_empty()
    }
}

views.rs

/// POST-handler for logging in.
pub async fn authenticate(
    request: HttpRequest,
    form: Form<LoginForm>
) -> Result<HttpResponse> {
    if request.is_authenticated()? {
        return request.redirect("/dashboard/");
    }

    let mut form = form.into_inner();
    if !form.is_valid() {
        return request.render(400, "accounts/login.html", {
            let mut context = Context::new();
            context.insert("error", "Invalid email or password.");
            context.insert("form", &form);
            context
        });
    }

In this case, EmailField will check that the email is a mostly-valid email address. PasswordField will check that it's a "secure" password. Each Field type has an internal errors stack, so you can pass it back to your view and render errors as necessary.

For more supported field types, see the jelly/forms module.

Request Helpers

A personal pet peeve: the default actix-web view definitions are mind-numbingly verbose. Code is read far more than it's written, and thus Jelly includes some choices to make writing views less of a headache: namely, access to things like database pools and authentication are implemented as traits on HttpRequest.

This makes the necessary view imports a bit cleaner, requiring just the prelude for some traits, and makes view definitons much cleaner overall. It's important to note that if, for whatever reason, you need to use standard actix-web view definitions, you totally can - Jelly doesn't restrict this, just provides a (we think) nicer alternative.

Checking a User

You can call request.is_authenticated()? to check if a User is authenticated. This does not incur a database hit, but simply checks against the signed cookie session value.

You can call request.user()? to get the User for a request. This does not incur a database hit, and just loads cached information from the signed cookie session. Users are, by default anonymous - and can be checked with is_anonymous.

If you want the full user Account object, you can call Account::get(user.id, &db_pool).await?, or write your own method.

You can restrict access to only authenticated users on a URL basis by using jelly::guards::Auth; example usage can be found in src/dashboard/mod.rs.

Rendering a Template

You can call request.render(http_code, template_path, model), where:

  • http_code is an HTTP response code.
  • template_path is a relative path to the template you want to load.
  • model is a tera::Context.

Why is http_code just passing a number?`, you might ask. It's personal preference, mostly: developers are intelligent enough to know what an HTTP response code is, and it's far less verbose to just pass the number - and simple enough to scan when you're trying to track down something related to it.

request.render() makes two things available to you by default:

  • user, which is the current User instance from the signed cookie session.
  • flash_messages, which are one-time messages that you can have on a view.

Returning a JSON response

You can call request.json(http_code, obj), where objc is an object that can be serialized to JSON.

Returning a Redirect

You can call request.redirect(path), where path is where you want the user to go.

Setting a Flash Message

You can call request.flash(title, message) to add a Flash message to the request. This is a one-time message, typically used for, say, confirming that something worked.

Getting a Database Pool

You can call request.db_pool()? to get a database pool instance. This can be passed to whatever you need to call for database work.

Queuing a Background Job

You can use accounts/jobs for a basis to create your own background jobs, and register them similar to how they're done in src/main.rs.

You can call request.queue(MyJob {...})? to dispatch a job in the background.

The End

Hopefully, this helps people get going with more web services in Rust, and provides a common base to work off of. There's three things to note here:

  • This is released under a "do-whatever-you-want" license. Just give credit if you use it as the basis for a web framework of your own.
  • Someone else is more than welcome to take this further and make a true web framework.
  • I would argue that actix-web, Rocket, and so on should really just have this kind of thing by default.
  • Thanks to every developer of a sub-crate used in this project; there are too many to list, but the Rust community is hands down one of the best out there.
Comments
  • Awesome Starter! Login takes approximately 10 seconds, new to rust, heres what I have done so far.

    Awesome Starter! Login takes approximately 10 seconds, new to rust, heres what I have done so far.

    I am completely new to rust, long time linux user, and I have done web development for personal projects for many years, but never as a job/career.

    I found this project to be immensely valuable as something to learn from and hopefully use as a starter for many projects! Thank you very much for this.

    I am seeing approximately a 10 second delay while registering or logging in, the only things I have not yet fixed is the email, will be working on that later today, normally in my other projects i just install the linux package 'mailutils', configure postfix, and then use sendmail or mailx to actually send the mail. I am going to be working on this change next once I get a little free time.

    I would really really appreciate it if you could look at my .env values. I guessed at the proper way to set some of them, the only ones I am fairly sure I have correct is BIND_TO, DOMAIN, DATABASE_URL, I am not absolutely positive about the others.

    (I did not redact any of this other than the domain because this is just a test environment for me)

    egrep -v '^#|^$' .env
    
    BIND_TO="0.0.0.0:17001"
    DOMAIN="https://www.example.com:17001"
    DATABASE_URL=postgres://rustweb:1uM8lUyBgSjuRh2KxvfmCIL063ZIktJq@localhost/rustweb""
    SECRET_KEY="2Dgj6QREAJ7ZsENEnXClMBsUhKzQjtYcA0fZDvl85z3U41nz6kDQXzRN7A6OgTc"
    TEMPLATES_GLOB="templates/**/*"
    STATIC_ROOT="static"
    RUST_BACKTRACE=1
    RUST_LOG="info,actix_web=trace,background_jobs_core=debug"
    

    My install is on an Ubuntu 20.04 VM For Posterity I am going to also include my entire install routine below:

    Update OS:

    apt update && apt upgrade

    Install dependencies

    apt install -y curl gnupg2 build-essential libssl-dev pkg-config zip

    Install PostgreSQL (ubuntu 20.04 repo is on postgre 12, this gets you 13)

    echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | sudo tee  /etc/apt/sources.list.d/pgdg.list
    wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
    apt update
    apt install postgresql libpq-dev
    reboot
    psql --version
    

    Install Rust

    curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
    1
    reboot
    rustup update
    rustc --version
    

    Install sqlx-cli

    cargo install sqlx-cli --no-default-features --features postgres

    Set Bash Variables for project name, author name, web URL, postgres password, and SECRET_KEY:

    PROJECT="rustweb" AUTHOR="xekon" WEBURL="https://www.example.com:17001" PGPASS=$(openssl rand -base64 32 | tr -dc _A-Z-a-z-0-9 | head -c 32) SECRET_KEY=$(openssl rand -base64 64 | tr -dc _A-Z-a-z-0-9 | head -c 63)

    Download jelly-actix-web-starter

    wget https://github.com/secretkeysio/jelly-actix-web-starter/archive/refs/heads/trunk.zip
    unzip trunk.zip
    mv jelly-actix-web-starter-trunk "${PROJECT}"
    

    Configure some values in Cargo.toml

    cd $PROJECT
    sed -i "s/^name =.*/name = \"${PROJECT}\"/" Cargo.toml
    sed -i "s/^authors =.*/authors = \[\"${AUTHOR}\"\]/" Cargo.toml
    

    initialize the postgres database:

    sudo -u postgres psql -c "create user ${PROJECT} with password '${PGPASS}' superuser;" -U postgres
    sudo -u postgres psql -c "create database ${PROJECT} with owner ${PROJECT};" -U postgres
    

    Configure some values in .env file:

    mkdir static
    cp .env.example .env
    WEBURL_ESCAPE=$(echo $WEBURL | sed 's/\./\\./g' | sed 's/\//\\\//g')
    sed -i 's/^BIND_TO=.*/BIND_TO="0\.0\.0\.0:17001"/' .env
    sed -i "s/^DOMAIN=.*/DOMAIN=\"${WEBURL_ESCAPE}\"/" .env
    sed -i "s/^# DATABASE_URL=""/DATABASE_URL=postgres:\/\/${PROJECT}:${PGPASS}@localhost\/${PROJECT}/" .env
    sed -i "s/^# SECRET_KEY=\"\"/SECRET_KEY=\"${SECRET_KEY}\"/" .env
    sed -i 's/^# TEMPLATES_GLOB=""/TEMPLATES_GLOB="templates\/\*\*\/\*"/' .env
    sed -i 's/^# STATIC_ROOT=""/STATIC_ROOT="static"/' .env
    egrep -v '^#|^$' .env
    

    Run the account migrations (creates table in db):

    sqlx migrate run

    Run the server:

    cargo run

    On Registration there is of coarse a panick because of the not yet configured email stuff, but on login it also has the delay but no errors or panicks show up in the console.

    registration: 2021-05-08_10-29

    login: 2021-05-08_10-32

    for login the console shows:

    INFO  actix_web::middleware::logger > 192.168.1.90:27406 "GET /accounts/login/ HTTP/1.1" 200 1231 "https://www.example.com/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0" 0.001888
    INFO  sqlx::query                   > /* SQLx ping */; rows: 0, elapsed: 1.258ms
    INFO  sqlx::query                   > SELECT id, name, password, …; rows: 1, elapsed: 2.766ms
    
    SELECT
     id,
     name,
     password,
     is_admin
    FROM
     accounts
    WHERE
     email = $1
    
    INFO  sqlx::query                   > /* SQLx ping */; rows: 0, elapsed: 667.520µs
    INFO  sqlx::query                   > UPDATE accounts SET last_login …; rows: 0, elapsed: 3.067ms
    
    UPDATE
     accounts
    SET
     last_login = now()
    WHERE
     id = $1
    
    INFO  actix_web::middleware::logger > 192.168.1.90:61377 "POST /accounts/login/ HTTP/1.1" 302 0 "https://www.example.com/accounts/login/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0" 11.075786
    INFO  actix_web::middleware::logger > 192.168.1.90:61377 "GET /dashboard/ HTTP/1.1" 200 990 "https://www.example.com/accounts/login/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0" 0.001099
    

    If resolving the email related stuff clears up the delay I will be sure to follow up and close this issue.

    opened by Jieiku 65
  • Add email-smtp and email-sendgrid features and use tera templates for mail

    Add email-smtp and email-sendgrid features and use tera templates for mail

    This feature allows to send email via smtp with the help of the lettre crate.

    In the process some environment variable are expected to be set.

    A new trait Configurable with the check_conf method allows for verification of the configuration upfront when starting the server in order to prevent the misconfiguration to be detected on production.

    The mail templates need both HTML and text template in order to be able to send accessible mails.

    Also both web and email templates now have all environment variables starting with JELLY_ in their context when being rendered.

    see #17.

    I intend to add at least a sendgrid provider, and I'd like to allow for several provider where each provider is tested in turn until one succeed (this will give some sort of higher availablity for mail delivery). I ialso like to add some integration tests and some template tests.

    • [X] use text and html mail templates
    • [X] send via smtp (manually tested)
    • [X] send via sendgrid (manually tested)
    • [X] send via postmark (manually tested)
    • [X] try several providers until one succeed
    • [ ] load and check config at start time
    • [x] add email template tests (to be sure that templates compile and work)
    • [ ] add integration tests for mail sending
    opened by matclab 20
  • have smtp as a mail option in addition to Postmark, SMTP is a very standard way to send mail.

    have smtp as a mail option in addition to Postmark, SMTP is a very standard way to send mail.

    I am suggesting the jelly-actix-web-starter also allow sending mail via a configurable SMTP account.

    I am currently working to implement this without removing any of the Postmark related stuff.

    A lot of developers are familiar with SMTP but may have never used the Postmark service before.

    I was thinking jelly application could look for the presence of a POSTMARK_API_KEY and if missing use SMTP instead.

    The two smtp crates that caught my eye so far are mail-smtp and lettre.

    mail-smtp uses tokio-smtp: https://crates.io/crates/mail-smtp

    lettre: https://github.com/lettre/lettre

    I am still trying to compare the two before I implement one. I can issue a pull request once I finish this if it would interest you?

    Are there any SMTP crates you have used? any you would recommend?

    opened by Jieiku 12
  • Low password strength doesn't log error properly.

    Low password strength doesn't log error properly.

    register a user:

    jake [email protected] 1z4v3j

    the form does nothing... no error no nothing....

    If you use a password like 1234 then it will atleast give you an error.

    make a much longer password and the account is created, such as 3flyingmonkeys6zoos

    I know this has to do with the password function because creation a longer password works.....

    Also in the log it returns a code 400 until I make the password long enough:

    2022-01-13_20-39

    I was looking at the file jelly/src/forms/password.rs and I believe the issue is here:

            if estimate.score() <= 2 {
                if let Some(feedback) = estimate.feedback() {
                    if let Some(warning) = feedback.warning() {
                        self.errors.push(format!("{}", warning));
                    }
    
                    self.hints = feedback
                        .suggestions()
                        .iter()
                        .map(|s| format!("{}", s))
                        .collect();
                }
    
                return false;
            }
    

    What I think happens is if the score is low.... and it has an inkling as to why such as using a password 12345 or qwerty then it will be like dont use sequential keys blah blah blah.... but if your password is complex but simply too short such as 1z4v3j then it probably still has a low score but no warning message to pass along....

    I am going to try and add some debug to this logic that will print the score to the server output/log

    opened by Jieiku 9
  • Password saves as plaintext after reset

    Password saves as plaintext after reset

    From:

     id |      name       |          email          |                                    password                                    | profile | plan | is_active | is_admin | has_verified_email |          last_login           |            created            |            updated            
    ----+-----------------+-------------------------+--------------------------------------------------------------------------------+---------+------+-----------+----------+--------------------+-------------------------------+-------------------------------+-------------------------------
     1  | Test 3          | [email protected]    | pbkdf2_sha256$216000$YwXdNcZGxyv5$hcVS0xd6UMKQiNYzQMcEIi3dgjqOY4RicB2r91+PbV0= | {}      |    0 | t         | f        | t                  | 2021-04-26 14:05:59.621787-04 | 2021-04-26 14:02:00.216135-04 | 2021-04-26 14:05:59.621787-04
    

    To:

     id |      name       |          email          |                                    password                                    | profile | plan | is_active | is_admin | has_verified_email |          last_login           |            created            |            updated            
    ----+-----------------+-------------------------+--------------------------------------------------------------------------------+---------+------+-----------+----------+--------------------+-------------------------------+-------------------------------+-------------------------------
     1  | Test 3          | [email protected]    | ThisIsATest                                                                    | {}      |    0 | t         | f        | t                  | 2021-04-26 22:07:46.449801-04 | 2021-04-26 14:02:00.216135-04 | 2021-04-26 22:07:46.449801-04
    
    
    opened by mikeumus 6
  • Login request takes 8 seconds

    Login request takes 8 seconds

    After setting everything up to try and take this app for a spin (and that wasn't easy, especially with #3 not merged and the application requiring me to set literally every single env var in .env before launch), I've come across the problem of very slow login responses (7.5 seconds).

    Any idea why that could happen ?

    INFO  actix_web::middleware::logger > 127.0.0.1:60779 "GET /accounts/verify/ HTTP/1.1" 200 933 "http://127.0.0.1:8000/accounts/register/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0" 0.000454
     INFO  actix_web::middleware::logger > 127.0.0.1:60794 "GET / HTTP/1.1" 200 908 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0" 0.000416
     INFO  actix_web::middleware::logger > 127.0.0.1:60794 "GET /accounts/login/ HTTP/1.1" 200 1231 "http://127.0.0.1:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0" 0.000663
     INFO  sqlx::query                   > /* SQLx ping */; rows: 0, elapsed: 267.350µs
     INFO  sqlx::query                   > SELECT id, name, password, …; rows: 1, elapsed: 3.590ms
    
    SELECT
      id,
      name,
      password,
      is_admin
    FROM
      accounts
    WHERE
      email = $1
    
     INFO  actix_web::middleware::logger > 127.0.0.1:60797 "POST /accounts/login/ HTTP/1.1" 400 1329 "http://127.0.0.1:8000/accounts/login/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0" 7.477480
     INFO  sqlx::query                   > /* SQLx ping */; rows: 0, elapsed: 177.526µs
     INFO  sqlx::query                   > SELECT id, name, password, …; rows: 1, elapsed: 316.032µs
    
    SELECT
      id,
      name,
      password,
      is_admin
    FROM
      accounts
    WHERE
      email = $1
    
     INFO  actix_web::middleware::logger > 127.0.0.1:60800 "POST /accounts/login/ HTTP/1.1" 400 1329 "http://127.0.0.1:8000/accounts/login/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0" 7.313738
    
    opened by delneg 6
  • Add email send test

    Add email send test

    Add test for postmark and sendgrid email send api call with the help of httmock crate. Correctly detect and report any API error for sendgrid and postmark.

    This PR relies on #19.

    opened by matclab 5
  • Logout form button seems to work fine, using a Logout link directs to a blank page.

    Logout form button seems to work fine, using a Logout link directs to a blank page.

    Logout form button seems to work fine, using a Logout link directs to a blank page.

    I modified templates/dashboard/layout.html

    this goes to blank page:

    <a href="/accounts/logout/" title="Logout">Logout</a>
    

    the original ones logs you out AND redirects to index:

        <form method="post" action="/accounts/logout/">
            <button type="submit">Logout</button>
        </form>
    

    I am still looking for ways to resolve this. searching the source for logout, I see two other files:

    src/accounts/mod.rs and src/accounts/views/mod.rs

    In src/accounts/mod.rs I see that most functions have both a get and a post

    .service(
                    resource("/register/")
                        .route(get().to(views::register::form))
                        .route(post().to(views::register::create_account)),
                )
    

    but logout only has the post...

    .service(resource("/logout/").route(post().to(views::logout))),

    I am assuming this is my problem, and I would need to modify this in order to be able to logout using a link instead of a form?

    This is important because forms get styled very differently than links do.

    I do not want to put a form element into my sites navigation bar if I can help it.

    opened by Jieiku 4
  • Update .env.example

    Update .env.example

    Add some additional information regarding BIND_TO, DOMAIN, and SESSIONID_DOMAIN.

    Make information succinct.

    A couple options here are new that will be available in @matclab new SMTP pull request.

    He said the pull request looks good but asked that I submit this pull request here instead of to his repository.

    opened by Jieiku 4
  • Fix template compile error

    Fix template compile error

    thread 'main' panicked at 'Unable to compile templates!: Error { kind: Msg("\n* Failed to parse "templates/email/layout.html"\n --> 497:61\n |\n497 | \t<a href=\"{{ pm:unsubscribe }}\" style=\"text-decoration:none;\">Unsubscribe␊\n | \t ^---\n |\n = expected or, and, not, <=, >=, <, >, ==, !=, +, -, *, /, %, a filter, or a variable end (}})"), source: None }', jelly/src/templates.rs:54:69

    opened by alexxroche 4
  • bad master key length: expected >= 32 bytes, found 4

    bad master key length: expected >= 32 bytes, found 4

    Hi, I am trying to set up the project according to the instructions but facing an error

    `\Rust\jelly-actix-web-starter> cargo run RUST_BACKTRACE=FULL

    Finished dev [unoptimized + debuginfo] target(s) in 0.53s
     Running `target\debug\webserver.exe RUST_BACKTRACE=FULL`
    

    INFO actix_server::builder > Starting 4 workers INFO actix_server::builder > Starting "actix-web-service-127.0.0.1:17001" service on 127.0.0.1:17001 thread 'actix-rt:worker:0' panicked at 'bad master key length: expected >= 32 bytes, found 4', C:\Users\Asus.cargo\registry\src\github.com-1ecc6299db9ec823\cookie-0.14.4\src\secure\key.rs:88:13`

    opened by samarthc-rapid 3
  • Compile sass/scss to css at launch. (similar to Zola)

    Compile sass/scss to css at launch. (similar to Zola)

    After having used zola for 3 months of development, I have come to love its ability to compile sass.

    Additionally when you use zola serve it has a live reload feature, basically I can make changes to the scss files while zola serve is running, it will notice the changed files and automatically recompile the sass and refresh the page.

    Making changes and being able to instantly evaluate those changes has been wonderful, it has saved a lot of development time!

    opened by Jieiku 1
  • Implement a Cache Bust similar to Zola's (also Learn Tera Templates by Creating a Zola Theme)

    Implement a Cache Bust similar to Zola's (also Learn Tera Templates by Creating a Zola Theme)

    Just wanted to post that Zola (The static site generator) uses Tera Templates. (same Creator for both)

    I learned a lot about Tera Templates by creating a theme for Zola called abridge. (and some shortcodes)

    In Zola you can set a cache bust according to file hash, and you don't have to manually enter the hash, Zola calculates it for you when you zola build or zola serve Additionally it can calculate the hash for the integrity value on scripts (Subresource Integrity)

    Here is what that looks like in a Tera template html file from zola docs:

      <script src="{{ get_url(path=config.extra.js_theme, trailing_slash=false, cachebust=true) | safe }}"{%- if config.extra.integrity %} integrity="sha384-{{ get_file_hash(path=config.extra.js_theme, sha_type=384, base64=true) | safe }}"{%- endif %}></script>
    
    <link rel="preload" as="style" class="preStyle" href="{{ get_url(path=config.extra.katex_css, trailing_slash=false, cachebust=true) }}"{%- if config.extra.integrity %} integrity="sha384-{{ get_file_hash(path=config.extra.katex_css, sha_type=384, base64=true) | safe }}"{%- endif %} />
    

    It would be great if Jelly could do something similar. a file hash is a lot better than a timestamp generated at application launch because you don't want to invalidate user cache if you don't need to, otherwise you waste bandwidth when they have to cache the same exact file.

    opened by Jieiku 0
  • Update an installation instruction of sqlx-cli to support v0.5.11

    Update an installation instruction of sqlx-cli to support v0.5.11

    Purpose

    Update an installation instruction of sqlx-cli to support v0.5.11

    Overview

    Installation of sqlx-cli, with:

    cargo install sqlx-cli --no-default-features --features postgres
    

    Will fail:

    ✗ cargo install sqlx-cli --no-default-features --features postgres
        Updating crates.io index
      Installing sqlx-cli v0.5.11
       Compiling libc v0.2.121
       Compiling version_check v0.9.4
       Compiling proc-macro2 v1.0.36
       Compiling unicode-xid v0.2.2
       Compiling syn v1.0.90
       Compiling autocfg v1.1.0
       Compiling typenum v1.15.0
       Compiling cfg-if v1.0.0
       Compiling memchr v2.4.1
       Compiling serde_derive v1.0.136
       Compiling futures-core v0.3.21
       Compiling serde v1.0.136
       Compiling parking_lot_core v0.8.5
       Compiling futures-task v0.3.21
       Compiling futures-channel v0.3.21
       Compiling log v0.4.16
       Compiling crossbeam-utils v0.8.8
       Compiling futures-util v0.3.21
       Compiling tinyvec_macros v0.1.0
       Compiling crossbeam-queue v0.3.5
       Compiling serde_json v1.0.79
       Compiling once_cell v1.10.0
       Compiling bitflags v1.3.2
       Compiling pin-project-lite v0.2.8
       Compiling opaque-debug v0.3.0
       Compiling lazy_static v1.4.0
       Compiling scopeguard v1.1.0
       Compiling smallvec v1.8.0
       Compiling futures-sink v0.3.21
       Compiling matches v0.1.9
       Compiling unicode-bidi v0.3.7
       Compiling pin-utils v0.1.0
       Compiling cpufeatures v0.2.2
       Compiling subtle v2.4.1
       Compiling minimal-lexical v0.2.1
       Compiling futures-io v0.3.21
       Compiling percent-encoding v2.1.0
       Compiling ppv-lite86 v0.2.16
       Compiling slab v0.4.6
       Compiling unicode_categories v0.1.1
       Compiling paste v1.0.7
       Compiling nix v0.18.0
       Compiling crc-catalog v1.1.1
       Compiling itoa v1.0.1
       Compiling ryu v1.0.9
       Compiling hex v0.4.3
       Compiling unicode-width v0.1.9
       Compiling base64 v0.13.0
       Compiling sqlx-rt v0.5.11
       Compiling unicode-segmentation v1.9.0
       Compiling cfg-if v0.1.10
    error: one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', 'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', 'runtime-tokio-rustls'] must be enabled
      --> /Users/kamol.mavlonov/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-rt-0.5.11/src/lib.rs:9:1
       |
    9  | / compile_error!(
    10 | |     "one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', \
    11 | |      'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', \
    12 | |      'runtime-tokio-rustls'] must be enabled"
    13 | | );
       | |__^
    
    error: could not compile `sqlx-rt` due to previous error
    warning: build failed, waiting for other jobs to finish...
    error: failed to compile `sqlx-cli v0.5.11`, intermediate artifacts can be found at `/var/folders/xb/5j8mjkh162gf509skyrss8nc0000gp/T/cargo-installY00Lfl`
    
    Caused by:
      build failed
    
    opened by kamoljan 0
  • confirm email address is in a valid format.

    confirm email address is in a valid format.

    currently the validation for email is only checking that its not a blank field:

    src/forms/email.rs:

    impl Validation for EmailField {
        fn is_valid(&mut self) -> bool {
            if self.value == "" {
                self.errors
                    .push("Email address cannot be blank.".to_string());
                return false;
            }
    
            if !validate_email(&self.value) {
                self.errors.push("Invalid email format.".to_string());
                return false;
            }
    
            true
        }
    }
    

    I need to check all the things normally checked like it has the @ symbol and a tld etc.

    I considered writing this from scratch, but I wonder if it might be a better idea to use a crate for this? such as this (or possibly others): https://docs.rs/email_address/latest/email_address/

    I also seen a more direct approach using regex: https://turreta.com/2019/09/14/rust-validate-email-address-using-regular-expressions/

    I am curious what anyone else thinks?

    opened by Jieiku 7
Owner
SecretKeys
SecretKeys
Cookiecutter Rust Actix template for jumpstarting production-ready projects quickly.

Cookiecutter actix simple clean architecture This is a reusable Rust Cookiecutter template. The project is based on Actix web in combination with Dies

Microsoft 19 Feb 12, 2023
Static Web Server - a very small and fast production-ready web server suitable to serve static web files or assets

Static Web Server (or SWS abbreviated) is a very small and fast production-ready web server suitable to serve static web files or assets.

Jose Quintana 496 Jan 2, 2023
Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Actix Web Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust Features Supports HTTP/1.x and HTTP/2 Streaming and pipelining

Actix 16.3k Jan 8, 2023
Add Facebook and Google authentication to your HTTP REST API in Actix-web

I created this project while learning Rust. Project shows how to handle Facebook and Google token verification in Rust using Actix-Web. Hope this help

null 37 Dec 31, 2022
In-progress extractors and middleware for Actix Web

actix-web-lab Experimental extractors, middleware, and other extras for possible inclusion in Actix Web. Things To Know About This Crate It will never

Rob Ede 51 Dec 20, 2022
Extension for actix-web to validate user permissions

actix-web-grants Extension for actix-web to validate user permissions. To check user access to specific services, you can use built-in proc-macro, Per

Artem Medvedev 114 Dec 22, 2022
Easy to use multipart forms for actix-web

Actix Easy Multipart Easy to use Multipart Forms for actix-web. File uploads are written to disk as temporary files similar to the way the $_FILES var

Jacob Halsey 17 Jan 3, 2023
Example Blog using Rust, Actix Web, HTMX, Mustache

Actix Blog An example blog built with Actix. It uses htmx and handlebar templates. Running To run the blog, you need to have a recent version of Rust

Dru Jensen 2 Nov 11, 2022
Actix-web wrapper for garde, a Rust validation library.

Garde-actix-web   Actix-web wrapper for garde, a Rust validation library. Installation Usage example Feature flags About us Installation [dependencies

Netwo 5 Sep 8, 2023
Source Code for 'Practical Rust Web Projects' by Shing Lyu

Apress Source Code This repository accompanies Practical Rust Web Projects by Shing Lyu (Apress, 2021). Download the files as a zip using the green bu

Apress 44 Nov 17, 2022
OxHTTP is a very simple synchronous HTTP client and server

OxHTTP is a very simple synchronous implementation of HTTP 1.1 in Rust. It provides both a client and a server.

Oxigraph 13 Nov 29, 2022
Example Actix 2.x REST application implementing many features

Rust/Actix Example An Actix 2.0 REST server using the Rust language. Motivation Actix Web is a fast, powerful web framework for building web applicati

David D. 238 Dec 31, 2022
Basic Actix + Diesel + Postgres REST API

Actix-Crud Basic Actix + Diesel + Postgres REST API Setup Install and setup PostgreSQL Set DATABASE_URL environment variable or specify in .env file g

Aathif Naseer 4 Sep 23, 2022
Fahrenheit-celsius converter using actix

fahrenheit-celsius-converter Simple http Fahrenheit/Celsius/Kelvin converter using actix-web. Note This is a toy project, not yet finished. It's not r

null 5 Nov 7, 2023
A blazingly fast static web server with routing, templating, and security in a single binary you can set up with zero code. :zap::crab:

binserve ⚡ ?? A blazingly fast static web server with routing, templating, and security in a single binary you can set up with zero code. ?? UPDATE: N

Mufeed VH 722 Dec 27, 2022
Simple and fast web server

see Overview Simple and fast web server as a single executable with no extra dependencies required. Features Built with Tokio and Hyper TLS encryption

null 174 Dec 9, 2022
A flexible web framework that promotes stability, safety, security and speed.

A flexible web framework that promotes stability, safety, security and speed. Features Stability focused. All releases target stable Rust. This will n

Gotham 2.1k Jan 3, 2023
Sincere is a micro web framework for Rust(stable) based on hyper and multithreading

The project is no longer maintained! Sincere Sincere is a micro web framework for Rust(stable) based on hyper and multithreading. Style like koa. The

null 94 Oct 26, 2022
Operator is a web server. You provide a directory and Operator serves it over HTTP.

Operator Operator is a web server. You provide a directory and Operator serves it over HTTP. It serves static files the way you'd expect, but it can a

Matt Kantor 6 Jun 6, 2022