Add ntfy.sh

This commit is contained in:
Dries Augustyns
2025-12-04 17:34:24 +01:00
parent 726493a1ed
commit c114feb32e
15 changed files with 1070 additions and 19 deletions
+4
View File
@@ -7,6 +7,7 @@ import {prisma} from '../database/prisma.js';
import {redis, REDIS_ONE_MINUTE} from '../database/redis.js';
import {jwt} from '../middleware/auth.js';
import {AuthService} from '../services/AuthService.js';
import {NtfyService} from '../services/NtfyService.js';
import {UserService} from '../services/UserService.js';
import {Keys} from '../services/keys.js';
import {CatchAsync} from '../utils/asyncHandler.js';
@@ -68,6 +69,9 @@ export class Auth {
await redis.set(Keys.User.id(created_user.id), JSON.stringify(created_user), 'EX', REDIS_ONE_MINUTE * 60);
// Send notification about new user signup
await NtfyService.notifyUserSignup(created_user.email, created_user.id);
const token = jwt.sign(created_user.id);
const cookie = UserService.cookieOptions();
+8
View File
@@ -10,6 +10,7 @@ import {
} from '../../app/constants.js';
import {prisma} from '../../database/prisma.js';
import {jwt} from '../../middleware/auth.js';
import {NtfyService} from '../../services/NtfyService.js';
import {UserService} from '../../services/UserService.js';
import {CatchAsync} from '../../utils/asyncHandler.js';
@@ -59,6 +60,7 @@ export class Github {
const email = emails.find((e: {primary: boolean; email: string}) => e.primary).email;
let user = await UserService.email(email as string);
let isNewUser = false;
if (!user) {
user = await prisma.user.create({
@@ -67,12 +69,18 @@ export class Github {
type: 'GITHUB_OAUTH',
},
});
isNewUser = true;
}
if (user.type !== 'GITHUB_OAUTH') {
return res.redirect(DASHBOARD_URI + '/auth/login?message=You used another form of authentication');
}
// Send notification if this is a new user
if (isNewUser) {
await NtfyService.notifyUserOAuthSignup(user.email, user.id, 'GitHub');
}
const token = jwt.sign(user.id);
const cookie = UserService.cookieOptions();
+8
View File
@@ -10,6 +10,7 @@ import {
} from '../../app/constants.js';
import {prisma} from '../../database/prisma.js';
import {jwt} from '../../middleware/auth.js';
import {NtfyService} from '../../services/NtfyService.js';
import {UserService} from '../../services/UserService.js';
import {CatchAsync} from '../../utils/asyncHandler.js';
@@ -53,6 +54,7 @@ export class Google {
);
let user = await UserService.email(email);
let isNewUser = false;
if (!user) {
user = await prisma.user.create({
@@ -61,12 +63,18 @@ export class Google {
type: 'GOOGLE_OAUTH',
},
});
isNewUser = true;
}
if (user.type !== 'GOOGLE_OAUTH') {
return res.redirect(DASHBOARD_URI + '/auth/login?message=You used another form of authentication');
}
// Send notification if this is a new user
if (isNewUser) {
await NtfyService.notifyUserOAuthSignup(user.email, user.id, 'Google');
}
const token = jwt.sign(user.id);
const cookie = UserService.cookieOptions();
+22
View File
@@ -11,6 +11,7 @@ import {NotAuthenticated, NotFound} from '../exceptions/index.js';
import type {AuthResponse} from '../middleware/auth.js';
import {isAuthenticated} from '../middleware/auth.js';
import {BillingLimitService} from '../services/BillingLimitService.js';
import {NtfyService} from '../services/NtfyService.js';
import {SecurityService} from '../services/SecurityService.js';
import {UserService} from '../services/UserService.js';
import {CatchAsync} from '../utils/asyncHandler.js';
@@ -82,6 +83,9 @@ export class Users {
},
});
// Send notification about project creation
await NtfyService.notifyProjectCreated(project.name, project.id, auth.userId);
return res.status(201).json(project);
}
@@ -150,8 +154,22 @@ export class Users {
public: publicKey,
secret: secretKey,
},
select: {
id: true,
name: true,
public: true,
secret: true,
createdAt: true,
updatedAt: true,
disabled: true,
customer: true,
subscription: true,
},
});
// Send notification about API key regeneration
await NtfyService.notifyApiKeysRegenerated(project.name!, id!, auth.userId!);
return res.status(200).json(project);
}
@@ -717,6 +735,7 @@ export class Users {
const project = await prisma.project.findUnique({
where: {id},
select: {
name: true,
subscription: true,
customer: true,
},
@@ -741,6 +760,9 @@ export class Users {
where: {id},
});
// Send notification about project deletion
await NtfyService.notifyProjectDeleted(project.name, id, auth.userId);
return res.status(200).json({success: true, message: 'Project deleted successfully'});
}
}
+28 -5
View File
@@ -9,6 +9,7 @@ import {STRIPE_ENABLED, STRIPE_WEBHOOK_SECRET} from '../app/constants.js';
import {stripe} from '../app/stripe.js';
import {prisma} from '../database/prisma.js';
import {EventService} from '../services/EventService.js';
import {NtfyService} from '../services/NtfyService.js';
import {SecurityService} from '../services/SecurityService.js';
import {CatchAsync} from '../utils/asyncHandler.js';
@@ -165,6 +166,14 @@ export class Webhooks {
bounceType: body.bounce?.bounceType,
bouncedAt: now.toISOString(),
};
// Send notification about bounce
await NtfyService.notifyEmailBounce(
email.project.name,
email.projectId,
email.contact.email,
body.bounce?.bounceType,
);
break;
case 'Complaint':
@@ -180,6 +189,9 @@ export class Webhooks {
...baseEventData,
complainedAt: now.toISOString(),
};
// Send notification about complaint
await NtfyService.notifyEmailComplaint(email.project.name, email.projectId, email.contact.email);
break;
default:
@@ -256,7 +268,7 @@ export class Webhooks {
}
// Update project with customer and subscription IDs
await prisma.project.update({
const updatedProject = await prisma.project.update({
where: {id: projectId},
data: {
customer: customerId,
@@ -265,6 +277,9 @@ export class Webhooks {
});
signale.success(`[WEBHOOK] Checkout completed for project ${projectId}`);
// Send notification about subscription started
await NtfyService.notifySubscriptionStarted(updatedProject.name, projectId, subscriptionId);
break;
}
@@ -283,7 +298,9 @@ export class Webhooks {
}
signale.success(`[WEBHOOK] Invoice paid for project ${project.name} (${project.id})`);
// Additional logic can be added here (e.g., extend trial, update billing status)
// Send notification about invoice payment
await NtfyService.notifyInvoicePaid(project.name, project.id);
break;
}
@@ -302,7 +319,9 @@ export class Webhooks {
}
signale.warn(`[WEBHOOK] Payment failed for project ${project.name} (${project.id})`);
// Additional logic can be added here (e.g., disable project, send notification)
// Send notification about payment failure
await NtfyService.notifyPaymentFailed(project.name, project.id);
break;
}
@@ -329,7 +348,9 @@ export class Webhooks {
});
signale.warn(`[WEBHOOK] Subscription deleted for project ${project.name} (${project.id})`);
// Additional logic can be added here (e.g., downgrade to free plan)
// Send notification about subscription cancellation
await NtfyService.notifySubscriptionCancelled(project.name, project.id, subscriptionId);
break;
}
@@ -351,7 +372,9 @@ export class Webhooks {
signale.info(
`[WEBHOOK] Status: ${subscription.status}, Cancel at period end: ${subscription.cancel_at_period_end}`,
);
// Additional logic can be added here (e.g., update subscription status, handle plan changes)
// Send notification about subscription update
await NtfyService.notifySubscriptionUpdated(project.name, project.id);
break;
}