Build like bytebury. The official template that we use for our websites, skip
all the bootstrapping. We are not suggesting that this is
the right way to create an application, this is just our way and we're sharing
with everybody. We favor simplicity in developer
experience over processes and tools. Hence, running locally is a single command:
./dev.sh and deploying is done automatically through
GHA. By keeping local development simple, as well as our architecture, we can
move fast — relying on Rust's speed to match on the
server. It's how we built Fun Banking, CTRunner,
Oolong
and continue to build more.
This is the template that we use at bytebury. Our primary stack is Axum, Askama, HTMX, and SQLite through SQLx. We run all of our servers on DigitalOcean on various server sizes, so you'll notice some deployment workflows for DigitalOcean (feel free to change that to your liking). We use Stripe as our payment partner and Google for our OAuth (extensible). You will also need to install TailwindCSS. As, we use TailwindCSS to build out stylesheets.
Note
Running ./dev will run the application in watch mode for you as well as run
any pending migrations! It is all you need to get started locally.
- Clone the repository
git clone git@github.com:bytebury/tea.git - Run the development server
cd ./tea && ./dev.shin your terminal
This will run all of your migrations as well as generate a .env file in your
root directory. Open it up and change the environment variables to your liking.
After that, you should be ready to start development.
Note
You will need sqlx installed locally to create migrations.
sqlx migrate add create_my_tableYou can use the Stripe CLI to listen to the webhooks locally. To do that, you'll need to download the Stripe CLI and then run the following command in a separate terminal:
stripe listen --forward-to localhost:8080/webhooks/stripeImportant
If you wish to use IP2Location LITE, you will need to download one of their databases and put it in "db/ip2location.BIN" location.
We use IP2Location LITE for our IP services. If you plan on following suite, know that they require you to attribute them by adding the following HTML to your website.
<p>
{{ shared.app_info.name }} uses the IP2Location LITE database for
<a class="underline" href="https://lite.ip2location.com" target="_blank">IP geolocation</a>.
</p>You'll need to implement the Paginatable trait for your entity.
impl Paginatable for User {
fn table_name() -> &'static str {
"users"
}
}Once you do that, then you will be able to paginate your data like:
// paginate everything, without any filtering
User::paginate(db, Pagination::default())
// paginate with a filter
User::paginate_filter(db, Pagination::default(), "role = ? ORDER BY created_at DESC", vec!["admin"])At this time, we deal with Role Based Access Control (RBAC) through the Can
trait. This trait has a singular function that you can implement like so:
impl Can<Resource> for User {
fn can(&self, action: Action, resource: &Resource) -> bool {
match self.role {
Role::Admin => true, // Admins are super-users.
Role::User => match action {
Action::Read | Action::Create => true,
Action::Update | Action::Delete => resource.user_id == self.id,
}
}
}
}
// Usage:
if user.can(Action::Delete, &article) {
// success
}
if user.cannot(Action::Delete, &article) {
// uh-oh you can't do that.
}We decided to do this due to simplicity in our applications. We almost never need granular access through the database — most of the times, roles suffice for our use-cases.
Important
You will need to create a folder called APP_NAME in the root of your server.
For using SSH as a connection, you will need to provide a working private key.
When you are creating a server from scratch, you'll typically need to configure
your Nginx, or at least, we do at bytebury. So, we've included a script that
does the set up we use: SSL + HTTP/2 enabled via server-setup.sh. This is
a one-time command. Once you have your server already configured, you won't
need this again. However, we've included it so it can streamline those who use
it.
- Focus on what matters — off-load often
- Be close with the browser — it probably already does it
- Simple cloud architecture — your server can handle it
- Simple dev experience — doesn't mean "cool"
- If people don't ask for it — they probably don't want it