88 lines
2.2 KiB
TypeScript
88 lines
2.2 KiB
TypeScript
|
import * as admin from 'firebase-admin'
|
||
|
|
||
|
import { getUser } from './utils'
|
||
|
import { Contract } from '../../common/contract'
|
||
|
import { slugify } from '../../common/util/slugify'
|
||
|
import { randomString } from '../../common/util/random'
|
||
|
import {
|
||
|
Group,
|
||
|
MAX_ABOUT_LENGTH,
|
||
|
MAX_GROUP_NAME_LENGTH,
|
||
|
MAX_ID_LENGTH,
|
||
|
} from '../../common/group'
|
||
|
import { APIError, newEndpoint, validate } from '../../functions/src/api'
|
||
|
import { z } from 'zod'
|
||
|
|
||
|
const bodySchema = z.object({
|
||
|
name: z.string().min(1).max(MAX_GROUP_NAME_LENGTH),
|
||
|
memberIds: z.array(z.string().min(1).max(MAX_ID_LENGTH)),
|
||
|
anyoneCanJoin: z.boolean(),
|
||
|
about: z.string().min(1).max(MAX_ABOUT_LENGTH).optional(),
|
||
|
})
|
||
|
|
||
|
export const creategroup = newEndpoint(['POST'], async (req, auth) => {
|
||
|
const { name, about, memberIds, anyoneCanJoin } = validate(
|
||
|
bodySchema,
|
||
|
req.body
|
||
|
)
|
||
|
|
||
|
const creator = await getUser(auth.uid)
|
||
|
if (!creator)
|
||
|
throw new APIError(400, 'No user exists with the authenticated user ID.')
|
||
|
|
||
|
// Add creator id to member ids for convenience
|
||
|
if (!memberIds.includes(creator.id)) memberIds.push(creator.id)
|
||
|
|
||
|
console.log(
|
||
|
'creating group for',
|
||
|
creator.username,
|
||
|
'named',
|
||
|
name,
|
||
|
'about',
|
||
|
about,
|
||
|
'other member ids',
|
||
|
memberIds
|
||
|
)
|
||
|
|
||
|
const slug = await getSlug(name)
|
||
|
|
||
|
const groupRef = firestore.collection('groups').doc()
|
||
|
|
||
|
const group: Group = {
|
||
|
id: groupRef.id,
|
||
|
creatorId: creator.id,
|
||
|
slug,
|
||
|
name,
|
||
|
about: about ?? '',
|
||
|
createdTime: Date.now(),
|
||
|
mostRecentActivityTime: Date.now(),
|
||
|
// TODO: allow users to add contract ids on group creation
|
||
|
contractIds: [],
|
||
|
anyoneCanJoin,
|
||
|
memberIds,
|
||
|
}
|
||
|
|
||
|
await groupRef.create(group)
|
||
|
|
||
|
return { status: 'success', group: group }
|
||
|
})
|
||
|
|
||
|
const getSlug = async (name: string) => {
|
||
|
const proposedSlug = slugify(name)
|
||
|
|
||
|
const preexistingGroup = await getGroupFromSlug(proposedSlug)
|
||
|
|
||
|
return preexistingGroup ? proposedSlug + '-' + randomString() : proposedSlug
|
||
|
}
|
||
|
|
||
|
const firestore = admin.firestore()
|
||
|
|
||
|
export async function getGroupFromSlug(slug: string) {
|
||
|
const snap = await firestore
|
||
|
.collection('groups')
|
||
|
.where('slug', '==', slug)
|
||
|
.get()
|
||
|
|
||
|
return snap.empty ? undefined : (snap.docs[0].data() as Contract)
|
||
|
}
|