backend, web frontend, etc.
  • Rust 65.6%
  • TypeScript 32.9%
  • Python 0.4%
  • JavaScript 0.4%
  • Shell 0.3%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
FredApps 0cdedd7cea
api_clients: read upload files with tokio::fs (Sonar S7493) (#364)
## Summary

Replaces blocking `std::fs::read` calls inside `async fn`s with
`tokio::fs::read(..).await`. SonarCloud reports these as **rust:S7493**
("use an asynchronous file API instead of this blocking file
operation"). This PR resolves 5 findings:

| Crate | File |

|-------------------|--------------------------------------------------------------|
| `artcraft_client` |
`endpoints/media_files/legacy_upload_media_file_from_file.rs` |
| `artcraft_client` |
`endpoints/media_files/upload_image_media_file_from_file.rs` |
| `artcraft_client` |
`endpoints/media_files/upload_new_engine_asset_from_file.rs` |
| `artcraft_client` |
`endpoints/media_files/upload_video_media_file_from_file.rs` |
| `artcraft_router` |
`generate/generate_video/providers/kinovi/upload.rs` |

The workspace `tokio` dependency enables only `macros`.
`artcraft_client` (which had `tokio` only as a dev-dependency) and
`artcraft_router` now declare `tokio = { workspace = true, features =
["fs"] }`. `Cargo.lock` does not change. The error mapping and the
kinovi fallback that downloads again when a read fails are unchanged.

The same change for the desktop copies of these crates is
storytold/artcraft#2803.

## Not included (overlap with open PRs)

- `artcraft_client/src/recipes/download_media_file.rs` (2 findings):
#308 rewrites this write path.
- `google_drive_downloader/src/google_drive_download_command.rs` (2 ×
S7487, blocking `std::process::Command`): #241 restructures how this
command is built.

## Testing

Run locally on Linux x86_64 with stable Rust and `SQLX_OFFLINE=true`:

- `cargo test -p artcraft_client -p artcraft_router` passes: 2565 + 39
unit tests, with ignored tests left ignored.

No new test is added. This is a behaviour-preserving switch to
non-blocking reads, so no test fails before the change and passes after
it, and the changed functions do network I/O after the read.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Improvements**
* File-based media and asset uploads now read files asynchronously,
helping keep the app responsive during uploads.
* Video uploads continue to fall back to downloading the source when a
local file cannot be read.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-10 18:30:58 +01:00
.claude/rules testing omni gen (#1875) 2026-08-09 22:54:25 -04:00
.config recalculate hackari workspace acceleration package 2025-04-09 20:41:52 -04:00
.github ci: remove DigitalOcean Container Registry target (#356) 2026-11-08 20:16:03 -05:00
.idea artcraft router: seedance (#1153) 2026-03-16 22:43:50 -04:00
.sqlx Generate WebP thumbnails for videos instead of Gif (except: MacOS 10.15) 2026-09-28 00:52:07 -04:00
.vscode cleanup root directory (#996) 2026-01-19 23:08:39 -05:00
_database remove old website (#63) 2026-10-09 11:31:28 -04:00
_docs remove old website (#63) 2026-10-09 11:31:28 -04:00
_metrics/datadog Remove endpoints for old Rust / Bevy game engine (#1765) 2026-07-09 23:52:16 -04:00
_tools/postman remove old tags code (#1767) 2026-07-10 02:31:37 -04:00
build Add docker-compose setup for self-hosting the backend (#68) 2026-10-11 18:51:50 -04:00
crates api_clients: read upload files with tokio::fs (Sonar S7493) (#364) 2026-11-10 18:30:58 +01:00
docs fix: repair Linux installation flows (#221) 2026-11-01 16:32:34 -05:00
frontend feat(website): make Documentation the second option on app pages (#360) 2026-11-09 10:05:27 -05:00
includes increase username randomness to stop pages (#1261) 2026-03-31 00:31:22 -04:00
old_assets/resources shink assets that are way too large (#1717) 2026-07-02 03:42:41 -04:00
script security: pin Swagger UI, refuse npx installs, drop stale MinIO keys (SonarCloud) (#177) 2026-10-27 16:29:01 -04:00
secrets metrics defs (#1554) 2026-05-23 11:26:14 -04:00
test_data remove "secrets" (not ours) that Netlify is complaining about (#719) 2025-07-14 15:42:24 -04:00
.dockerignore fix: remove SMTP and Stripe secrets from tracked dev config (#236) 2026-11-02 20:26:45 -05:00
.env cleanup root directory (#996) 2026-01-19 23:08:39 -05:00
.gitattributes move rust code back to root directory 2025-02-11 21:58:04 -05:00
.gitignore high bitrate seedance 2.5 2026-09-12 13:22:42 -04:00
.nvmrc Port the entire website to CloudFlare Pages + Workers (#354) 2026-11-07 13:13:23 -05:00
.sonarcloud.properties Keep vendored code out of the SonarCloud analysis (#186) 2026-10-27 16:24:11 -04:00
AGENTS.md Serve Craft App release info for ArtCraft Launcher (#71) 2026-10-10 10:35:45 -04:00
Cargo.lock fold cdn configs in 2026-09-27 22:41:01 -04:00
Cargo.toml fold cdn configs in 2026-09-27 22:41:01 -04:00
CLAUDE.md rename CLAUDE.md to AGENTS.md (#1940) 2026-09-10 21:55:17 -04:00
Cranky.toml move rust code back to root directory 2025-02-11 21:58:04 -05:00
diesel.toml cleanup root directory (#996) 2026-01-19 23:08:39 -05:00
LICENSE.md update wording 2026-01-06 12:27:34 -05:00
package.json Configure artcraft-webapp for Cloudflare Pages deployment 2026-11-08 22:24:24 -05:00
README.md Automate Craft App Release Version Updates (#159) 2026-10-30 14:43:47 -04:00
ROADMAP.md formatting, small revisions 2026-01-06 12:22:46 -05:00
rustfmt.toml move rust code back to root directory 2025-02-11 21:58:04 -05:00
wrangler.jsonc Port the entire website to CloudFlare Pages + Workers (#354) 2026-11-07 13:13:23 -05:00

ArtCraft Services

This repository contains ArtCraft's backend, HTTP API, background workers, and web frontends. It is a Rust and TypeScript monorepo with shared API definitions, provider clients, database queries, and development tooling.

The Tauri desktop application is maintained in storytold/artcraft, along with the product overview, feature demos, and desktop downloads.

Backend architecture

storyteller-web is the main HTTP service, built with Rust, Actix Web, and Tokio. It handles authentication, accounts, media uploads and libraries, generation requests, job status, credits, and Stripe billing. The service retains the storyteller-web name, and its hosted API uses https://api.storyteller.ai.

Generation spans the HTTP service, provider integrations, and asynchronous workers:

flowchart LR
  Clients[Web, desktop, and API clients] --> API[storyteller-web]
  API --> Router[artcraft_router]
  Router --> Providers[Generation providers]
  Providers -->|Webhooks| API
  Workers[Background workers] -->|Poll jobs| Providers
  API --> DB[(MySQL)]
  Workers --> DB
  API --> Storage[(Object storage)]
  Workers --> Storage

A typical generation request follows this path:

  1. The API authenticates the caller, validates the request and input media, and checks the user's access and credits.
  2. The generation pipeline calculates the cost, bills the wallet, and uses artcraft_router to build and submit a provider-specific request. The API records inference jobs in MySQL and returns job tokens to the client.
  3. Completion is handled through provider webhooks or polling workers, depending on the integration. Results are downloaded into object storage, registered as media files, and associated with the completed jobs. Separate workers handle follow-up processing such as video thumbnails.
  4. Clients poll job-status endpoints and load the resulting media through CDN URLs.

Storage responsibilities are split across these components:

Component Role
MySQL + SQLx Accounts, media metadata, inference jobs, wallets, and bills
Redis Caching, rate limiting, and job progress
Elasticsearch Search indexes and queries
S3-compatible storage / R2 Uploaded media, generated assets, and derived files

HTTP routes and handlers live in storyteller_web/src/http_server. MySQL queries belong in the shared mysql_queries crate so that handlers, workers, and CLI tools use the same data access layer. Reusable billing components live under crates/service/plugins, and background services live under crates/service/job.

HTTP API

The API exposes two generation interfaces:

  • Application endpoints: /v1/omni_gen/generate/* uses user sessions for image, video, audio, mesh, and splat generation. /v1/omni_gen/models/* and /v1/omni_gen/cost/* expose model discovery and cost estimation without requiring a user session. Application job status is available under /v1/jobs.
  • Programmatic endpoints: /v1/omni_api uses an API key in the Authorization header. It provides image and video generation, image/video/audio uploads, and job-status polling. Use Authorization: Bearer <api-key>; API access must be enabled for the account.

For example, a programmatic video request goes to POST /v1/omni_api/generate/video. The response contains an inference_job_token, which the caller polls with GET /v1/omni_api/job_status/job/{token}. The Omni API guide covers authentication, request and response bodies, URL inputs, and runnable examples.

Start with these sources when adding or tracing an endpoint:

Frontend

frontend contains the Nx workspace for React and TypeScript apps and shared libraries. The main web frontends use Vite, with Zustand and signals for state, Three.js for 3D scenes, and shared UI and generation tools.

Path Purpose
frontend/apps/artcraft-webapp Browser application at app.getartcraft.com
frontend/apps/artcraft-website-next Product website at getartcraft.com (Next.js)
frontend/libs/api HTTP clients, API host selection, and models
frontend/libs/omni-gen Shared generation logic
frontend/libs/components Reusable UI, editors, and generation controls
frontend/libs/tauri-api Frontend bindings for native desktop commands

The web apps call the backend through the shared API library, which handles JSON and multipart requests and session credentials. Libraries such as tauri-api and tauri-utils remain because shared web components still import their types, helpers, and browser-compatible behavior. The native desktop app and libraries used only by that app live in the separate desktop repository.

Repository layout

artcraft-services/
├── crates/
│   ├── service/web/       # HTTP services, including storyteller_web
│   ├── service/job/       # Provider workers, media processing, analytics
│   ├── service/plugins/   # Shared billing and service components
│   ├── api_clients/       # ArtCraft API types, clients, router, provider clients
│   ├── schema/            # Database access, public tokens/enums, bucket paths
│   ├── lib/               # Shared Rust utilities
│   └── cli/               # Development and operations tools
├── frontend/
│   ├── apps/              # Web frontends
│   └── libs/              # Shared TypeScript libraries
├── _database/             # SQL migrations, materialized schemas, search schemas
├── _docs/                 # Setup guides and technical documentation
├── _tools/postman/        # HTTP request collections
├── build/                 # Service Dockerfiles
├── script/                # Development, build, and database tooling
└── Cargo.toml             # Rust workspace

Local development

Use Rust/Cargo for backend work and Node.js/npm for the main frontend workspace. See the development setup guide for toolchain setup and frontend README for dependency installation and Nx usage.

Backend

The server needs a migrated MySQL database, Redis, Elasticsearch configuration, object storage, and credentials for the integrations being exercised. The server setup guide covers local MySQL and Redis; the remaining configuration is defined in the server config directory and startup code.

In development, the server loads storyteller-web.common.env, storyteller-web.development.env, and storyteller-web.development-secrets.env from its configuration search paths: the repository root, ./config, and the server's config directory. Its bootstrap skips the root .env file.

With the toolchain and service configuration in place, run from the repository root:

SQLX_OFFLINE=true cargo check -p storyteller-web
SQLX_OFFLINE=true cargo run -p storyteller-web

The default bind address is 0.0.0.0:12345, configurable through BIND_ADDRESS. GET /_status exposes the service health check. Provider polling and thumbnail processing require their corresponding worker processes and configuration.

SQLX_OFFLINE=true uses the checked-in .sqlx query metadata during compilation; the running server still needs its databases. When changing SQLx queries, use script/rust/sqlx_codegen_database.sh to regenerate metadata against migrated development databases.

Web frontend

To run the browser app against a local backend:

cd frontend
npm install
VITE_USE_LOCAL_API=true npx nx dev artcraft-webapp

The browser app runs at http://localhost:4201. VITE_USE_LOCAL_API=true selects http://localhost:12345; without that override, its Vite development proxy targets the hosted API. API host selection lives in StorytellerApiHostStore.

From frontend, build the web app with npx nx build artcraft-webapp. The product website is a standalone Next.js app (not part of the Nx workspace); run it from the repository root with ./script/website/unix_frontend_website_dev.sh.

The website runs at http://localhost:3000. Repository-root launchers are in script/website. For desktop development, use the ArtCraft desktop repository.

Automated Craft Release Version Updates

The .github/workflows/update-craft-release-versions.yml workflow automatically updates the release versions of the Craft Launcher and Crafting Apps.

When adding a new Craft app repository, you must:

  1. Add the repository name to the apps array in .github/workflows/update-craft-release-versions.yml (const apps).
  2. Add the app to CRAFT_APP_RELEASES in frontend/apps/artcraft-website-next/src/lib/crafting-app-releases.ts and update the corresponding app definitions as required.
  3. Ensure the repository publishes stable releases using the vX.Y.Z tag format and provides the required release assets.

Important: The repository must be added to both the workflow's apps array and the release catalog. Adding it to the workflow alone is not enough.

Further reading