Skip to content

Commit 2bb369b

Browse files
committed
Merge branch 'main' into bu-f-er
2 parents 3367e6c + 0424905 commit 2bb369b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+810
-281
lines changed

functions/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"engines": {
1515
"node": "20"
1616
},
17-
"main": "lib/index.js",
17+
"main": "lib/functions/src/index.js",
1818
"dependencies": {
1919
"@fastify/auth": "^4.4.0",
2020
"@fastify/cors": "^8.4.2",

functions/src/api/apiKeyPlugin.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,37 @@ import fastifyPlugin from 'fastify-plugin'
22
import { EventDao } from './dao/eventDao'
33
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
44
import { FastifyAuthFunction } from '@fastify/auth'
5-
5+
import { Static, Type } from '@sinclair/typebox'
66
const verifyRequest = async (fastify: FastifyInstance, request: FastifyRequest, reply: FastifyReply) => {
77
// @ts-ignore
88
const eventId = request.params?.eventId
99
// @ts-ignore
1010
const apiKey = request.query?.apiKey
1111

1212
if (!eventId || eventId.length === 0) {
13-
reply.code(400).send({ error: "Bad Request! Missing eventId, hophop let's get to work !" })
13+
reply.code(400).send({ error: "Bad Request! Missing eventId, hophop let's get to work !", success: false })
1414
return
1515
}
1616

1717
if (!apiKey || apiKey.length === 0) {
18-
reply.code(401).send({ error: 'Unauthorized! Du balai !' })
18+
reply.code(401).send({ error: 'Unauthorized! Du balai !', success: false })
1919
return
2020
}
2121

2222
const event = await EventDao.getEvent(fastify.firebase, eventId)
2323

2424
if (event.apiKey !== apiKey) {
25-
reply.code(401).send({ error: 'Unauthorized! Du balai !' })
25+
reply.code(401).send({ error: 'Unauthorized! Du balai !', success: false })
2626
return
2727
}
2828
}
2929

30+
export const Error400_401_VerifyRequest = Type.Object({
31+
success: Type.Boolean(),
32+
error: Type.String(),
33+
})
34+
export type Error400_401_VerifyRequestType = Static<typeof Error400_401_VerifyRequest>
35+
3036
export const apiKeyPlugin = fastifyPlugin(
3137
(fastify: FastifyInstance, options: any, next: () => void) => {
3238
fastify.decorate<FastifyAuthFunction>('verifyApiKey', async (request: FastifyRequest, reply: FastifyReply) => {

functions/src/api/dao/faqDao.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import firebase from 'firebase-admin'
22
import { v4 as uuidv4 } from 'uuid'
33
import { FaqCategoryType, FaqType } from '../routes/faq/faq'
4+
import { Faq, FaqCategory } from '../../../../src/types'
45
import { NotFoundError } from '../other/Errors'
6+
import { dateToString } from '../other/dateConverter'
57

68
const { FieldValue } = firebase.firestore
79

@@ -87,4 +89,39 @@ export class FaqDao {
8789
} as FaqType)
8890
)
8991
}
92+
93+
public static async getFullFaqs(firebaseApp: firebase.app.App, eventId: string): Promise<FaqCategory[]> {
94+
const db = firebaseApp.firestore()
95+
96+
const snapshots = await db.collection(`events/${eventId}/faq`).get()
97+
98+
const faqCategory: FaqCategory[] = snapshots.docs.map((snapshot) => ({
99+
...(snapshot.data() as FaqCategory),
100+
id: snapshot.id,
101+
}))
102+
103+
const faqItems = await Promise.all(
104+
faqCategory.map((category) => FaqDao.getFaqItems(firebaseApp, eventId, category.id))
105+
)
106+
107+
return faqCategory.map((category, index) => ({
108+
...category,
109+
items: faqItems[index] || [],
110+
}))
111+
}
112+
113+
private static async getFaqItems(firebaseApp: firebase.app.App, eventId: string, faqId: string): Promise<Faq[]> {
114+
const db = firebaseApp.firestore()
115+
const snapshots = await db.collection(`events/${eventId}/faq/${faqId}/items`).get()
116+
return snapshots.docs
117+
.map((snapshot) => ({
118+
...(snapshot.data() as Faq),
119+
id: snapshot.id,
120+
}))
121+
.map((faq) => ({
122+
...faq,
123+
updatedAt: dateToString(faq.updatedAt),
124+
createdAt: dateToString(faq.createdAt),
125+
}))
126+
}
90127
}

functions/src/api/dao/sessionDao.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
import firebase from 'firebase-admin'
22
import { Session } from '../../types'
3-
3+
import { Session as SessionConverted } from '../../../../src/types'
4+
import { unknownToDateTime } from '../other/dateConverter'
45
const { FieldValue } = firebase.firestore
56

67
export class SessionDao {
8+
public static async getSessions(firebaseApp: firebase.app.App, eventId: string): Promise<SessionConverted[]> {
9+
const db = firebaseApp.firestore()
10+
const snapshot = await db.collection(`events/${eventId}/sessions`).get()
11+
return (
12+
snapshot.docs.map((doc) => ({
13+
id: doc.id,
14+
...doc.data(),
15+
})) as Session[]
16+
).map((session) => ({
17+
...session,
18+
dates: session.dates
19+
? {
20+
start: unknownToDateTime(session.dates.start),
21+
end: unknownToDateTime(session.dates.end),
22+
}
23+
: null,
24+
}))
25+
}
26+
727
public static async doesSessionExist(
828
firebaseApp: firebase.app.App,
929
eventId: string,

functions/src/api/dao/speakerDao.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ import firebase from 'firebase-admin'
44
const { FieldValue } = firebase.firestore
55

66
export class SpeakerDao {
7+
public static async getSpeakers(firebaseApp: firebase.app.App, eventId: string): Promise<Speaker[]> {
8+
const db = firebaseApp.firestore()
9+
const snapshot = await db.collection(`events/${eventId}/speakers`).get()
10+
return snapshot.docs.map((doc) => ({
11+
id: doc.id,
12+
...doc.data(),
13+
})) as Speaker[]
14+
}
15+
716
public static async doesSpeakerExist(
817
firebaseApp: firebase.app.App,
918
eventId: string,

functions/src/api/dao/sponsorDao.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import firebase from 'firebase-admin'
22
import { SponsorType } from '../routes/sponsors/sponsors'
33
import { SponsorResponse } from '../../types'
44
import { v4 as uuidv4 } from 'uuid'
5+
import { SponsorCategory } from '../../../../src/types'
56

67
const { FieldValue } = firebase.firestore
78

@@ -54,4 +55,13 @@ export class SponsorDao {
5455
const sponsors = snapshot.data()?.sponsors as SponsorResponse[]
5556
return sponsors.find((sponsor) => sponsor.id === sponsorId) as SponsorResponse
5657
}
58+
59+
public static async getSponsors(firebaseApp: firebase.app.App, eventId: string): Promise<SponsorCategory[]> {
60+
const db = firebaseApp.firestore()
61+
const snapshot = await db.collection(`events/${eventId}/sponsors`).get()
62+
return snapshot.docs.map((doc) => ({
63+
id: doc.id,
64+
...doc.data(),
65+
})) as SponsorCategory[]
66+
}
5767
}

functions/src/api/dao/teamDao.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import firebase from 'firebase-admin'
2+
import { TeamMember } from '../../../../src/types'
3+
4+
export class TeamDao {
5+
public static async getTeams(
6+
firebaseApp: firebase.app.App,
7+
eventId: string
8+
): Promise<{
9+
team: TeamMember[]
10+
teams: { id: string; members: TeamMember[]; order: number }[]
11+
}> {
12+
const db = firebaseApp.firestore()
13+
const snapshot = await db.collection(`events/${eventId}/team`).get()
14+
const members = snapshot.docs.map((doc) => ({
15+
id: doc.id,
16+
...doc.data(),
17+
})) as TeamMember[]
18+
19+
// Group members by team
20+
const teamGroups = members.reduce((acc, member) => {
21+
const teamId = member.team || 'default'
22+
if (!acc[teamId]) {
23+
acc[teamId] = {
24+
id: teamId,
25+
members: [],
26+
order: member.teamOrder || 0, // Default high order for unspecified
27+
}
28+
}
29+
acc[teamId].members.push(member)
30+
return acc
31+
}, {} as Record<string, { id: string; members: TeamMember[]; order: number }>)
32+
33+
const teamOrdered = Object.values(teamGroups)
34+
.map((team) => ({
35+
id: team.id,
36+
order: team.order,
37+
members: [...team.members].sort((a, b) => (a.order || 999) - (b.order || 999)),
38+
}))
39+
.sort((a, b) => a.order - b.order)
40+
41+
// Sort teams by teamOrder and flatten the structure
42+
return {
43+
team: members,
44+
teams: teamOrdered,
45+
}
46+
}
47+
}

functions/src/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { setupFastify } from './setupFastify'
44

55
export const fastify = setupFastify()
66

7-
export const fastifyFunction = onRequest({ timeoutSeconds: 300 }, async (request, reply) => {
7+
export const fastifyFunction = onRequest({ timeoutSeconds: 300, region: 'europe-west1' }, async (request, reply) => {
88
fastify.ready((error) => {
99
if (error) throw error
1010
fastify.server.emit('request', request, reply)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Timestamp } from 'firebase/firestore'
2+
import { DateTime } from 'luxon'
3+
4+
export const dateToString = (date: Timestamp | Date | DateTime | string | null | undefined): string => {
5+
if (!date) {
6+
return ''
7+
}
8+
return unknownToDateTime(date).toISO() || ''
9+
}
10+
11+
export const unknownToDateTime = (date: Timestamp | Date | DateTime | string | null | undefined): DateTime => {
12+
if (!date) {
13+
return DateTime.now()
14+
}
15+
if (typeof date === 'string') {
16+
return DateTime.fromISO(date)
17+
}
18+
if (date instanceof Date) {
19+
return DateTime.fromJSDate(date)
20+
}
21+
if ('toISO' in date) {
22+
// Already a DateTime object
23+
return date as DateTime
24+
}
25+
// Firestore Timestamp
26+
return DateTime.fromJSDate(date.toDate())
27+
}

functions/src/api/other/fastifyErrorHandler.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@ export const fastifyErrorHandler = (
1515
console.error(request.originalUrl, error)
1616
if (error instanceof HttpError) {
1717
reply.header('content-type', 'application/json')
18-
reply.status(error.statusCode).send(JSON.stringify({ error: error.message }))
18+
reply.status(error.statusCode).send(JSON.stringify({ error: error.message, success: false }))
1919
} else {
2020
reply.header('content-type', 'application/json')
2121
if (error.code && error.statusCode) {
2222
reply.status(error.statusCode).send(
2323
JSON.stringify({
2424
error: error.code,
2525
reason: error.toString(),
26+
success: false,
2627
})
2728
)
2829
} else {
29-
reply.status(400).send(JSON.stringify({ error: error, reason: error.message || JSON.stringify(error) }))
30+
reply.status(400).send(
31+
JSON.stringify({
32+
error: error,
33+
reason: error.message || JSON.stringify(error),
34+
success: false,
35+
})
36+
)
3037
}
3138
}
3239
}

0 commit comments

Comments
 (0)