Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/frontend-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Install dependencies
run: bun install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/frontend-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Install dependencies
run: bun install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Install dependencies
run: bun install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Set up Node.js for NPM publish
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Install dependencies
run: bun install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Install dependencies
run: bun install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testnet-api-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
bun-version: 1.3.6

- name: Create Directory
run: mkdir -p ./packages/api-main/data/postgres_data
Expand Down
100 changes: 100 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Contributing to Dither.chat

This guide will help you set up a local development environment for Dither.chat.

## Prerequisites

- Go 1.21+
- Node.js 20+
- Bun

## Setting Up a Local Network

You can run a local instance of the AtomOne blockchain to test Dither.chat.

### 1. Clone and Build AtomOne

```bash
git clone https://github.com/atomone-hub/atomone
cd atomone
make build
```

### 2. Start the Local Network

```bash
make localnet-start
```

### 3. Copy the Mnemonics

Look at the logs output and find the mnemonic phrases for the test accounts. Copy them for later use.

### 4. Enable CORS

Stop the running process, then run the following command to enable CORS for local development:

```bash
./build/atomoned --home ~/.atomone-localnet config set app api.enabled-unsafe-cors true
```

### 5. Restart the Local Network

```bash
make localnet-restart
```

### 6. Configure the Network Spammer (Optional)

If you want to generate test data, configure the network spammer tool:

```bash
cd packages/tool-network-spammer
```

Create a `.env` file with the following content:

```env
MNEMONIC="your mnemonic phrase here"
RPC_ENDPOINT="http://localhost:26657"
INTERVAL_MS=10000
```

Replace `your mnemonic phrase here` with one of the mnemonics from step 3.

## Running the Application

Start the services in the following order (each in a new terminal):

### 1. Start the API

```bash
docker run -d --name dither-postgres -p 5432:5432 -v dither_postgres_data:/var/lib/postgresql/data -e POSTGRES_USER=default -e POSTGRES_PASSWORD=password -e POSTGRES_DB=postgres postgres:16
cd packages/api-main
bun start
```

### 2. Start the Reader

```bash
cd packages/reader-main
API_URLS=http://localhost:1317 bun start
```

The reader will start indexing blockchain data and populating the database.

### 3. Start the Spammer (Optional)

To generate test data:

```bash
cd packages/tool-network-spammer
bun start
```

### 4. Start the Frontend

```bash
cd packages/frontend-main
VITE_SKIP_CSP=true VITE_ENVIRONMENT_TYPE=localnet bun dev
```
484 changes: 213 additions & 271 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@
"*.{js,jsx,ts,tsx,mjs,cjs,vue}": "eslint --fix",
"*.{json,yml,yaml,md}": "eslint --fix"
},
"packageManager": "bun@1.3.2"
"packageManager": "bun@1.3.6"
}
2 changes: 1 addition & 1 deletion packages/api-main/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PG_URI="postgresql://default:password@localhost:5432/postgres"
AUTH=default_auth_secret
JWT=default_jwt_secret
JWT_STRICTNESS=lax
DISCORD_WEBHOOK_URL=

TELEGRAM_BOT_TOKEN=
TELEGRAM_BOT_USERNAME=@bot_username
Expand Down
4 changes: 2 additions & 2 deletions packages/api-main/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Stage 1: Dependencies installation
FROM oven/bun:1.3.2-slim AS deps
FROM oven/bun:1.3.6-slim AS deps
WORKDIR /app

# Copy all monorepo files, excluding the ones ignored by dockerignore.
Expand All @@ -11,7 +11,7 @@ RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \
bun install --frozen-lockfile

# Stage 2: Runtime
FROM oven/bun:1.3.2-slim AS runtime
FROM oven/bun:1.3.6-slim AS runtime
WORKDIR /app

# Copy installed dependencies from deps stage (only root node_modules, bun hoists workspace deps)
Expand Down
19 changes: 18 additions & 1 deletion packages/api-main/drizzle/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const FeedTable = pgTable(
likes: integer().default(0), // The amount of likes this post / reply has
dislikes: integer().default(0), // The amount of dislikes this post / reply has
flags: integer().default(0), // The amount of flags this post / reply has
reposts: integer().default(0), // The amount of reposts this post / reply has
likes_burnt: text().default('0'), // The amount of tokens burnt from each user who liked this post / reply
dislikes_burnt: text().default('0'), // The amount of tokens burnt from each user who disliked this post / reply
flags_burnt: text().default('0'), // The amount of tokens burnt from each user who wante dto flag this post / reply
Expand Down Expand Up @@ -75,6 +76,21 @@ export const FlagsTable = pgTable(
],
);

export const RepostsTable = pgTable(
'reposts',
{
hash: varchar({ length: 64 }).primaryKey(),
post_hash: varchar({ length: 64 }).notNull(),
author: varchar({ length: 44 }).notNull(),
quantity: text().default('0'),
timestamp: timestamp({ withTimezone: true }).notNull(),
},
t => [
index('repost_post_hash_idx').on(t.post_hash),
index('repost_author_idx').on(t.author),
],
);

export const FollowsTable = pgTable(
'follows',
{
Expand Down Expand Up @@ -129,7 +145,7 @@ export const ModeratorTable = pgTable('moderators', {
deleted_at: timestamp({ withTimezone: true }),
});

export const notificationTypeEnum = pgEnum('notification_type', ['like', 'dislike', 'flag', 'follow', 'reply']);
export const notificationTypeEnum = pgEnum('notification_type', ['like', 'dislike', 'flag', 'follow', 'reply', 'repost']);

export const NotificationTable = pgTable(
'notifications',
Expand Down Expand Up @@ -159,6 +175,7 @@ export const tables = [
'likes',
'dislikes',
'flags',
'reposts',
'follows',
'audits',
'moderators',
Expand Down
4 changes: 2 additions & 2 deletions packages/api-main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"db:generate": "bunx drizzle-kit generate",
"db:migrate": "bunx drizzle-kit migrate",
"db:push": "bunx drizzle-kit push",
"db:push:force": "bunx drizzle-kit push --force",
"push-and-start": "bunx drizzle-kit push --force && bun start"
"db:push:force": "drizzle-kit push --force",
"push-and-start": "drizzle-kit push --force && bun start"
},
"dependencies": {
"@atomone/chronostate": "^2.3.0",
Expand Down
7 changes: 0 additions & 7 deletions packages/api-main/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ interface Config {
AUTH: string;
JWT: string;
JWT_STRICTNESS: JWT_STRICTNESS;
DISCORD_WEBHOOK_URL: string;
}

let config: Config;
Expand Down Expand Up @@ -41,18 +40,12 @@ export function useConfig(): Config {
process.env.JWT_STRICTNESS = 'lax';
}

if (typeof process.env.DISCORD_WEBHOOK_URL === 'undefined') {
console.warn(`DISCORD_WEBHOOK_URL not set, defaulting to empty`);
process.env.DISCORD_WEBHOOK_URL = '';
}

config = {
PORT: process.env.PORT ? Number.parseInt(process.env.PORT) : 3000,
PG_URI: process.env.PG_URI,
AUTH: process.env.AUTH as string,
JWT: process.env.JWT as string,
JWT_STRICTNESS: process.env.JWT_STRICTNESS as JWT_STRICTNESS,
DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL,
};

return config;
Expand Down
21 changes: 16 additions & 5 deletions packages/api-main/src/gets/posts.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import type { Gets } from '@atomone/dither-api-types';

import { and, desc, eq, gte, inArray, isNull, sql } from 'drizzle-orm';
import { and, desc, eq, getTableColumns, gte, inArray, isNull, sql } from 'drizzle-orm';

import { getDatabase } from '../../drizzle/db';
import { FeedTable, FollowsTable } from '../../drizzle/schema';
import { FeedTable, FollowsTable, RepostsTable } from '../../drizzle/schema';

const feedColumns = {
...getTableColumns(FeedTable),
reposted_by: RepostsTable.author,
reposted_at: RepostsTable.timestamp,
};

const statement = getDatabase()
.select()
.select(feedColumns)
.from(FeedTable)
.leftJoin(
RepostsTable,
eq(FeedTable.hash, RepostsTable.post_hash),
)
.where(
and(
eq(FeedTable.author, sql.placeholder('author')),
Expand All @@ -18,7 +28,7 @@ const statement = getDatabase()
)
.limit(sql.placeholder('limit'))
.offset(sql.placeholder('offset'))
.orderBy(desc(FeedTable.timestamp))
.orderBy(desc(sql`COALESCE(${RepostsTable.timestamp}, ${FeedTable.timestamp})`))
.prepare('stmnt_get_posts');

export async function Posts(query: Gets.PostsQuery) {
Expand All @@ -40,10 +50,11 @@ export async function Posts(query: Gets.PostsQuery) {

try {
const results = await statement.execute({ author: query.address, limit, offset, minQuantity });

return { status: 200, rows: results };
} catch (error) {
console.error(error);
return { status: 404, error: 'failed to find matching reply' };
return { status: 404, error: 'failed to find matching posts' };
}
}

Expand Down
2 changes: 0 additions & 2 deletions packages/api-main/src/posts/dislike.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { getDatabase } from '../../drizzle/db';
import { DislikesTable, FeedTable } from '../../drizzle/schema';
import { notify } from '../shared/notify';
import { useSharedQueries } from '../shared/useSharedQueries';
import { postToDiscord } from '../utility';

const sharedQueries = useSharedQueries();

Expand Down Expand Up @@ -62,7 +61,6 @@ export async function Dislike(body: Posts.DislikeBody) {
});
}

await postToDiscord(`Disliked by ${body.from.toLowerCase()}`, `https://dither.chat/post/${body.hash.toLowerCase()}`);
return { status: 200 };
} catch (err) {
console.error(err);
Expand Down
1 change: 1 addition & 0 deletions packages/api-main/src/posts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export * from './mod';
export * from './post';
export * from './postRemove';
export * from './reply';
export * from './repost';
export * from './unfollow';
export * from './updateState';
2 changes: 0 additions & 2 deletions packages/api-main/src/posts/like.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { getDatabase } from '../../drizzle/db';
import { FeedTable, LikesTable } from '../../drizzle/schema';
import { notify } from '../shared/notify';
import { useSharedQueries } from '../shared/useSharedQueries';
import { postToDiscord } from '../utility';

const sharedQueries = useSharedQueries();

Expand Down Expand Up @@ -62,7 +61,6 @@ export async function Like(body: Posts.LikeBody) {
});
}

await postToDiscord(`Liked by ${body.from.toLowerCase()}`, `https://dither.chat/post/${body.hash.toLowerCase()}`);
return { status: 200 };
} catch (err) {
console.error(err);
Expand Down
3 changes: 0 additions & 3 deletions packages/api-main/src/posts/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { desc, eq, sql } from 'drizzle-orm';

import { getDatabase } from '../../drizzle/db';
import { AuditTable, FeedTable } from '../../drizzle/schema';
import { postToDiscord } from '../utility';

const statement = getDatabase()
.insert(FeedTable)
Expand Down Expand Up @@ -34,8 +33,6 @@ export async function Post(body: Posts.PostBody) {

await removePostIfBanned(body);

await postToDiscord(body.msg, `https://dither.chat/post/${body.hash.toLowerCase()}`);

return { status: 200 };
} catch (err) {
console.error(err);
Expand Down
Loading
Loading