Implement backend API and database services in Docker setup
- Added a new `api` service for the NestJS backend, including health checks and dependencies on PostgreSQL, Redis, and MinIO. - Introduced PostgreSQL and Redis services with health checks and configurations for data persistence. - Added MinIO for S3-compatible object storage and a one-shot service to initialize required buckets. - Updated the Nginx configuration to proxy requests to the new backend API and MinIO storage. - Enhanced the Dockerfile to support the new API environment variables and configurations. - Updated the `package.json` and `package-lock.json` to include new dependencies for QR code generation and other utilities. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
108
backend/test/nostr-session.e2e-spec.ts
Normal file
108
backend/test/nostr-session.e2e-spec.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-disable unicorn/no-useless-undefined */
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { createHash } from 'node:crypto';
|
||||
import {
|
||||
finalizeEvent,
|
||||
generateSecretKey,
|
||||
getPublicKey,
|
||||
type UnsignedEvent,
|
||||
} from 'nostr-tools';
|
||||
import { AuthController } from '../src/auth/auth.controller';
|
||||
import { AuthService } from '../src/auth/auth.service';
|
||||
import { UsersService } from '../src/users/users.service';
|
||||
import { FilmmakersService } from '../src/filmmakers/filmmakers.service';
|
||||
import { NostrAuthGuard } from '../src/nostr-auth/nostr-auth.guard';
|
||||
import { NostrAuthService } from '../src/nostr-auth/nostr-auth.service';
|
||||
import { NostrSessionService } from '../src/auth/nostr-session.service';
|
||||
import { NostrSessionJwtGuard } from '../src/auth/guards/nostr-session-jwt.guard';
|
||||
|
||||
const hashPayload = (payload: string) =>
|
||||
createHash('sha256').update(payload).digest('hex');
|
||||
|
||||
describe('Nostr session bridge (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
const secretKey = generateSecretKey();
|
||||
const pubkey = getPublicKey(secretKey);
|
||||
const host = 'nostr.test';
|
||||
const path = '/auth/nostr/session';
|
||||
const url = `http://${host}${path}`;
|
||||
|
||||
const buildAuthHeader = (unsignedEvent: UnsignedEvent): string => {
|
||||
const event = finalizeEvent(unsignedEvent, secretKey);
|
||||
return `Nostr ${Buffer.from(JSON.stringify(event)).toString('base64')}`;
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
providers: [
|
||||
NostrSessionService,
|
||||
NostrAuthService,
|
||||
NostrAuthGuard,
|
||||
NostrSessionJwtGuard,
|
||||
{ provide: AuthService, useValue: {} },
|
||||
{
|
||||
provide: UsersService,
|
||||
useValue: {
|
||||
findUserByNostrPubkey: jest.fn().mockResolvedValue(undefined),
|
||||
findUsersById: jest.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
},
|
||||
{ provide: FilmmakersService, useValue: {} },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('issues, refreshes, and accepts nostr session JWTs', async () => {
|
||||
const body = { ping: 'pong' };
|
||||
const payload = JSON.stringify(body);
|
||||
|
||||
const authHeader = buildAuthHeader({
|
||||
pubkey,
|
||||
kind: 27_235,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [
|
||||
['u', url],
|
||||
['method', 'POST'],
|
||||
['payload', hashPayload(payload)],
|
||||
],
|
||||
content: '',
|
||||
});
|
||||
|
||||
const createResponse = await request(app.getHttpServer())
|
||||
.post(path)
|
||||
.set('host', host)
|
||||
.set('x-forwarded-proto', 'http')
|
||||
.set('authorization', authHeader)
|
||||
.send(body)
|
||||
.expect(201);
|
||||
|
||||
const { accessToken, refreshToken } = createResponse.body;
|
||||
expect(accessToken).toBeDefined();
|
||||
expect(refreshToken).toBeDefined();
|
||||
|
||||
const refreshResponse = await request(app.getHttpServer())
|
||||
.post('/auth/nostr/refresh')
|
||||
.send({ refreshToken })
|
||||
.expect(201);
|
||||
|
||||
expect(refreshResponse.body.accessToken).toBeDefined();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/auth/nostr/logout')
|
||||
.set('authorization', `Bearer ${accessToken}`)
|
||||
.expect(201)
|
||||
.expect((res) => expect(res.body.success).toBe(true));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user