-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from farcasterxyz/horsefacts/api-routes
feat: naive API routes
- Loading branch information
Showing
10 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{ | ||
"name": "quikcast", | ||
"version": "0.1.0", | ||
"private": true, | ||
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"lint": "next lint", | ||
"db:codegen": "kysely-codegen --dialect postgres --out-file ./src/lib/database/types.ts" | ||
}, | ||
"dependencies": { | ||
"@farcaster/auth-kit": "^0.0.39", | ||
"@farcaster/core": "^0.13.4", | ||
"next": "14.1.0", | ||
"next-auth": "^4.24.5", | ||
"react": "^18", | ||
"react-dom": "^18" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^20", | ||
"@types/pg": "^8.10.9", | ||
"@types/react": "^18", | ||
"@types/react-dom": "^18", | ||
"autoprefixer": "^10.0.1", | ||
"eslint": "^8", | ||
"eslint-config-next": "14.1.0", | ||
"kysely": "^0.27.2", | ||
"kysely-codegen": "^0.11.0", | ||
"pg": "^8.11.3", | ||
"postcss": "^8", | ||
"tailwindcss": "^3.3.0", | ||
"typescript": "^5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { type NextRequest, NextResponse } from 'next/server'; | ||
|
||
import { getCasts } from '@/lib/services/casts'; | ||
|
||
export async function GET(request: NextRequest) { | ||
const searchParams = request.nextUrl.searchParams; | ||
const fid = searchParams.get('fid'); | ||
if (!fid) { | ||
return NextResponse.json({ error: 'fid is required' }, { status: 400 }); | ||
} | ||
const casts = await getCasts(fid); | ||
return NextResponse.json({ casts }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { type NextRequest, NextResponse } from 'next/server'; | ||
|
||
import { getFeed } from '@/lib/services/feed'; | ||
|
||
export async function GET(request: NextRequest) { | ||
const searchParams = request.nextUrl.searchParams; | ||
const fid = searchParams.get('fid'); | ||
if (!fid) { | ||
return NextResponse.json({ error: 'fid is required' }, { status: 400 }); | ||
} | ||
const feed = await getFeed(fid); | ||
return NextResponse.json({ feed }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { type NextRequest, NextResponse } from 'next/server'; | ||
|
||
import { getProfile } from '@/lib/services/user'; | ||
|
||
export async function GET(request: NextRequest) { | ||
const searchParams = request.nextUrl.searchParams; | ||
const fid = searchParams.get('fid'); | ||
if (!fid) { | ||
return NextResponse.json({ error: 'fid is required' }, { status: 400 }); | ||
} | ||
const profile = await getProfile(fid); | ||
return NextResponse.json({ profile }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Kysely, PostgresDialect } from 'kysely'; | ||
import { Pool } from 'pg'; | ||
|
||
import { DB } from './types'; | ||
|
||
const dialect = new PostgresDialect({ | ||
pool: new Pool({ | ||
connectionString: process.env.DATABASE_URL, | ||
}), | ||
}); | ||
|
||
export const db = new Kysely<DB>({ | ||
dialect, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import type { ColumnType } from "kysely"; | ||
|
||
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> | ||
? ColumnType<S, I | undefined, U> | ||
: ColumnType<T, T | undefined, T>; | ||
|
||
export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>; | ||
|
||
export type Json = ColumnType<JsonValue, string, string>; | ||
|
||
export type JsonArray = JsonValue[]; | ||
|
||
export type JsonObject = { | ||
[K in string]?: JsonValue; | ||
}; | ||
|
||
export type JsonPrimitive = boolean | number | string | null; | ||
|
||
export type JsonValue = JsonArray | JsonObject | JsonPrimitive; | ||
|
||
export type Timestamp = ColumnType<Date, Date | string, Date | string>; | ||
|
||
export interface Casts { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
embeds: Generated<Json>; | ||
fid: Int8; | ||
hash: Buffer; | ||
id: Generated<string>; | ||
mentions: Generated<Json>; | ||
mentions_positions: Generated<Json>; | ||
parent_fid: Int8 | null; | ||
parent_hash: Buffer | null; | ||
parent_url: string | null; | ||
root_parent_hash: Buffer | null; | ||
root_parent_url: string | null; | ||
text: string; | ||
timestamp: Timestamp; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface ChainEvents { | ||
block_hash: Buffer; | ||
block_number: Int8; | ||
block_timestamp: Timestamp; | ||
body: Json; | ||
chain_id: Int8; | ||
created_at: Generated<Timestamp>; | ||
fid: Int8; | ||
id: Generated<string>; | ||
log_index: number; | ||
raw: Buffer; | ||
transaction_hash: Buffer; | ||
transaction_index: number; | ||
type: number; | ||
} | ||
|
||
export interface Fids { | ||
chain_event_id: string; | ||
created_at: Generated<Timestamp>; | ||
custody_address: Buffer; | ||
fid: Int8; | ||
recovery_address: Buffer; | ||
registered_at: Timestamp; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface Fnames { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
id: Generated<string>; | ||
registered_at: Timestamp; | ||
type: number; | ||
updated_at: Generated<Timestamp>; | ||
username: string; | ||
} | ||
|
||
export interface Links { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
display_timestamp: Timestamp | null; | ||
fid: Int8; | ||
hash: Buffer; | ||
id: Generated<string>; | ||
target_fid: Int8; | ||
timestamp: Timestamp; | ||
type: string; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface Messages { | ||
body: Json; | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
hash: Buffer; | ||
hash_scheme: number; | ||
id: Generated<string>; | ||
pruned_at: Timestamp | null; | ||
raw: Buffer; | ||
revoked_at: Timestamp | null; | ||
signature: Buffer; | ||
signature_scheme: number; | ||
signer: Buffer; | ||
timestamp: Timestamp; | ||
type: number; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface Reactions { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
hash: Buffer; | ||
id: Generated<string>; | ||
target_cast_fid: Int8 | null; | ||
target_cast_hash: Buffer | null; | ||
target_url: string | null; | ||
timestamp: Timestamp; | ||
type: number; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface Signers { | ||
add_chain_event_id: string; | ||
added_at: Timestamp; | ||
created_at: Generated<Timestamp>; | ||
fid: Int8; | ||
id: Generated<string>; | ||
key: Buffer; | ||
key_type: number; | ||
metadata: Json; | ||
metadata_type: number; | ||
remove_chain_event_id: string | null; | ||
removed_at: Timestamp | null; | ||
requester_fid: Int8; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface StorageAllocations { | ||
chain_event_id: string; | ||
created_at: Generated<Timestamp>; | ||
expires_at: Timestamp; | ||
fid: Int8; | ||
id: Generated<string>; | ||
payer: Buffer; | ||
rented_at: Timestamp; | ||
units: number; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface UserData { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
hash: Buffer; | ||
id: Generated<string>; | ||
timestamp: Timestamp; | ||
type: number; | ||
updated_at: Generated<Timestamp>; | ||
value: string; | ||
} | ||
|
||
export interface UsernameProofs { | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
id: Generated<string>; | ||
owner: Buffer; | ||
signature: Buffer; | ||
timestamp: Timestamp; | ||
type: number; | ||
updated_at: Generated<Timestamp>; | ||
username: string; | ||
} | ||
|
||
export interface Verifications { | ||
block_hash: Buffer; | ||
created_at: Generated<Timestamp>; | ||
deleted_at: Timestamp | null; | ||
fid: Int8; | ||
hash: Buffer; | ||
id: Generated<string>; | ||
signature: Buffer; | ||
signer_address: Buffer; | ||
timestamp: Timestamp; | ||
updated_at: Generated<Timestamp>; | ||
} | ||
|
||
export interface DB { | ||
casts: Casts; | ||
chain_events: ChainEvents; | ||
fids: Fids; | ||
fnames: Fnames; | ||
links: Links; | ||
messages: Messages; | ||
reactions: Reactions; | ||
signers: Signers; | ||
storage_allocations: StorageAllocations; | ||
user_data: UserData; | ||
username_proofs: UsernameProofs; | ||
verifications: Verifications; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { sql } from 'kysely'; | ||
|
||
import { db } from '../database/db'; | ||
|
||
export function formatHash(hash: Buffer) { | ||
return hash.toString('hex'); | ||
} | ||
|
||
export async function getCasts(fid: string, limit = 100) { | ||
const profile = db | ||
.selectFrom('user_data') | ||
.select([ | ||
'fid', | ||
sql`MAX(CASE WHEN type = 1 THEN value ELSE NULL END)`.as('pfp_url'), | ||
sql`MAX(CASE WHEN type = 2 THEN value ELSE NULL END)`.as('display_name'), | ||
sql`MAX(CASE WHEN type = 3 THEN value ELSE NULL END)`.as('bio'), | ||
sql`MAX(CASE WHEN type = 6 THEN value ELSE NULL END)`.as('username'), | ||
]) | ||
.where('deleted_at', 'is', null) | ||
.where('fid', '=', fid) | ||
.groupBy('fid') | ||
.as('profile'); | ||
|
||
const casts = await db | ||
.selectFrom('casts') | ||
.leftJoin(profile, 'profile.fid', 'casts.fid') | ||
.select([ | ||
'casts.hash', | ||
'casts.timestamp', | ||
'casts.text', | ||
'casts.embeds', | ||
'casts.mentions', | ||
'casts.mentions_positions', | ||
'casts.fid', | ||
'profile.pfp_url', | ||
'profile.display_name', | ||
'profile.bio', | ||
'profile.username', | ||
]) | ||
.where('casts.fid', '=', fid) | ||
.where('casts.deleted_at', 'is', null) | ||
.where('casts.parent_hash', 'is', null) | ||
.orderBy('casts.timestamp', 'desc') | ||
.limit(limit) | ||
.execute(); | ||
|
||
return casts.map((row) => { | ||
const { fid, pfp_url, display_name, bio, username, ...rest } = row; | ||
return { | ||
...rest, | ||
hash: formatHash(row.hash), | ||
user: { | ||
fid, | ||
pfp_url, | ||
display_name, | ||
bio, | ||
username, | ||
}, | ||
}; | ||
}); | ||
} |
Oops, something went wrong.