all: remove the notification demo path
The test-push button and the unauthenticated /moods/notify broadcast existed only to fake a notification the system couldn't yet produce. Both are gone, so the only way to push is through the service that writes the row first
Pedro Lucas Porcellis porcellis@eletrotupi.com · 11 days ago
ce0e2eb7abbf8316bd9002e290d658d6c7da055f Browse filesParents
f6425d4Changed files
5
Additions
+16
Deletions
-69
api/src/controllers/moods.ts
1 -
import { Request, Response, NextFunction } from 'express';
1 +
import { Response, NextFunction } from 'express';
2 2
import { AuthenticatedRequest } from '@app/middleware/auth';
3 -
import { User } from '@prisma/client';
4 3
import {
5 4
createMood,
6 5
getMoodsByUserId,
7 6
getMoodById,
8 7
destroyMoodById
9 8
} from '@app/services/mood.service';
10 -
11 -
import { findUsersElligibleForPush } from '@app/services/user.service';
12 -
import { sendPushNotification } from '@app/lib/sendPushNotification';
13 9
14 10
import {
15 11
CreateMoodSchema
16 12
} from '@app/schemas'
17 13
import { formatValidationError } from '@app/lib/errors/validationError'
69 65
destroy: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
70 66
try {
71 67
const result = await destroyMoodById(req.userId!, Number(req.params.id!));
72 68
73 69
return res.status(200).json({})
74 -
} catch (err) {
75 -
next(err);
76 -
}
77 -
},
78 -
79 -
sendNotification: async (req: Request, res: Response, next: NextFunction) => {
80 -
try {
81 -
const users = await findUsersElligibleForPush();
82 -
83 -
if (users) {
84 -
await Promise.all(
85 -
users
86 -
.filter((user: User) => user.pushToken)
87 -
.map((user: User) =>
88 -
sendPushNotification({
89 -
token: user.pushToken!,
90 -
title: 'Como você está?',
91 -
body: 'Adicione mais registros de humor.',
92 -
data: { screen: 'notifications' },
93 -
})
94 -
)
95 -
);
96 -
}
97 -
98 -
return res.status(200).json(users)
99 70
} catch (err) {
100 71
next(err);
101 72
}
102 73
}
103 74
};
api/src/routes/moods.ts
1 1
import { Router } from "express";
2 2
import { MoodsController } from '@app/controllers/moods'
3 3
import { requireAuth } from '@app/middleware/auth';
4 4
5 5
const router = Router();
6 6
7 7
router.get('/', requireAuth, MoodsController.index);
8 8
router.get('/:id', requireAuth, MoodsController.show);
9 9
router.post('/', requireAuth, MoodsController.create);
10 10
router.delete('/:id', requireAuth, MoodsController.destroy);
11 -
router.post('/notify', MoodsController.sendNotification);
12 11
13 12
export default router;
api/src/services/user.service.ts
1 1
import { prisma } from '@app/lib/prisma';
2 2
import { User } from '@prisma/client';
3 3
import bcrypt from 'bcryptjs';
4 4
5 5
import {
61 61
id: input.id
62 62
},
63 63
data: updateData
64 64
})
65 65
}
66 -
67 -
export const findUsersElligibleForPush = async (): Promise<User[]> => {
68 -
return await prisma.user.findMany({
69 -
where: {
70 -
pushToken: {
71 -
not: null
72 -
}
73 -
}
74 -
});
75 -
}
/dev/null → api/tests/moods/notify.test.ts
1 +
import { describe, it, expect } from 'vitest'
2 +
import request from 'supertest'
3 +
import { createApp } from '@app/createApp'
4 +
5 +
const app = createApp()
6 +
7 +
// The unauthenticated demo broadcast was deleted once notifications became
8 +
// stored records; this guards against reintroducing it.
9 +
describe('POST /moods/notify', () => {
10 +
it('is no longer routed', async () => {
11 +
const res = await request(app).post('/moods/notify').send({})
12 +
13 +
expect(res.status).toBe(404)
14 +
})
15 +
})
frontend/app/notifications.tsx
1 1
import {
2 2
StyleSheet,
3 3
ScrollView,
4 4
KeyboardAvoidingView,
5 5
} from "react-native";
6 6
import { Text } from "@/components/ui/Text";
7 7
8 8
import { useState, useEffect } from "react";
9 9
import * as Notifications from "expo-notifications";
10 -
import { registerForPushNotifications } from "@/hooks/useRegisterPushToken";
11 10
import { useLocalSearchParams } from "expo-router";
12 11
import { StatusBar } from "expo-status-bar";
13 12
import { SafeAreaView } from "react-native-safe-area-context";
14 -
import { Button } from "@/components/ui/Button";
15 13
import { Colors, Spacing } from "@/constants/theme";
16 14
import { Card } from "@/components/ui/Cards";
17 15
import { Section, SectionHeader } from "@/components/ui/Sections";
18 16
19 17
type NotificationParams = {
41 39
return () => sub.remove();
42 40
}, []);
43 41
44 42
const displayed = liveNotification ?? (params.title || params.body ? { title: params.title, body: params.body } : null);
45 43
46 -
async function sendPushNotification() {
47 -
const expoPushToken = await registerForPushNotifications();
48 -
49 -
if (expoPushToken) {
50 -
const message = {
51 -
to: expoPushToken,
52 -
sound: "default",
53 -
title: "Oi meu querido",
54 -
body: "Vamo escutar um The Doors",
55 -
data: { screen: "notifications" },
56 -
};
57 -
58 -
await fetch("https://exp.host/--/api/v2/push/send", {
59 -
method: "POST",
60 -
headers: {
61 -
Accept: "application/json",
62 -
"Accept-encoding": "gzip, deflate",
63 -
"Content-Type": "application/json",
64 -
},
65 -
body: JSON.stringify(message),
66 -
});
67 -
}
68 -
}
69 -
70 44
return (
71 45
<SafeAreaView style={styles.container}>
72 46
<StatusBar style={"dark"} />
73 47
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
74 48
<ScrollView
87 61
<Card style={{ padding: Spacing.cardGap }}>
88 62
<Text>Nenhuma notificação recebida.</Text>
89 63
</Card>
90 64
)}
91 65
</Section>
92 -
93 -
<Button title="Quer ver uma coisa?" onPress={sendPushNotification} />
94 66
</ScrollView>
95 67
</KeyboardAvoidingView>
96 68
</SafeAreaView>
97 69
);
98 70
}
114 86
header: {
115 87
alignItems: "flex-start",
116 88
marginBottom: 40,
117 89
},
118 90
});