Phases 2-4: Auth, providers, stream management

This commit is contained in:
2026-02-23 15:32:24 +01:00
parent 8ea3279c3b
commit 538c24c58f
14 changed files with 1530 additions and 0 deletions

74
src/routes/auth/meta.ts Normal file
View File

@@ -0,0 +1,74 @@
import { FastifyPluginAsync } from 'fastify';
import { randomUUID } from 'node:crypto';
import { exchangeMetaCode, fetchMetaProfile } from '../../services/meta-auth.service.js';
import { signAccessToken } from '../../plugins/auth.js';
import { hashToken } from '../../services/crypto.service.js';
import { config } from '../../config.js';
import { AppError } from '../../plugins/error-handler.js';
import type { MetaCallbackBody, AuthTokensResponse } from '../../types/api.js';
const metaAuthRoutes: FastifyPluginAsync = async (fastify) => {
fastify.post<{ Body: MetaCallbackBody }>('/auth/meta/callback', {
schema: {
body: {
type: 'object',
required: ['code'],
properties: {
code: { type: 'string', minLength: 1 },
deviceInfo: { type: 'string' },
},
additionalProperties: false,
},
},
}, async (request, reply) => {
const { code, deviceInfo } = request.body;
// Exchange code for Meta access token
const { accessToken: metaToken } = await exchangeMetaCode(code);
// Fetch user profile
const profile = await fetchMetaProfile(metaToken);
// Upsert user
const user = await fastify.prisma.user.upsert({
where: { metaId: profile.metaId },
update: {
displayName: profile.displayName,
email: profile.email,
avatarUrl: profile.avatarUrl,
},
create: {
metaId: profile.metaId,
displayName: profile.displayName,
email: profile.email,
avatarUrl: profile.avatarUrl,
},
});
// Create session with hashed refresh token
const refreshToken = randomUUID();
const expiresAt = new Date(Date.now() + config.jwt.refreshTtl * 1000);
await fastify.prisma.session.create({
data: {
userId: user.id,
refreshToken: hashToken(refreshToken),
expiresAt,
deviceInfo: deviceInfo ?? null,
},
});
// Sign JWT
const accessToken = await signAccessToken(user.id);
const response: AuthTokensResponse = {
accessToken,
refreshToken,
expiresIn: config.jwt.accessTtl,
};
reply.status(200).send(response);
});
};
export default metaAuthRoutes;