Skip to content

Commit f60cd3a

Browse files
Uploader Bugs (round 2) (#1264)
* fix deleting no parts * fix * maybe fix multipart initiate * fix auth check for multipart initiate * fix auth policy * bump URL expiry * fixes * fixes * fixes * fixes * format
1 parent 2097b39 commit f60cd3a

File tree

10 files changed

+52
-39
lines changed

10 files changed

+52
-39
lines changed

apps/web/actions/organization/delete-space.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export async function deleteSpace(
7777
prefix: `organizations/${user.activeOrganizationId}/spaces/${spaceId}/`,
7878
});
7979

80-
if (listedObjects.Contents?.length) {
80+
if (listedObjects.Contents) {
8181
yield* bucket.deleteObjects(
8282
listedObjects.Contents.map((content) => ({
8383
Key: content.Key,

apps/web/app/api/desktop/[...route]/video.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ import { Effect, Option } from "effect";
2020
import { Hono } from "hono";
2121
import { z } from "zod";
2222
import { runPromise } from "@/lib/server";
23-
import {
24-
isAtLeastSemver,
25-
isFromDesktopSemver,
26-
UPLOAD_PROGRESS_VERSION,
27-
} from "@/utils/desktop";
23+
import { isFromDesktopSemver, UPLOAD_PROGRESS_VERSION } from "@/utils/desktop";
2824
import { stringOrNumberOptional } from "@/utils/zod";
2925
import { withAuth } from "../../utils";
3026

@@ -295,7 +291,7 @@ app.delete(
295291
prefix: `${user.id}/${videoId}/`,
296292
});
297293

298-
if (listedObjects.Contents?.length)
294+
if (listedObjects.Contents)
299295
yield* bucket.deleteObjects(
300296
listedObjects.Contents.map((content: any) => ({
301297
Key: content.Key,

apps/web/app/api/playlist/route.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ const ApiLive = HttpApiBuilder.api(Api).pipe(
4747

4848
return handlers.handle("getVideoSrc", ({ urlParams }) =>
4949
Effect.gen(function* () {
50-
const [video] = yield* videos.getById(urlParams.videoId).pipe(
51-
Effect.flatten,
52-
Effect.catchTag(
53-
"NoSuchElementException",
54-
() => new HttpApiError.NotFound(),
55-
),
56-
);
50+
const [video] = yield* videos
51+
.getByIdForViewing(urlParams.videoId)
52+
.pipe(
53+
Effect.flatten,
54+
Effect.catchTag(
55+
"NoSuchElementException",
56+
() => new HttpApiError.NotFound(),
57+
),
58+
);
5759

5860
return yield* getPlaylistResponse(video, urlParams);
5961
}).pipe(

apps/web/app/api/upload/[...route]/multipart.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
CloudFrontClient,
33
CreateInvalidationCommand,
44
} from "@aws-sdk/client-cloudfront";
5-
import { db, updateIfDefined } from "@cap/database";
5+
import { updateIfDefined } from "@cap/database";
66
import * as Db from "@cap/database/schema";
77
import { serverEnv } from "@cap/env";
88
import {
@@ -11,8 +11,10 @@ import {
1111
provideOptionalAuth,
1212
S3Buckets,
1313
Videos,
14+
VideosPolicy,
15+
VideosRepo,
1416
} from "@cap/web-backend";
15-
import { Video } from "@cap/web-domain";
17+
import { CurrentUser, Policy, Video } from "@cap/web-domain";
1618
import { zValidator } from "@hono/zod-validator";
1719
import { and, eq } from "drizzle-orm";
1820
import { Effect, Option, Schedule } from "effect";
@@ -47,14 +49,18 @@ app.post(
4749
});
4850

4951
const videoIdFromFileKey = fileKey.split("/")[1];
50-
const videoId = "videoId" in body ? body.videoId : videoIdFromFileKey;
51-
if (!videoId) throw new Error("Video ID is required");
52+
const videoIdRaw = "videoId" in body ? body.videoId : videoIdFromFileKey;
53+
if (!videoIdRaw) return c.text("Video id not found", 400);
54+
const videoId = Video.VideoId.make(videoIdRaw);
5255

5356
const resp = await Effect.gen(function* () {
54-
const videos = yield* Videos;
57+
const repo = yield* VideosRepo;
58+
const policy = yield* VideosPolicy;
5559
const db = yield* Database;
5660

57-
const video = yield* videos.getById(Video.VideoId.make(videoId));
61+
const video = yield* repo
62+
.getById(videoId)
63+
.pipe(Policy.withPolicy(policy.isOwner(videoId)));
5864
if (Option.isNone(video)) return yield* new Video.NotFoundError();
5965

6066
yield* db.use((db) =>
@@ -74,6 +80,7 @@ app.post(
7480
c.json({ error: "Error initiating multipart upload" }, 500),
7581
);
7682
}),
83+
Effect.provideService(CurrentUser, user),
7784
runPromise,
7885
);
7986
if (resp) return resp;
@@ -230,24 +237,28 @@ app.post(
230237
]),
231238
),
232239
),
233-
(c) =>
234-
Effect.gen(function* () {
235-
const videos = yield* Videos;
236-
const db = yield* Database;
240+
(c) => {
241+
const { uploadId, parts, ...body } = c.req.valid("json");
242+
const user = c.get("user");
237243

238-
const { uploadId, parts, ...body } = c.req.valid("json");
239-
const user = c.get("user");
244+
return Effect.gen(function* () {
245+
const repo = yield* VideosRepo;
246+
const policy = yield* VideosPolicy;
247+
const db = yield* Database;
240248

241249
const fileKey = parseVideoIdOrFileKey(user.id, {
242250
...body,
243251
subpath: "result.mp4",
244252
});
245253

246254
const videoIdFromFileKey = fileKey.split("/")[1];
247-
const videoId = "videoId" in body ? body.videoId : videoIdFromFileKey;
248-
if (!videoId) throw new Error("Video ID is required");
255+
const videoIdRaw = "videoId" in body ? body.videoId : videoIdFromFileKey;
256+
if (!videoIdRaw) return c.text("Video id not found", 400);
257+
const videoId = Video.VideoId.make(videoIdRaw);
249258

250-
const maybeVideo = yield* videos.getById(Video.VideoId.make(videoId));
259+
const maybeVideo = yield* repo
260+
.getById(videoId)
261+
.pipe(Policy.withPolicy(policy.isOwner(videoId)));
251262
if (Option.isNone(maybeVideo)) {
252263
c.status(404);
253264
return c.text(`Video '${encodeURIComponent(videoId)}' not found`);
@@ -467,5 +478,6 @@ app.post(
467478
);
468479
}),
469480
);
470-
}).pipe(provideOptionalAuth, runPromise),
481+
}).pipe(Effect.provideService(CurrentUser, user), runPromise);
482+
},
471483
);

apps/web/app/embed/[videoId]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export async function generateMetadata(
2929
const params = await props.params;
3030
const videoId = params.videoId as Video.VideoId;
3131

32-
return Effect.flatMap(Videos, (v) => v.getById(videoId)).pipe(
32+
return Effect.flatMap(Videos, (v) => v.getByIdForViewing(videoId)).pipe(
3333
Effect.map(
3434
Option.match({
3535
onNone: () => notFound(),

apps/web/app/s/[videoId]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export async function generateMetadata(
134134
referrer.includes(domain),
135135
);
136136

137-
return Effect.flatMap(Videos, (v) => v.getById(videoId)).pipe(
137+
return Effect.flatMap(Videos, (v) => v.getByIdForViewing(videoId)).pipe(
138138
Effect.map(
139139
Option.match({
140140
onNone: () => notFound(),

apps/web/lib/server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
SpacesPolicy,
1414
Videos,
1515
VideosPolicy,
16+
VideosRepo,
1617
Workflows,
1718
} from "@cap/web-backend";
1819
import { type HttpAuthMiddleware, Video } from "@cap/web-domain";
@@ -21,11 +22,10 @@ import {
2122
Headers,
2223
type HttpApi,
2324
HttpApiBuilder,
24-
HttpApiClient,
2525
HttpMiddleware,
2626
HttpServer,
2727
} from "@effect/platform";
28-
import { RpcClient, RpcMessage, RpcMiddleware } from "@effect/rpc";
28+
import { RpcClient, RpcMiddleware } from "@effect/rpc";
2929
import {
3030
Cause,
3131
Config,
@@ -100,6 +100,7 @@ export const Dependencies = Layer.mergeAll(
100100
S3Buckets.Default,
101101
Videos.Default,
102102
VideosPolicy.Default,
103+
VideosRepo.Default,
103104
Folders.Default,
104105
SpacesPolicy.Default,
105106
OrganisationsPolicy.Default,

packages/web-backend/src/S3Buckets/S3BucketAccess.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export const createS3BucketAccess = Effect.gen(function* () {
181181
),
182182
),
183183
),
184-
),
184+
).pipe(Effect.when(() => objects.length > 0)),
185185
getPresignedPutUrl: (
186186
key: string,
187187
args?: Omit<S3.PutObjectRequest, "Key" | "Bucket">,
@@ -256,6 +256,7 @@ export const createS3BucketAccess = Effect.gen(function* () {
256256
UploadId: uploadId,
257257
PartNumber: partNumber,
258258
}),
259+
{ expiresIn: 3600 },
259260
),
260261
),
261262
),

packages/web-backend/src/Videos/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
1616
const policy = yield* VideosPolicy;
1717
const s3Buckets = yield* S3Buckets;
1818

19-
const getById = (id: Video.VideoId) =>
19+
const getByIdForViewing = (id: Video.VideoId) =>
2020
repo
2121
.getById(id)
2222
.pipe(
@@ -30,7 +30,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
3030
*/
3131
// This is only for external use since it does an access check,
3232
// internal use should prefer the repo directly
33-
getById,
33+
getByIdForViewing,
3434

3535
/*
3636
* Delete a video. Will fail if the user does not have access.
@@ -56,7 +56,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
5656

5757
const listedObjects = yield* bucket.listObjects({ prefix });
5858

59-
if (listedObjects.Contents?.length) {
59+
if (listedObjects.Contents) {
6060
yield* bucket.deleteObjects(
6161
listedObjects.Contents.map((content) => ({
6262
Key: content.Key,
@@ -199,7 +199,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
199199
getAnalytics: Effect.fn("Videos.getAnalytics")(function* (
200200
videoId: Video.VideoId,
201201
) {
202-
const [video] = yield* getById(videoId).pipe(
202+
const [video] = yield* getByIdForViewing(videoId).pipe(
203203
Effect.flatten,
204204
Effect.catchTag(
205205
"NoSuchElementException",

packages/web-backend/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ export { Spaces } from "./Spaces/index.ts";
1111
export { SpacesPolicy } from "./Spaces/SpacesPolicy.ts";
1212
export { Videos } from "./Videos/index.ts";
1313
export { VideosPolicy } from "./Videos/VideosPolicy.ts";
14+
export { VideosRepo } from "./Videos/VideosRepo.ts";
1415
export * as Workflows from "./Workflows.ts";

0 commit comments

Comments
 (0)