···7# Dev Mode Configuration
8DEV_MODE="false" # Enable dev mode for testing with dummy data. Access via ?dev=true query parameter when enabled.
910-000
···7# Dev Mode Configuration
8DEV_MODE="false" # Enable dev mode for testing with dummy data. Access via ?dev=true query parameter when enabled.
910+# Custom Emojis
11+# Directory to read/write custom emoji image files at runtime.
12+# For local dev, keep under the repo:
13+EMOJI_DIR="static/emojis"
+28-1
README.md
···25# navigate to http://127.0.0.1:8080
26```
2700000000000000000000000000028### available commands
2930we use [just](https://github.com/casey/just) for common tasks:
···43- [at protocol](https://atproto.com/) (via [atrium-rs](https://github.com/sugyan/atrium))
44- [sqlite](https://www.sqlite.org/) for local storage
45- [jetstream](https://github.com/bluesky-social/jetstream) for firehose consumption
46-- [fly.io](https://fly.io/) for hosting
···25# navigate to http://127.0.0.1:8080
26```
2728+### custom emojis (no redeploys)
29+30+Emojis are now served from a runtime directory configured by `EMOJI_DIR` (defaults to `static/emojis` locally; set to `/data/emojis` on Fly.io). On startup, if the runtime emoji directory is empty, it will be seeded from the bundled `static/emojis`.
31+32+- Local dev: add image files to `static/emojis/` (or set `EMOJI_DIR` in `.env`).
33+- Production (Fly.io): upload files directly into the mounted volume at `/data/emojis` — no rebuild or redeploy needed.
34+35+Examples with Fly CLI:
36+37+```bash
38+# Open an SSH console to the machine
39+fly ssh console -a zzstoatzz-status
40+41+# Inside the VM, copy or fetch files into /data/emojis
42+mkdir -p /data/emojis
43+curl -L -o /data/emojis/my_new_emoji.png https://example.com/my_new_emoji.png
44+```
45+46+Or from your machine using SFTP:
47+48+```bash
49+fly ssh sftp -a zzstoatzz-status
50+sftp> put ./static/emojis/my_new_emoji.png /data/emojis/
51+```
52+53+The app serves them at `/emojis/<filename>` and lists them via `/api/custom-emojis`.
54+55### available commands
5657we use [just](https://github.com/casey/just) for common tasks:
···70- [at protocol](https://atproto.com/) (via [atrium-rs](https://github.com/sugyan/atrium))
71- [sqlite](https://www.sqlite.org/) for local storage
72- [jetstream](https://github.com/bluesky-social/jetstream) for firehose consumption
73+- [fly.io](https://fly.io/) for hosting
···01use crate::resolver::HickoryDnsTxtResolver;
2use crate::{
3 api::auth::OAuthClientType,
···729730/// Get all custom emojis available on the site
731#[get("/api/custom-emojis")]
732-pub async fn get_custom_emojis() -> Result<impl Responder> {
733 use std::fs;
734735 #[derive(Serialize)]
···738 filename: String,
739 }
740741- let emojis_dir = "static/emojis";
742 let mut emojis = Vec::new();
743744- if let Ok(entries) = fs::read_dir(emojis_dir) {
745 for entry in entries.flatten() {
746 if let Some(filename) = entry.file_name().to_str() {
747 // Only include image files
···1+use crate::config::Config;
2use crate::resolver::HickoryDnsTxtResolver;
3use crate::{
4 api::auth::OAuthClientType,
···730731/// Get all custom emojis available on the site
732#[get("/api/custom-emojis")]
733+pub async fn get_custom_emojis(app_config: web::Data<Config>) -> Result<impl Responder> {
734 use std::fs;
735736 #[derive(Serialize)]
···739 filename: String,
740 }
741742+ let emojis_dir = app_config.emoji_dir.clone();
743 let mut emojis = Vec::new();
744745+ if let Ok(entries) = fs::read_dir(&emojis_dir) {
746 for entry in entries.flatten() {
747 if let Some(filename) = entry.file_name().to_str() {
748 // Only include image files
+5
src/config.rs
···3132 /// Dev mode for testing with dummy data
33 pub dev_mode: bool,
00034}
3536impl Config {
···60 .unwrap_or_else(|_| "false".to_string())
61 .parse()
62 .unwrap_or(false),
0063 })
64 }
65}
···3132 /// Dev mode for testing with dummy data
33 pub dev_mode: bool,
34+35+ /// Directory to serve and manage custom emojis from
36+ pub emoji_dir: String,
37}
3839impl Config {
···63 .unwrap_or_else(|_| "false".to_string())
64 .parse()
65 .unwrap_or(false),
66+ // Default to static/emojis for local dev; override in prod to /data/emojis
67+ emoji_dir: env::var("EMOJI_DIR").unwrap_or_else(|_| "static/emojis".to_string()),
68 })
69 }
70}