Skip to content

Commit e51f182

Browse files
committed
Convert teams api routes.
1 parent 7d5556a commit e51f182

File tree

15 files changed

+354
-4
lines changed

15 files changed

+354
-4
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { z } from 'zod';
2+
import { unauthorized, json, badRequest, notFound, ok } from 'lib/response';
3+
import { canDeleteTeam, canUpdateTeam, canViewTeam, checkAuth } from 'lib/auth';
4+
import { checkRequest } from 'lib/request';
5+
import { deleteTeam, getTeam, updateTeam } from 'queries';
6+
7+
export async function GET(request: Request, { params }: { params: Promise<{ teamId: string }> }) {
8+
const schema = z.object({
9+
teamId: z.string().uuid(),
10+
});
11+
12+
const { error } = await checkRequest(request, schema);
13+
14+
if (error) {
15+
return badRequest(error);
16+
}
17+
18+
const { teamId } = await params;
19+
20+
const auth = await checkAuth(request);
21+
22+
if (!auth || !(await canViewTeam(auth, teamId))) {
23+
return unauthorized();
24+
}
25+
26+
const team = await getTeam(teamId, { includeMembers: true });
27+
28+
if (!team) {
29+
return notFound('Team not found.');
30+
}
31+
32+
return json(team);
33+
}
34+
35+
export async function POST(request: Request, { params }: { params: Promise<{ teamId: string }> }) {
36+
const schema = z.object({
37+
name: z.string().max(50),
38+
accessCode: z.string().max(50),
39+
});
40+
41+
const { body, error } = await checkRequest(request, schema);
42+
43+
if (error) {
44+
return badRequest(error);
45+
}
46+
47+
const { teamId } = await params;
48+
49+
const auth = await checkAuth(request);
50+
51+
if (!auth || !(await canUpdateTeam(auth, teamId))) {
52+
return unauthorized('You must be the owner of this team.');
53+
}
54+
55+
const team = await updateTeam(teamId, body);
56+
57+
return json(team);
58+
}
59+
60+
export async function DELETE(
61+
request: Request,
62+
{ params }: { params: Promise<{ teamId: string }> },
63+
) {
64+
const { teamId } = await params;
65+
66+
const auth = await checkAuth(request);
67+
68+
if (!auth || !(await canDeleteTeam(auth, teamId))) {
69+
return unauthorized('You must be the owner of this team.');
70+
}
71+
72+
await deleteTeam(teamId);
73+
74+
return ok();
75+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { z } from 'zod';
2+
import { unauthorized, json, badRequest, ok } from 'lib/response';
3+
import { canDeleteTeam, canUpdateTeam, checkAuth } from 'lib/auth';
4+
import { checkRequest } from 'lib/request';
5+
import { deleteTeam, getTeamUser, updateTeamUser } from 'queries';
6+
7+
export async function GET(
8+
request: Request,
9+
{ params }: { params: Promise<{ teamId: string; userId: string }> },
10+
) {
11+
const { teamId, userId } = await params;
12+
13+
const auth = await checkAuth(request);
14+
15+
if (!(await canUpdateTeam(auth, teamId))) {
16+
return unauthorized('You must be the owner of this team.');
17+
}
18+
19+
const teamUser = await getTeamUser(teamId, userId);
20+
21+
return json(teamUser);
22+
}
23+
24+
export async function POST(
25+
request: Request,
26+
{ params }: { params: Promise<{ teamId: string; userId: string }> },
27+
) {
28+
const schema = z.object({
29+
role: z.string().regex(/team-member|team-view-only|team-manager/),
30+
});
31+
32+
const { body, error } = await checkRequest(request, schema);
33+
34+
if (error) {
35+
return badRequest(error);
36+
}
37+
38+
const { teamId, userId } = await params;
39+
40+
const auth = await checkAuth(request);
41+
42+
if (!(await canUpdateTeam(auth, teamId))) {
43+
return unauthorized('You must be the owner of this team.');
44+
}
45+
46+
const teamUser = await getTeamUser(teamId, userId);
47+
48+
if (!teamUser) {
49+
return badRequest('The User does not exists on this team.');
50+
}
51+
52+
const user = await updateTeamUser(teamUser.id, body);
53+
54+
return json(user);
55+
}
56+
57+
export async function DELETE(
58+
request: Request,
59+
{ params }: { params: Promise<{ teamId: string }> },
60+
) {
61+
const { teamId } = await params;
62+
63+
const auth = await checkAuth(request);
64+
65+
if (!auth || !(await canDeleteTeam(auth, teamId))) {
66+
return unauthorized('You must be the owner of this team.');
67+
}
68+
69+
await deleteTeam(teamId);
70+
71+
return ok();
72+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { z } from 'zod';
2+
import { unauthorized, json, badRequest } from 'lib/response';
3+
import { canAddUserToTeam, canUpdateTeam, checkAuth } from 'lib/auth';
4+
import { checkRequest } from 'lib/request';
5+
import { pagingParams, roleParam } from 'lib/schema';
6+
import { createTeamUser, getTeamUser, getTeamUsers } from 'queries';
7+
8+
export async function GET(request: Request, { params }: { params: Promise<{ teamId: string }> }) {
9+
const schema = z.object({
10+
...pagingParams,
11+
});
12+
13+
const { query, error } = await checkRequest(request, schema);
14+
15+
if (error) {
16+
return badRequest(error);
17+
}
18+
19+
const { teamId } = await params;
20+
21+
const auth = await checkAuth(request);
22+
23+
if (!(await canUpdateTeam(auth, teamId))) {
24+
return unauthorized('You must be the owner of this team.');
25+
}
26+
27+
const users = await getTeamUsers(
28+
{
29+
where: {
30+
teamId,
31+
user: {
32+
deletedAt: null,
33+
},
34+
},
35+
include: {
36+
user: {
37+
select: {
38+
id: true,
39+
username: true,
40+
},
41+
},
42+
},
43+
},
44+
query,
45+
);
46+
47+
return json(users);
48+
}
49+
50+
export async function POST(
51+
request: Request,
52+
{ params }: { params: Promise<{ teamId: string; userId: string }> },
53+
) {
54+
const schema = z.object({
55+
role: roleParam,
56+
});
57+
58+
const { body, error } = await checkRequest(request, schema);
59+
60+
if (error) {
61+
return badRequest(error);
62+
}
63+
64+
const { teamId } = await params;
65+
66+
const auth = await checkAuth(request);
67+
68+
if (!auth || !(await canAddUserToTeam(auth))) {
69+
return unauthorized();
70+
}
71+
72+
const { userId, role } = body;
73+
74+
const teamUser = await getTeamUser(teamId, userId);
75+
76+
if (teamUser) {
77+
return badRequest('User is already a member of the Team.');
78+
}
79+
80+
const users = await createTeamUser(userId, teamId, role);
81+
82+
return json(users);
83+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { z } from 'zod';
2+
import { unauthorized, json, badRequest } from 'lib/response';
3+
import { canViewTeam, checkAuth } from 'lib/auth';
4+
import { checkRequest } from 'lib/request';
5+
import { pagingParams } from 'lib/schema';
6+
import { getTeamWebsites } from 'queries';
7+
8+
export async function GET(request: Request, { params }: { params: Promise<{ teamId: string }> }) {
9+
const schema = z.object({
10+
...pagingParams,
11+
});
12+
13+
const { query, error } = await checkRequest(request, schema);
14+
15+
if (error) {
16+
return badRequest(error);
17+
}
18+
19+
const { teamId } = await params;
20+
21+
const auth = await checkAuth(request);
22+
23+
if (!auth || !(await canViewTeam(auth, teamId))) {
24+
return unauthorized();
25+
}
26+
27+
const websites = await getTeamWebsites(teamId, query);
28+
29+
return json(websites);
30+
}

src/app/api/teams/join/route.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { z } from 'zod';
2+
import { unauthorized, json, badRequest, notFound } from 'lib/response';
3+
import { canCreateTeam, checkAuth } from 'lib/auth';
4+
import { checkRequest } from 'lib/request';
5+
import { ROLES } from 'lib/constants';
6+
import { createTeamUser, findTeam, getTeamUser } from 'queries';
7+
8+
export async function POST(request: Request) {
9+
const schema = z.object({
10+
accessCode: z.string().max(50),
11+
});
12+
13+
const { body, error } = await checkRequest(request, schema);
14+
15+
if (error) {
16+
return badRequest(error);
17+
}
18+
19+
const auth = await checkAuth(request);
20+
21+
if (!auth || !(await canCreateTeam(auth))) {
22+
return unauthorized();
23+
}
24+
25+
const { accessCode } = body;
26+
27+
const team = await findTeam({
28+
where: {
29+
accessCode,
30+
},
31+
});
32+
33+
if (!team) {
34+
return notFound('Team not found.');
35+
}
36+
37+
const teamUser = await getTeamUser(team.id, auth.user.id);
38+
39+
if (teamUser) {
40+
return badRequest('User is already a team member.');
41+
}
42+
43+
const user = await createTeamUser(auth.user.id, team.id, ROLES.teamMember);
44+
45+
return json(user);
46+
}

src/app/api/teams/route.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { z } from 'zod';
2+
import { getRandomChars } from 'next-basics';
3+
import { unauthorized, json, badRequest } from 'lib/response';
4+
import { canCreateTeam, checkAuth } from 'lib/auth';
5+
import { uuid } from 'lib/crypto';
6+
import { checkRequest } from 'lib/request';
7+
import { createTeam } from 'queries';
8+
9+
export async function POST(request: Request) {
10+
const schema = z.object({
11+
name: z.string().max(50),
12+
});
13+
14+
const { body, error } = await checkRequest(request, schema);
15+
16+
if (error) {
17+
return badRequest(error);
18+
}
19+
20+
const auth = await checkAuth(request);
21+
22+
if (!auth || !(await canCreateTeam(auth))) {
23+
return unauthorized();
24+
}
25+
26+
const { name } = body;
27+
28+
const team = await createTeam(
29+
{
30+
id: uuid(),
31+
name,
32+
accessCode: `team_${getRandomChars(16)}`,
33+
},
34+
auth.user.userId,
35+
);
36+
37+
return json(team);
38+
}

src/components/hooks/queries/useWebsitePageviews.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UseQueryOptions } from '@tanstack/react-query';
22
import { useApi } from '../useApi';
3-
import { useFilterParams } from '..//useFilterParams';
3+
import { useFilterParams } from '../useFilterParams';
44

55
export function useWebsitePageviews(
66
websiteId: string,

src/lib/response.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ export function badRequest(message?: any) {
1212
return Response.json({ error: 'Bad request', message }, { status: 400 });
1313
}
1414

15-
export function unauthorized() {
16-
return Response.json({ error: 'Unauthorized' }, { status: 401 });
15+
export function notFound(message?: any) {
16+
return Response.json({ error: 'Not found', message, status: 404 });
1717
}
1818

19-
export function serverError(error: any) {
19+
export function unauthorized(message?: any) {
20+
return Response.json({ error: 'Unauthorized', message }, { status: 401 });
21+
}
22+
23+
export function serverError(error?: any) {
2024
return Response.json({ error: 'Server error', message: serializeError(error), status: 500 });
2125
}

src/lib/schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export const unitParam = z.string().refine(value => UNIT_TYPES.includes(value),
3030
message: 'Invalid unit',
3131
});
3232

33+
export const roleParam = z.string().regex(/team-member|team-view-only|team-manager/);
34+
3335
export const filterParams = {
3436
url: z.string().optional(),
3537
referrer: z.string().optional(),

0 commit comments

Comments
 (0)