WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/actions/organization/delete-space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function deleteSpace(
prefix: `organizations/${user.activeOrganizationId}/spaces/${spaceId}/`,
});

if (listedObjects.Contents?.length) {
if (listedObjects.Contents) {
yield* bucket.deleteObjects(
listedObjects.Contents.map((content) => ({
Key: content.Key,
Expand Down
8 changes: 2 additions & 6 deletions apps/web/app/api/desktop/[...route]/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ import { Effect, Option } from "effect";
import { Hono } from "hono";
import { z } from "zod";
import { runPromise } from "@/lib/server";
import {
isAtLeastSemver,
isFromDesktopSemver,
UPLOAD_PROGRESS_VERSION,
} from "@/utils/desktop";
import { isFromDesktopSemver, UPLOAD_PROGRESS_VERSION } from "@/utils/desktop";
import { stringOrNumberOptional } from "@/utils/zod";
import { withAuth } from "../../utils";

Expand Down Expand Up @@ -295,7 +291,7 @@ app.delete(
prefix: `${user.id}/${videoId}/`,
});

if (listedObjects.Contents?.length)
if (listedObjects.Contents)
yield* bucket.deleteObjects(
listedObjects.Contents.map((content: any) => ({
Key: content.Key,
Expand Down
16 changes: 9 additions & 7 deletions apps/web/app/api/playlist/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ const ApiLive = HttpApiBuilder.api(Api).pipe(

return handlers.handle("getVideoSrc", ({ urlParams }) =>
Effect.gen(function* () {
const [video] = yield* videos.getById(urlParams.videoId).pipe(
Effect.flatten,
Effect.catchTag(
"NoSuchElementException",
() => new HttpApiError.NotFound(),
),
);
const [video] = yield* videos
.getByIdForViewing(urlParams.videoId)
.pipe(
Effect.flatten,
Effect.catchTag(
"NoSuchElementException",
() => new HttpApiError.NotFound(),
),
);

return yield* getPlaylistResponse(video, urlParams);
}).pipe(
Expand Down
44 changes: 28 additions & 16 deletions apps/web/app/api/upload/[...route]/multipart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
CloudFrontClient,
CreateInvalidationCommand,
} from "@aws-sdk/client-cloudfront";
import { db, updateIfDefined } from "@cap/database";
import { updateIfDefined } from "@cap/database";
import * as Db from "@cap/database/schema";
import { serverEnv } from "@cap/env";
import {
Expand All @@ -11,8 +11,10 @@ import {
provideOptionalAuth,
S3Buckets,
Videos,
VideosPolicy,
VideosRepo,
} from "@cap/web-backend";
import { Video } from "@cap/web-domain";
import { CurrentUser, Policy, Video } from "@cap/web-domain";
import { zValidator } from "@hono/zod-validator";
import { and, eq } from "drizzle-orm";
import { Effect, Option, Schedule } from "effect";
Expand Down Expand Up @@ -47,14 +49,18 @@ app.post(
});

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

const resp = await Effect.gen(function* () {
const videos = yield* Videos;
const repo = yield* VideosRepo;
const policy = yield* VideosPolicy;
const db = yield* Database;

const video = yield* videos.getById(Video.VideoId.make(videoId));
const video = yield* repo
.getById(videoId)
.pipe(Policy.withPolicy(policy.isOwner(videoId)));
if (Option.isNone(video)) return yield* new Video.NotFoundError();

yield* db.use((db) =>
Expand All @@ -74,6 +80,7 @@ app.post(
c.json({ error: "Error initiating multipart upload" }, 500),
);
}),
Effect.provideService(CurrentUser, user),
runPromise,
);
if (resp) return resp;
Expand Down Expand Up @@ -230,24 +237,28 @@ app.post(
]),
),
),
(c) =>
Effect.gen(function* () {
const videos = yield* Videos;
const db = yield* Database;
(c) => {
const { uploadId, parts, ...body } = c.req.valid("json");
const user = c.get("user");

const { uploadId, parts, ...body } = c.req.valid("json");
const user = c.get("user");
return Effect.gen(function* () {
const repo = yield* VideosRepo;
const policy = yield* VideosPolicy;
const db = yield* Database;

const fileKey = parseVideoIdOrFileKey(user.id, {
...body,
subpath: "result.mp4",
});

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

const maybeVideo = yield* videos.getById(Video.VideoId.make(videoId));
const maybeVideo = yield* repo
.getById(videoId)
.pipe(Policy.withPolicy(policy.isOwner(videoId)));
if (Option.isNone(maybeVideo)) {
c.status(404);
return c.text(`Video '${encodeURIComponent(videoId)}' not found`);
Expand Down Expand Up @@ -467,5 +478,6 @@ app.post(
);
}),
);
}).pipe(provideOptionalAuth, runPromise),
}).pipe(Effect.provideService(CurrentUser, user), runPromise);
},
);
2 changes: 1 addition & 1 deletion apps/web/app/embed/[videoId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function generateMetadata(
const params = await props.params;
const videoId = params.videoId as Video.VideoId;

return Effect.flatMap(Videos, (v) => v.getById(videoId)).pipe(
return Effect.flatMap(Videos, (v) => v.getByIdForViewing(videoId)).pipe(
Effect.map(
Option.match({
onNone: () => notFound(),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/s/[videoId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export async function generateMetadata(
referrer.includes(domain),
);

return Effect.flatMap(Videos, (v) => v.getById(videoId)).pipe(
return Effect.flatMap(Videos, (v) => v.getByIdForViewing(videoId)).pipe(
Effect.map(
Option.match({
onNone: () => notFound(),
Expand Down
5 changes: 3 additions & 2 deletions apps/web/lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
SpacesPolicy,
Videos,
VideosPolicy,
VideosRepo,
Workflows,
} from "@cap/web-backend";
import { type HttpAuthMiddleware, Video } from "@cap/web-domain";
Expand All @@ -21,11 +22,10 @@ import {
Headers,
type HttpApi,
HttpApiBuilder,
HttpApiClient,
HttpMiddleware,
HttpServer,
} from "@effect/platform";
import { RpcClient, RpcMessage, RpcMiddleware } from "@effect/rpc";
import { RpcClient, RpcMiddleware } from "@effect/rpc";
import {
Cause,
Config,
Expand Down Expand Up @@ -100,6 +100,7 @@ export const Dependencies = Layer.mergeAll(
S3Buckets.Default,
Videos.Default,
VideosPolicy.Default,
VideosRepo.Default,
Folders.Default,
SpacesPolicy.Default,
OrganisationsPolicy.Default,
Expand Down
3 changes: 2 additions & 1 deletion packages/web-backend/src/S3Buckets/S3BucketAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export const createS3BucketAccess = Effect.gen(function* () {
),
),
),
),
).pipe(Effect.when(() => objects.length > 0)),
getPresignedPutUrl: (
key: string,
args?: Omit<S3.PutObjectRequest, "Key" | "Bucket">,
Expand Down Expand Up @@ -256,6 +256,7 @@ export const createS3BucketAccess = Effect.gen(function* () {
UploadId: uploadId,
PartNumber: partNumber,
}),
{ expiresIn: 3600 },
),
),
),
Expand Down
8 changes: 4 additions & 4 deletions packages/web-backend/src/Videos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
const policy = yield* VideosPolicy;
const s3Buckets = yield* S3Buckets;

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

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

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

if (listedObjects.Contents?.length) {
if (listedObjects.Contents) {
yield* bucket.deleteObjects(
listedObjects.Contents.map((content) => ({
Key: content.Key,
Expand Down Expand Up @@ -199,7 +199,7 @@ export class Videos extends Effect.Service<Videos>()("Videos", {
getAnalytics: Effect.fn("Videos.getAnalytics")(function* (
videoId: Video.VideoId,
) {
const [video] = yield* getById(videoId).pipe(
const [video] = yield* getByIdForViewing(videoId).pipe(
Effect.flatten,
Effect.catchTag(
"NoSuchElementException",
Expand Down
1 change: 1 addition & 0 deletions packages/web-backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export { Spaces } from "./Spaces/index.ts";
export { SpacesPolicy } from "./Spaces/SpacesPolicy.ts";
export { Videos } from "./Videos/index.ts";
export { VideosPolicy } from "./Videos/VideosPolicy.ts";
export { VideosRepo } from "./Videos/VideosRepo.ts";
export * as Workflows from "./Workflows.ts";