Custom RTMP saved accounts, CUSTOM destination prepare, debug logging

- Add POST /providers/accounts/custom-rtmp endpoint for saved RTMP servers
- Encrypt rtmpUrl/streamKey in accessTokenEnc/refreshTokenEnc fields
- Decrypt and return rtmpUrl/streamKey in GET /providers/accounts for CUSTOM_RTMP
- Skip token revocation on DELETE for CUSTOM_RTMP accounts
- Decrypt CUSTOM_RTMP credentials into CUSTOM destinations on plan create/update
- Handle CUSTOM destinations in prepare lifecycle (already READY, skip provider auth)
- Add debug logging for plan operations and user upsert
This commit is contained in:
2026-03-01 10:50:28 +01:00
parent 02755bd1f0
commit 08cca68086
6 changed files with 239 additions and 46 deletions

View File

@@ -82,11 +82,17 @@ const lifecycleRoutes: FastifyPluginAsync = async (fastify) => {
},
},
}, async (request) => {
request.log.info({ planId: request.params.id, userId: request.userId }, 'Prepare plan request');
const plan = await fastify.prisma.streamPlan.findFirst({
where: { id: request.params.id, userId: request.userId },
include: { destinations: true },
});
if (!plan) throw new AppError(404, 'Stream plan not found');
if (!plan) {
// Debug: check if plan exists under any user
const anyPlan = await fastify.prisma.streamPlan.findUnique({ where: { id: request.params.id } });
request.log.warn({ planId: request.params.id, userId: request.userId, existsUnderOtherUser: !!anyPlan, otherUserId: anyPlan?.userId }, 'Plan not found for user');
throw new AppError(404, 'Stream plan not found');
}
// If already READY, return the existing prepared data
if (plan.status === 'READY') {
@@ -94,7 +100,7 @@ const lifecycleRoutes: FastifyPluginAsync = async (fastify) => {
planId: plan.id,
destinations: plan.destinations.map((dest) => ({
id: dest.id,
serviceId: dest.serviceId as 'YOUTUBE' | 'TWITCH',
serviceId: dest.serviceId as 'YOUTUBE' | 'TWITCH' | 'CUSTOM',
rtmpUrl: dest.rtmpUrl || '',
streamKey: dest.streamKey || '',
broadcastId: dest.broadcastId || '',
@@ -110,6 +116,24 @@ const lifecycleRoutes: FastifyPluginAsync = async (fastify) => {
const prepared: PreparedDestination[] = [];
for (const dest of plan.destinations) {
// CUSTOM destinations are already READY with rtmpUrl/streamKey set at creation
if (dest.serviceId === 'CUSTOM') {
if (dest.status !== 'READY') {
await fastify.prisma.streamDestination.update({
where: { id: dest.id },
data: { status: 'READY' },
});
}
prepared.push({
id: dest.id,
serviceId: 'CUSTOM',
rtmpUrl: dest.rtmpUrl || '',
streamKey: dest.streamKey || '',
broadcastId: '',
});
continue;
}
const { account, accessToken } = await getDecryptedTokenByAccountId(
fastify.prisma,
request.userId,