Select the types of activity you want to include in your feed.
Demonstration bridge between ATproto and GraphQL. Generate schema types and interface with the ATmosphere via GraphQL queries. Includes a TypeScript server with IDE.
···11+# Mothball GraphQL Server Configuration
22+# Copy this file to .env and modify as needed
33+44+# Server host (default: 0.0.0.0 for all interfaces)
55+HOST=0.0.0.0
66+77+# Server port (default: 8080)
88+PORT=8080
99+1010+# Database connection string (if using database)
1111+# DATABASE_URL=postgres://user:password@localhost:5432/mothball
1212+1313+# Logging level (debug, info, warn, error)
1414+# RUST_LOG=info
···11+# Mothball (Backend)
22+33+This repo is for Mothball, an app for selling items locally.
44+55+- The repo is written in Rust.
66+- The primary frameworks are `axum` for powering the web server, `juniper` for powering the GraphQL engine.
···11+[package]
22+name = "mothball-server"
33+version = "0.1.0"
44+edition = "2021"
55+66+[dependencies]
77+axum = "0.8"
88+juniper = "0.17"
99+juniper_axum = "0.3"
1010+serde = { version = "1.0", features = ["derive"] }
1111+serde_json = "1.0"
1212+tokio = { version = "1.0", features = ["full"] }
1313+dotenv = "0.15"
1414+chrono = "0.4"
1515+uuid = { version = "1.0", features = ["v4"] }
+35
Dockerfile
···11+# Dockerfile for Mothball GraphQL Server
22+# Multi-stage build to keep the final image small
33+44+# Stage 1: Build the Rust application
55+FROM rust:1.60-slim as builder
66+77+WORKDIR /app
88+99+# Copy the entire project
1010+COPY . .
1111+1212+# Build the application in release mode
1313+RUN cargo build --release
1414+1515+# Stage 2: Create the final lightweight image
1616+FROM debian:bullseye-slim
1717+1818+# Install only the essential dependencies
1919+RUN apt-get update && \
2020+ apt-get install -y ca-certificates && \
2121+ rm -rf /var/lib/apt/lists/*
2222+2323+WORKDIR /app
2424+2525+# Copy the compiled binary from the builder stage
2626+COPY --from=builder /app/target/release/mothball-server .
2727+2828+# Copy environment variables and static files
2929+COPY .env .
3030+3131+# Expose the port the app runs on
3232+EXPOSE 8080
3333+3434+# Command to run the application
3535+CMD ["./mothball-server"]
+102
README.md
···11+# Mothball GraphQL Server
22+33+A simple GraphQL server for the Mothball application, built with Rust, Actix Web, and Juniper.
44+55+## Features
66+77+- GraphQL API endpoint at `/graphql`
88+- Interactive GraphiQL interface for development
99+- Health check endpoint at `/health`
1010+- Environment-based configuration
1111+- Ready for deployment to Fly.io
1212+1313+## Installation
1414+1515+### Prerequisites
1616+1717+- Rust (latest stable version)
1818+- Cargo
1919+- Fly.io CLI (for deployment)
2020+2121+### Building
2222+2323+```bash
2424+cd mothball-server
2525+cargo build --release
2626+```
2727+2828+### Running Locally
2929+3030+```bash
3131+# Copy the example environment file
3232+cp .env.example .env
3333+3434+# Run the server
3535+cargo run
3636+```
3737+3838+The server will start on `http://localhost:8080` by default.
3939+4040+## Development
4141+4242+### GraphQL Schema
4343+4444+The server provides basic queries:
4545+- `hello`: Returns a greeting message
4646+- `health`: Returns server health status
4747+4848+### Extending the Schema
4949+5050+To add new GraphQL types and queries:
5151+1. Add new types to the `Query` struct in `src/main.rs`
5252+2. Use the `#[juniper::graphql_object]` attribute
5353+3. Add async functions for each query/mutation
5454+5555+## Deployment to Fly.io
5656+5757+### Prerequisites
5858+- Fly.io account
5959+- Fly.io CLI installed
6060+6161+### Deployment Steps
6262+6363+1. **Install Fly.io CLI** (if not already installed):
6464+ ```bash
6565+ curl -L https://fly.io/install.sh | sh
6666+ ```
6767+6868+2. **Login to Fly.io**:
6969+ ```bash
7070+ fly auth login
7171+ ```
7272+7373+3. **Create a new Fly.io app**:
7474+ ```bash
7575+ fly launch
7676+ ```
7777+7878+4. **Set up PostgreSQL (if needed)**:
7979+ ```bash
8080+ fly postgres create
8181+ ```
8282+8383+5. **Deploy**:
8484+ ```bash
8585+ fly deploy
8686+ ```
8787+8888+### Environment Configuration
8989+9090+Copy `.env.example` to `.env` and configure as needed for your deployment.
9191+9292+## Configuration
9393+9494+| Variable | Default | Description |
9595+|----------|---------|-------------|
9696+| `HOST` | `0.0.0.0` | Host to bind the server to |
9797+| `PORT` | `8080` | Port to listen on |
9898+| `DATABASE_URL` | (none) | Database connection string |
9999+100100+## License
101101+102102+MIT
+23
fly.toml
···11+# fly.toml app configuration
22+# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
33+44+app = "mothball-server"
55+primary_region = "iad"
66+77+[build]
88+ dockerfile = "Dockerfile"
99+1010+[env]
1111+ PRIMARY_REGION = "iad"
1212+1313+[http_service]
1414+ internal_port = 8080
1515+ force_https = true
1616+ auto_stop_machines = true
1717+ auto_start_machines = true
1818+ min_machines_running = 1
1919+2020+[[vm]]
2121+ cpu_kind = "shared"
2222+ cpus = 1
2323+ memory_mb = 1024
+60
graphql/useInventoryQuery.json
···11+{
22+ "data": {
33+ "inventory": [
44+ {
55+ "id": "ikea-desk",
66+ "title": "Large IKEA Desk - Spacious Workspace",
77+ "cost": "20.00",
88+ "images": [
99+ "https://www.ikea.com/us/en/images/products/linnmon-adils-desk-black-brown__0974302_pe812345_s5.jpg?f=xl"
1010+ ],
1111+ "description": "Specifications:\n\n- Dimensions: 79\" long tabletop\n- Color: Black-brown\n- Model: LINNMON with ADILS legs\n- Condition: Used, good overall state\n\nThis versatile desk has been a reliable workspace for multiple years. Ideal for various activities including:\n\n- Computer setup\n- Crafting\n- Project work\n- Organizational tasks\n\nSlight cosmetic wear, minor surface imperfections.\n\nPricing:\n\n- Retail replacement cost: Approximately $100\n- Selling price: $30\n\nFlexible payment options available.",
1212+ "location": "Portland, OR",
1313+ "condition": "Used - Good",
1414+ "category": "Furniture",
1515+ "postedAt": "2024-01-15"
1616+ },
1717+ {
1818+ "id": "orbi-router",
1919+ "title": "Orbi Router",
2020+ "cost": "0",
2121+ "images": [
2222+ "https://www.netgear.com/support/cloudimage/1/11/123573"
2323+ ],
2424+ "description": "This is an orbi wireless router. Normally it comes with satellites but\nthis one does not.",
2525+ "location": "Portland, OR",
2626+ "condition": "Used - Good",
2727+ "category": "Electronics",
2828+ "postedAt": "2024-01-18"
2929+ },
3030+ {
3131+ "id": "arris-router",
3232+ "title": "Arris Router",
3333+ "cost": "10.00",
3434+ "images": [
3535+ "https://m.media-amazon.com/images/I/71gmmM9G8CL._AC_SY879_.jpg"
3636+ ],
3737+ "description": "This is an Arris SURFBoard router.",
3838+ "location": "Portland, OR",
3939+ "condition": "Used - Good",
4040+ "category": "Electronics",
4141+ "postedAt": "2024-01-20"
4242+ },
4343+ {
4444+ "id": "dell-soundbar",
4545+ "title": "Dell Stereo SoundBar - AC511M",
4646+ "cost": "10.00",
4747+ "images": [
4848+ "https://m.media-amazon.com/images/I/71MiidZgcfL._AC_SX679_.jpg",
4949+ "https://m.media-amazon.com/images/I/71VKkGixWWL._AC_SX679_.jpg"
5050+ ],
5151+ "description": "- Until you hear it you'll hardly notice it's there Dell Stereo Soundbar can give you the sound you want without big speakers and Long cables.Total usb ports:1\n- USB powered the Dell Stereo Soundbar is simplicity itself Just Plug in to an available USB port on your monitor or laptop No extra software Installation No power cord No Batteries required\n- An included Soundbar mount offers Easy attachment to the monitor allowing you to enjoy clear Stereo sound without losing desk space",
5252+ "location": "Portland, OR",
5353+ "condition": "Used - Good",
5454+ "category": "Electronics",
5555+ "postedAt": "2024-01-22"
5656+ }
5757+ ]
5858+ },
5959+ "errors": []
6060+}
+167
graphql/useMessagesQuery.json
···11+{
22+ "data": {
33+ "chats": [
44+ {
55+ "id": "chat:1",
66+ "sender": "John Doe",
77+ "avatar": "JD",
88+ "unread": true,
99+ "messages": [
1010+ {
1111+ "id": "chat:1:1",
1212+ "sender": "them",
1313+ "text": "Hi, I'm interested in your vintage lamp. Is it still available?",
1414+ "time": "2:30 PM"
1515+ },
1616+ {
1717+ "id": "chat:1:2",
1818+ "sender": "me",
1919+ "text": "Yes, the vintage lamp is still available. Would you like more details about it?",
2020+ "time": "2:31 PM"
2121+ },
2222+ {
2323+ "id": "chat:1:3",
2424+ "sender": "them",
2525+ "text": "Absolutely! How old is it and what's the condition?",
2626+ "time": "2:35 PM"
2727+ },
2828+ {
2929+ "id": "chat:1:4",
3030+ "sender": "me",
3131+ "text": "The lamp is from the 1960s and in excellent condition. The base has a small chip but the shade is perfect.",
3232+ "time": "2:38 PM"
3333+ }
3434+ ]
3535+ },
3636+ {
3737+ "id": "chat:2",
3838+ "sender": "Sarah Johnson",
3939+ "avatar": "SJ",
4040+ "unread": true,
4141+ "messages": [
4242+ {
4343+ "id": "chat:2:1",
4444+ "sender": "them",
4545+ "text": "Can we negotiate the price on your records collection?",
4646+ "time": "1:15 PM"
4747+ },
4848+ {
4949+ "id": "chat:2:2",
5050+ "sender": "me",
5151+ "text": "Of course! What price were you thinking?",
5252+ "time": "1:17 PM"
5353+ },
5454+ {
5555+ "id": "chat:2:3",
5656+ "sender": "them",
5757+ "text": "I was thinking around $150 for the whole collection. Is that doable?",
5858+ "time": "1:20 PM"
5959+ },
6060+ {
6161+ "id": "chat:2:4",
6262+ "sender": "me",
6363+ "text": "I could do $175, but that's my final offer. The records are in near mint condition.",
6464+ "time": "1:22 PM"
6565+ }
6666+ ]
6767+ },
6868+ {
6969+ "id": "chat:3",
7070+ "sender": "Michael Brown",
7171+ "avatar": "MB",
7272+ "unread": false,
7373+ "messages": [
7474+ {
7575+ "id": "chat:3:1",
7676+ "sender": "them",
7777+ "text": "Thanks for the vintage camera! Can we arrange pickup tomorrow?",
7878+ "time": "Yesterday"
7979+ },
8080+ {
8181+ "id": "chat:3:2",
8282+ "sender": "me",
8383+ "text": "That sounds great! Would 2 PM work for you?",
8484+ "time": "Yesterday"
8585+ },
8686+ {
8787+ "id": "chat:3:3",
8888+ "sender": "them",
8989+ "text": "Yes, 2 PM is perfect. What's your address?",
9090+ "time": "Yesterday"
9191+ },
9292+ {
9393+ "id": "chat:3:4",
9494+ "sender": "me",
9595+ "text": "I'm at 123 Main Street, Apt 4B. Just ring the buzzer for Brown.",
9696+ "time": "Yesterday"
9797+ }
9898+ ]
9999+ },
100100+ {
101101+ "id": "chat:4",
102102+ "sender": "Alex Wilson",
103103+ "avatar": "AW",
104104+ "unread": false,
105105+ "messages": [
106106+ {
107107+ "id": "chat:4:1",
108108+ "sender": "them",
109109+ "text": "I'm looking for similar items like your sound system.",
110110+ "time": "Monday"
111111+ },
112112+ {
113113+ "id": "chat:4:2",
114114+ "sender": "me",
115115+ "text": "What specifically are you looking for? I might know where to find similar items.",
116116+ "time": "Monday"
117117+ },
118118+ {
119119+ "id": "chat:4:3",
120120+ "sender": "them",
121121+ "text": "Vinyl record players and mid-century modern speakers.",
122122+ "time": "Monday"
123123+ },
124124+ {
125125+ "id": "chat:4:4",
126126+ "sender": "me",
127127+ "text": "I know a great shop downtown that specializes in exactly that. Want me to text you the address?",
128128+ "time": "Monday"
129129+ }
130130+ ]
131131+ },
132132+ {
133133+ "id": "chat:5",
134134+ "sender": "Emily Davis",
135135+ "avatar": "ED",
136136+ "unread": false,
137137+ "messages": [
138138+ {
139139+ "id": "chat:5:1",
140140+ "sender": "them",
141141+ "text": "Can you provide more details about your furniture pieces?",
142142+ "time": "Last week"
143143+ },
144144+ {
145145+ "id": "chat:5:2",
146146+ "sender": "me",
147147+ "text": "Sure! The dining table is solid oak with a glass top, and the chairs are original mid-century design.",
148148+ "time": "Last week"
149149+ },
150150+ {
151151+ "id": "chat:5:3",
152152+ "sender": "them",
153153+ "text": "Are the chairs comfortable? I have back problems so seating is important to me.",
154154+ "time": "Last week"
155155+ },
156156+ {
157157+ "id": "chat:5:4",
158158+ "sender": "me",
159159+ "text": "Yes, they're very comfortable! The cushions are original and still in great shape. I can send you photos if you'd like.",
160160+ "time": "Last week"
161161+ }
162162+ ]
163163+ }
164164+ ]
165165+ },
166166+ "errors": []
167167+}
+7
graphql/useProfileQuery.json
···11+{
22+ "data": {
33+ },
44+ "errors": [
55+ {"description":"This query should not make it to the server!"}
66+ ]
77+}