Skip to content

Commit 72e04d8

Browse files
authored
Merge pull request #108 from Lycoon/dev
added middleware for authentication, fixed macos deploymenet
2 parents 4f032b8 + 5ad1c68 commit 72e04d8

File tree

17 files changed

+162
-292
lines changed

17 files changed

+162
-292
lines changed

.github/workflows/deploy-macos.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Download provisioning profile
2323
uses: apple-actions/download-provisioning-profiles@v3
2424
with:
25-
bundle-id: "ArkoLogic.Scriptio"
25+
bundle-id: "app.scriptio"
2626
profile-type: "MAC_APP_STORE"
2727
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
2828
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}

components/home/HomeClient.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,22 @@
11
"use client";
22

33
import { useEffect } from "react";
4-
import { useCookieUser } from "@src/lib/utils/hooks";
54
import HomePageContainer from "@components/home/HomePageContainer";
65
import Loading from "@components/utils/Loading";
76
import LandingPageNavbar from "@components/navbar/LandingPageNavbar";
87
import { isTauri } from "@tauri-apps/api/core";
98
import { useTheme } from "next-themes";
10-
import { useRouter } from "next/navigation";
119

1210
export default function HomeClient() {
13-
const { user, isLoading } = useCookieUser();
1411
const { setTheme } = useTheme();
15-
const router = useRouter();
1612

1713
useEffect(() => {
18-
if (!isLoading && !user && !isTauri()) {
14+
if (!isTauri()) {
1915
setTheme("dark");
2016
}
21-
}, [user, isLoading, setTheme]);
17+
}, [setTheme]);
2218

23-
useEffect(() => {
24-
if (!isLoading && (user || isTauri())) {
25-
router.replace("/projects");
26-
}
27-
}, [user, isLoading, router]);
28-
29-
if (isLoading || user || isTauri()) {
19+
if (isTauri()) {
3020
return <Loading />;
3121
}
3222

src/app/api/projects/[projectId]/cloud-token/route.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { getCookieUser } from "@src/lib/session";
2-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
3-
import { ForbiddenError, Success, UnauthorizedError, validate } from "@src/lib/utils/api-utils";
1+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
2+
import { ForbiddenError, Success, validate } from "@src/lib/utils/api-utils";
43

54
import * as ProjectService from "@src/server/service/project-service";
65

@@ -12,12 +11,7 @@ const QuerySchema = z.object({
1211
projectId: z.string(),
1312
});
1413

15-
async function projectCloudTokenRoute(req: NextRequest, { routeParams }: ApiContext) {
16-
const user = await getCookieUser();
17-
if (!user || !user.id) {
18-
throw new UnauthorizedError();
19-
}
20-
14+
async function projectCloudTokenRoute(req: NextRequest, { routeParams, user }: AuthApiContext) {
2115
const { projectId } = validate(QuerySchema, routeParams);
2216
const member = await ProjectService.getMembership(projectId, user.id);
2317
if (!member) {

src/app/api/projects/[projectId]/invite/route.ts

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { getCookieUser } from "@src/lib/session";
2-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
1+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
32
import { ProjectRole } from "@prisma/client";
43
import {
54
ForbiddenError,
@@ -8,7 +7,6 @@ import {
87
Success,
98
SuccessCreated,
109
SuccessNoContent,
11-
UnauthorizedError,
1210
validate,
1311
} from "@src/lib/utils/api-utils";
1412
import { requirePro } from "@src/lib/utils/pro-utils";
@@ -34,12 +32,7 @@ const QuerySchema = z.object({
3432
*
3533
* Returns the list of pending invites for this project
3634
*/
37-
async function getInvites(req: NextRequest, { routeParams }: ApiContext) {
38-
const cookie = await getCookieUser();
39-
if (!cookie || !cookie.id) {
40-
throw new UnauthorizedError();
41-
}
42-
35+
async function getInvites(req: NextRequest, { routeParams }: AuthApiContext) {
4336
const { projectId } = validate(QuerySchema, routeParams);
4437
const invites = await ProjectService.getInvites(projectId);
4538
return Success(invites);
@@ -50,25 +43,20 @@ async function getInvites(req: NextRequest, { routeParams }: ApiContext) {
5043
*
5144
* Invites a given user to a project, creating a pending `ProjectInvitation`
5245
*/
53-
async function inviteMember(req: NextRequest, { routeParams }: ApiContext) {
54-
const cookie = await getCookieUser();
55-
if (!cookie || !cookie.id) {
56-
throw new UnauthorizedError();
57-
}
58-
46+
async function inviteMember(req: NextRequest, { routeParams, user }: AuthApiContext) {
5947
const body = await req.json();
6048
const { email: emailToInvite } = validate(ProjectMemberEmailBodySchema, body);
6149
const { projectId } = validate(QuerySchema, routeParams);
6250

63-
const member = await ProjectService.getMembership(projectId, cookie.id);
51+
const member = await ProjectService.getMembership(projectId, user.id);
6452
if (!member) {
6553
throw new NotFoundError();
6654
}
6755
if (!Roles.hasRoleOrGreater(member.role, ProjectRole.ADMIN)) {
6856
throw new ForbiddenError("Only admin members can issue invites");
6957
}
7058

71-
await requirePro(cookie.id);
59+
await requirePro(user.id);
7260

7361
const invites = await ProjectService.getInvites(projectId);
7462
const isAlreadyInvited = invites.some((i) => i.email === emailToInvite);
@@ -98,17 +86,12 @@ async function inviteMember(req: NextRequest, { routeParams }: ApiContext) {
9886
*
9987
* Deletes the invite associated to a given email address, removing its pending `ProjectInvitation`
10088
*/
101-
async function deleteInvite(req: NextRequest, { routeParams }: ApiContext) {
102-
const cookie = await getCookieUser();
103-
if (!cookie || !cookie.id) {
104-
throw new UnauthorizedError();
105-
}
106-
89+
async function deleteInvite(req: NextRequest, { routeParams, user }: AuthApiContext) {
10790
const body = await req.json();
10891
const { email: emailToDelete } = validate(ProjectMemberEmailBodySchema, body);
10992
const { projectId } = validate(QuerySchema, routeParams);
11093

111-
const member = await ProjectService.getMembership(projectId, cookie.id);
94+
const member = await ProjectService.getMembership(projectId, user.id);
11295
if (!member) {
11396
throw new NotFoundError();
11497
}

src/app/api/projects/[projectId]/members/[userId]/route.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { ProjectRole } from "@prisma/client";
2-
import { getCookieUser } from "@src/lib/session";
3-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
2+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
43
import {
54
ForbiddenError,
65
BodyFieldError,
76
ProjectNotFoundError,
87
Success,
9-
UnauthorizedError,
108
SuccessNoContent,
119
NotFoundError,
1210
validate,
@@ -32,15 +30,9 @@ const QuerySchema = z.object({
3230
*
3331
* Returns a project member given its userId and associated projectId
3432
*/
35-
async function getProjectMember(req: NextRequest, { routeParams }: ApiContext) {
36-
// We query the user role for this poject, throw 404 in case it doesn't belong to it
37-
const cookie = await getCookieUser();
38-
if (!cookie || !cookie.id) {
39-
throw new UnauthorizedError();
40-
}
41-
33+
async function getProjectMember(req: NextRequest, { routeParams, user }: AuthApiContext) {
4234
const { projectId } = validate(QuerySchema, routeParams);
43-
const member = await ProjectService.getMembership(projectId, cookie.id);
35+
const member = await ProjectService.getMembership(projectId, user.id);
4436
if (!member) {
4537
throw new ProjectNotFoundError();
4638
}
@@ -53,23 +45,18 @@ async function getProjectMember(req: NextRequest, { routeParams }: ApiContext) {
5345
*
5446
* Updates a project member role
5547
*/
56-
async function updateProjectMemberRole(req: NextRequest, { routeParams }: ApiContext) {
57-
const cookie = await getCookieUser();
58-
if (!cookie || !cookie.id) {
59-
throw new UnauthorizedError();
60-
}
61-
48+
async function updateProjectMemberRole(req: NextRequest, { routeParams, user }: AuthApiContext) {
6249
const body = await req.json();
6350
const { role } = validate(UpdateRoleSchema, body);
6451
const { userId: userToUpdateId, projectId } = validate(QuerySchema, routeParams);
6552

66-
const isSelf = cookie.id === userToUpdateId;
53+
const isSelf = user.id === userToUpdateId;
6754
if (isSelf) throw new ForbiddenError("You cannot update your own role");
6855

6956
if (!Roles.isValid(role)) throw new BodyFieldError("Unknown role");
7057
const newRole = role as ProjectRole;
7158

72-
const member = await ProjectService.getMembership(projectId, cookie.id);
59+
const member = await ProjectService.getMembership(projectId, user.id);
7360
if (!member) {
7461
throw new ProjectNotFoundError();
7562
}
@@ -86,7 +73,10 @@ async function updateProjectMemberRole(req: NextRequest, { routeParams }: ApiCon
8673
throw new ForbiddenError("You cannot assign the same role to another user");
8774
}
8875

89-
if (!Roles.hasRoleOrGreater(member.role, newRole) || !Roles.hasRoleOrGreater(member.role, memberToUpdate.role)) {
76+
if (
77+
!Roles.hasRoleOrGreater(member.role, newRole) ||
78+
!Roles.hasRoleOrGreater(member.role, memberToUpdate.role)
79+
) {
9080
throw new ForbiddenError("User does not have sufficient permissions");
9181
}
9282

@@ -99,27 +89,19 @@ async function updateProjectMemberRole(req: NextRequest, { routeParams }: ApiCon
9989
*
10090
* Removes a member from a project. A user can leave the project itself.
10191
*/
102-
async function deleteProjectMember(req: NextRequest, { routeParams }: ApiContext) {
103-
const cookie = await getCookieUser();
104-
if (!cookie || !cookie.id) {
105-
throw new UnauthorizedError();
106-
}
107-
92+
async function deleteProjectMember(req: NextRequest, { routeParams, user }: AuthApiContext) {
10893
const { userId: userToDelete, projectId } = validate(QuerySchema, routeParams);
109-
const member = await ProjectService.getMembership(projectId, cookie.id);
94+
const member = await ProjectService.getMembership(projectId, user.id);
11095
if (!member) {
11196
throw new NotFoundError();
11297
}
11398

114-
const isSelf = cookie.id === userToDelete;
99+
const isSelf = user.id === userToDelete;
115100
if (isSelf) {
116101
if (member.role !== ProjectRole.OWNER) {
117-
// No need to check roles, any non-owner member can leave the project on its own
118102
await ProjectService.deleteProjectMember(projectId, userToDelete);
119103
return Success({ redirectUrl: "/projects" });
120104
} else {
121-
// An owner cannot leave its project as a collaborator
122-
// He either needs to transfer ownership or delete project
123105
throw new ForbiddenError("Owner cannot leave project");
124106
}
125107
}

src/app/api/projects/[projectId]/members/route.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { getCookieUser } from "@src/lib/session";
2-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
3-
import { ProjectNotFoundError, Success, UnauthorizedError, validate } from "@src/lib/utils/api-utils";
1+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
2+
import { ProjectNotFoundError, Success, validate } from "@src/lib/utils/api-utils";
43

54
import * as ProjectService from "@src/server/service/project-service";
65

@@ -16,12 +15,7 @@ const QuerySchema = z.object({
1615
*
1716
* Returns project memberships associated projectId
1817
*/
19-
async function getProjectMemberships(req: NextRequest, { routeParams }: ApiContext) {
20-
const cookie = await getCookieUser();
21-
if (!cookie || !cookie.id) {
22-
throw new UnauthorizedError();
23-
}
24-
18+
async function getProjectMemberships(req: NextRequest, { routeParams }: AuthApiContext) {
2519
const { projectId } = validate(QuerySchema, routeParams);
2620
const collaborators = await ProjectService.getCollaborators(projectId);
2721
if (!collaborators) {

src/app/api/projects/[projectId]/route.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import { getCookieUser } from "@src/lib/session";
21
import { ProjectRole } from "@prisma/client";
32

43
import * as S3 from "@src/lib/s3";
54
import * as ProjectService from "@src/server/service/project-service";
65
import * as Roles from "@src/lib/utils/roles";
7-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
6+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
87
import {
98
ForbiddenError,
109
InternalServerError,
1110
ProjectNotFoundError,
1211
Success,
13-
UnauthorizedError,
1412
BodyFieldError,
1513
validate,
1614
SuccessNoContent,
@@ -30,13 +28,9 @@ const QuerySchema = z.object({
3028
*
3129
* Gets project information from authenticated user
3230
*/
33-
async function getProject(req: NextRequest, { routeParams }: ApiContext) {
34-
const cookie = await getCookieUser();
35-
if (!cookie || !cookie.id) {
36-
throw new UnauthorizedError();
37-
}
31+
async function getProject(req: NextRequest, { routeParams, user }: AuthApiContext) {
3832
const { projectId } = validate(QuerySchema, routeParams);
39-
const membership = await ProjectService.getMembership(projectId, cookie.id);
33+
const membership = await ProjectService.getMembership(projectId, user.id);
4034

4135
if (!membership) {
4236
throw new ProjectNotFoundError();
@@ -50,14 +44,9 @@ async function getProject(req: NextRequest, { routeParams }: ApiContext) {
5044
*
5145
* Updates project information from authenticated user
5246
*/
53-
async function updateProject(req: NextRequest, { routeParams }: ApiContext) {
54-
const cookie = await getCookieUser();
55-
if (!cookie || !cookie.id) {
56-
throw new UnauthorizedError();
57-
}
58-
47+
async function updateProject(req: NextRequest, { routeParams, user }: AuthApiContext) {
5948
const { projectId } = validate(QuerySchema, routeParams);
60-
const member = await ProjectService.getMembership(projectId, cookie.id);
49+
const member = await ProjectService.getMembership(projectId, user.id);
6150

6251
if (!member) {
6352
throw new ProjectNotFoundError();
@@ -103,16 +92,11 @@ async function updateProject(req: NextRequest, { routeParams }: ApiContext) {
10392
/**
10493
* DELETE `/projects/[projectId]`
10594
*
106-
* Deletes project from unautheticated user
95+
* Deletes project from authenticated user
10796
*/
108-
async function deleteProject(req: NextRequest, { routeParams }: ApiContext) {
109-
const cookie = await getCookieUser();
110-
if (!cookie || !cookie.id) {
111-
throw new UnauthorizedError();
112-
}
113-
97+
async function deleteProject(req: NextRequest, { routeParams, user }: AuthApiContext) {
11498
const { projectId } = validate(QuerySchema, routeParams);
115-
const member = await ProjectService.getMembership(projectId, cookie.id);
99+
const member = await ProjectService.getMembership(projectId, user.id);
116100

117101
if (!member) {
118102
throw new ForbiddenError();

src/app/api/projects/[projectId]/saves/manual/route.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { ProjectRole } from "@prisma/client";
2-
import { getCookieUser } from "@src/lib/session";
3-
import { ApiContext, apiHandler } from "@src/lib/utils/api-handler";
4-
import { ForbiddenError, getCollabHttpUrl, Success, SuccessCreated, UnauthorizedError, validate } from "@src/lib/utils/api-utils";
2+
import { apiHandler, AuthApiContext } from "@src/lib/utils/api-handler";
3+
import {
4+
ForbiddenError,
5+
getCollabHttpUrl,
6+
Success,
7+
SuccessCreated,
8+
validate,
9+
} from "@src/lib/utils/api-utils";
510
import { requirePro } from "@src/lib/utils/pro-utils";
611

712
import * as Roles from "@src/lib/utils/roles";
@@ -19,7 +24,7 @@ async function forwardToWorker(
1924
projectId: string,
2025
method: string,
2126
path: string,
22-
body?: any
27+
body?: any,
2328
): Promise<Response> {
2429
const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
2530
const token = await new SignJWT({ type: "admin-action", projectId })
@@ -46,12 +51,7 @@ async function forwardToWorker(
4651
* Creates a manual save with a user-provided name.
4752
* Requires EDITOR+ role.
4853
*/
49-
async function createManualSave(req: NextRequest, { routeParams }: ApiContext) {
50-
const user = await getCookieUser();
51-
if (!user || !user.id) {
52-
throw new UnauthorizedError();
53-
}
54-
54+
async function createManualSave(req: NextRequest, { routeParams, user }: AuthApiContext) {
5555
const { projectId } = validate(QuerySchema, routeParams);
5656
const member = await ProjectService.getMembership(projectId, user.id);
5757
if (!member) {
@@ -81,12 +81,7 @@ async function createManualSave(req: NextRequest, { routeParams }: ApiContext) {
8181
*
8282
* Renames a manual save. Requires ADMIN+ role.
8383
*/
84-
async function renameManualSave(req: NextRequest, { routeParams }: ApiContext) {
85-
const user = await getCookieUser();
86-
if (!user || !user.id) {
87-
throw new UnauthorizedError();
88-
}
89-
84+
async function renameManualSave(req: NextRequest, { routeParams, user }: AuthApiContext) {
9085
const { projectId } = validate(QuerySchema, routeParams);
9186
const member = await ProjectService.getMembership(projectId, user.id);
9287
if (!member) {

0 commit comments

Comments
 (0)