Skip to content

Commit a37b121

Browse files
authored
Merge branch 'LabsCrypt:main' into fix/stream-create-form-labels
2 parents 95e5cdb + f50d60b commit a37b121

27 files changed

Lines changed: 141 additions & 205 deletions

File tree

.github/dependabot.yml

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ version: 2
22

33
updates:
44
# ── npm: root workspace ────────────────────────────────────────────────────
5+
# flowfi is a single npm workspace (frontend + backend hoisted into one root
6+
# package-lock.json). Dependabot must update from the workspace root so the
7+
# root lockfile CI runs `npm ci` against stays in sync. Per-directory entries
8+
# for /frontend and /backend only touched their package.json without updating
9+
# the root lockfile, so every PR they opened failed `npm ci` with
10+
# "lock file's X does not satisfy Y". One root entry covers all workspaces.
511
- package-ecosystem: "npm"
612
directory: "/"
713
schedule:
@@ -14,32 +20,6 @@ updates:
1420
- "minor"
1521
- "patch"
1622

17-
# ── npm: frontend ──────────────────────────────────────────────────────────
18-
- package-ecosystem: "npm"
19-
directory: "/frontend"
20-
schedule:
21-
interval: "weekly"
22-
day: "monday"
23-
open-pull-requests-limit: 10
24-
groups:
25-
minor-and-patch:
26-
update-types:
27-
- "minor"
28-
- "patch"
29-
30-
# ── npm: backend ───────────────────────────────────────────────────────────
31-
- package-ecosystem: "npm"
32-
directory: "/backend"
33-
schedule:
34-
interval: "weekly"
35-
day: "monday"
36-
open-pull-requests-limit: 10
37-
groups:
38-
minor-and-patch:
39-
update-types:
40-
- "minor"
41-
- "patch"
42-
4323
# ── Cargo: contracts ───────────────────────────────────────────────────────
4424
- package-ecosystem: "cargo"
4525
directory: "/contracts"

.github/workflows/ci.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,6 @@ jobs:
4242
run: npm run test:coverage
4343
working-directory: frontend
4444

45-
- name: Upload frontend coverage to Codecov
46-
uses: codecov/codecov-action@v5
47-
with:
48-
files: frontend/coverage/lcov.info
49-
flags: frontend
50-
name: frontend-coverage
51-
fail_ci_if_error: false
52-
verbose: true
53-
env:
54-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
55-
5645
- name: Build
5746
run: npm run build
5847
working-directory: frontend

backend/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Database
22
DATABASE_URL="postgresql://user:password@localhost:5433/flowfi?schema=public"
33

4+
# PostgreSQL pool settings
5+
# Maximum database connections per backend process (default: 10)
6+
PG_POOL_MAX=10
7+
# How long an idle connection stays open before being closed (milliseconds, default: 30000)
8+
PG_IDLE_TIMEOUT_MS=30000
9+
# How long to wait when establishing a new database connection (milliseconds, default: 5000)
10+
PG_CONNECTION_TIMEOUT_MS=5000
11+
# Maximum time a PostgreSQL statement may run before cancellation (milliseconds, default: 30000)
12+
PG_STATEMENT_TIMEOUT_MS=30000
13+
414
# Server
515
PORT=3001
616
NODE_ENV=development

backend/Dockerfile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ COPY package*.json ./
2121
RUN npm install --omit=dev
2222

2323
COPY --from=builder /app/dist ./dist
24-
COPY --from=builder /app/src/generated ./src/generated
24+
COPY --from=builder /app/src/generated ./dist/generated
25+
COPY --from=builder /app/prisma ./prisma
26+
# Prisma 7 reads the schema location and datasource url from prisma.config.ts,
27+
# and the schema's `datasource db {}` block has no inline url. The CI health
28+
# check runs `prisma db push` inside this image, so the config must be present
29+
# too (dotenv is a runtime dependency, so the config loads).
30+
COPY prisma.config.ts ./
2531

2632
EXPOSE 3001
2733

backend/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
"express-rate-limit": "^8.5.2",
3333
"ioredis": "^5.11.1",
3434
"pg": "^8.21.0",
35-
"stellar-sdk": "^13.3.0",
3635
"swagger-jsdoc": "^6.3.0",
3736
"swagger-ui-express": "^5.0.1",
3837
"winston": "^3.11.0",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- CreateIndex
2+
CREATE INDEX IF NOT EXISTS "Stream_tokenAddress_idx" ON "Stream"("tokenAddress");

backend/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ model Stream {
5151
5252
@@index([sender])
5353
@@index([recipient])
54+
@@index([tokenAddress])
5455
@@index([streamId])
5556
@@index([isActive])
5657
@@index([isPaused])

backend/prisma/seed.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import pg from 'pg';
21
import { PrismaPg } from '@prisma/adapter-pg';
32
import { PrismaClient } from '../src/generated/prisma/index.js';
3+
import { createPgPool } from '../src/lib/pg-pool.js';
44

5-
const connectionString = process.env.DATABASE_URL;
6-
const pool = new pg.Pool({ connectionString });
5+
const pool = createPgPool();
76
const adapter = new PrismaPg(pool);
87
const prisma = new PrismaClient({ adapter });
98

backend/src/controllers/stream.controller.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,11 @@ export const getStreamEvents = async (req: Request, res: Response) => {
316316
const [events, total] = await Promise.all([
317317
prisma.streamEvent.findMany({
318318
where: whereClause,
319-
orderBy: { timestamp: order },
319+
// `timestamp` is not unique (events in the same block/ledger can
320+
// share a timestamp), so it can't be the sole sort key for cursor
321+
// pagination. Add `id` as a unique tiebreaker so ordering (and
322+
// therefore cursor pagination) is stable across pages.
323+
orderBy: [{ timestamp: order }, { id: order }],
320324
take: limit,
321325
...(cursor
322326
? { cursor: { id: cursor }, skip: 1 }
@@ -770,4 +774,4 @@ export const resumeStream = async (req: Request, res: Response) => {
770774
logger.error('Error resuming stream:', error);
771775
return res.status(500).json({ error: 'Internal server error' });
772776
}
773-
};
777+
};

backend/src/lib/pg-pool.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pg from 'pg';
2+
3+
const parsePositiveIntegerEnv = (name: string, defaultValue: number): number => {
4+
const rawValue = process.env[name];
5+
6+
if (!rawValue) return defaultValue;
7+
8+
const parsedValue = Number.parseInt(rawValue, 10);
9+
10+
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : defaultValue;
11+
};
12+
13+
export const createPgPoolConfig = (): pg.PoolConfig => ({
14+
connectionString: process.env.DATABASE_URL,
15+
max: parsePositiveIntegerEnv('PG_POOL_MAX', 10),
16+
idleTimeoutMillis: parsePositiveIntegerEnv('PG_IDLE_TIMEOUT_MS', 30_000),
17+
connectionTimeoutMillis: parsePositiveIntegerEnv('PG_CONNECTION_TIMEOUT_MS', 5_000),
18+
statement_timeout: parsePositiveIntegerEnv('PG_STATEMENT_TIMEOUT_MS', 30_000),
19+
});
20+
21+
export const createPgPool = () => new pg.Pool(createPgPoolConfig());

0 commit comments

Comments
 (0)