Skip to content

Commit

Permalink
Storage usage per app; Bug fixes;
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajat Saxena committed Feb 18, 2024
1 parent 4e9a082 commit 9c49700
Show file tree
Hide file tree
Showing 13 changed files with 826 additions and 10 deletions.
660 changes: 660 additions & 0 deletions LICENSE.md

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion apps/api/lib/media/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import logger from "../services/log";
import { Request } from "express";
import mediaService from "./service";
import { getMediaCount as getCount } from "./queries";
import { getMediaCount as getCount, getTotalSpace } from "./queries";

function validateUploadOptions(req: Request): Joi.ValidationResult {
const uploadSchema = Joi.object({
Expand Down Expand Up @@ -118,6 +118,18 @@ export async function getMediaCount(req: any, res: any) {
}
}

export async function getTotalSpaceOccupied(req: any, res: any) {
const userId = req.user._id;
const apikey = req.apikey;

try {
const totalMediaFiles = await getTotalSpace({ userId, apikey });
return res.status(200).json({ count: totalMediaFiles });
} catch (err: any) {
return res.status(500).json(err.message);
}
}

export async function getMediaDetails(req: any, res: any) {
const { mediaId } = req.params;

Expand Down
37 changes: 37 additions & 0 deletions apps/api/lib/media/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,43 @@ export async function getMediaCount({
return await MediaModel.countDocuments({ apikey, userId }).lean();
}

export async function getTotalSpace({
userId,
apikey,
}: {
userId: string;
apikey: string;
}): Promise<number> {
const result = await MediaModel
// calculate sum of size of all media files
.aggregate([
{
$match: {
userId,
apikey,
},
},
{
$group: {
_id: null,
totalSize: { $sum: "$size" },
},
},
{
$project: {
_id: 0,
totalSize: 1,
},
},
]);

if (result.length === 0) {
return 0;
}

return result[0].totalSize;
}

export async function getPaginatedMedia({
userId,
apikey,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/lib/media/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
uploadMedia,
deleteMedia,
getMediaCount,
getTotalSpaceOccupied,
} from "./handlers";
import presigned from "../presigning/middleware";

Expand Down Expand Up @@ -39,6 +40,7 @@ router.post(
uploadMedia
);
router.post("/get/count", apikey, getMediaCount);
router.post("/get/size", apikey, getTotalSpaceOccupied);
router.post("/get/:mediaId", apikey, getMediaDetails);
router.post("/get", apikey, getMedia);
router.delete("/delete/:mediaId", apikey, deleteMedia);
Expand Down
1 change: 0 additions & 1 deletion apps/web/app/app/[keyid]/files/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export async function getCount(keyid: string) {

const mediacount = await getMediaCount({
apikey: apikey.key,
userId: apikey.userId,
});

return mediacount;
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/app/[keyid]/files/file-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,11 @@ export default function FilePreview({
<div className="text-sm">{media.mimeType}</div>
</div>
</form>
<div className="flex justify-center items-center">
<div className="flex justify-center items-center max-h-[200px] overflow-y-scroll">
<Image
alt="File preview"
src={media.thumbnail}
sizes="40vw"
width={200}
height={200}
/>
Expand Down
41 changes: 40 additions & 1 deletion apps/web/app/app/[keyid]/settings/actions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"use server";

import { auth } from "@/auth";
import { getApikeyFromKeyId, getInternalApikey } from "@/lib/apikey-handlers";
import connectToDatabase from "@/lib/connect-db";
import { getMediaTotalSize } from "@/lib/media-handlers";
import { getUserFromSession } from "@/lib/user-handlers";
import ApikeyModel from "@/models/apikey";
import { Apikey } from "@medialit/models";

export async function updateAppName(
previousState: Record<string, unknown>,
Expand Down Expand Up @@ -45,3 +46,41 @@ export async function updateAppName(
return { success: false, error: err.message };
}
}

export async function getTotalSpaceByApikey(keyid: string): Promise<number> {
const session = await auth();
if (!session || !session.user) {
throw new Error("Unauthenticated");
}

await connectToDatabase();

const dbUser = await getUserFromSession(session);
if (!dbUser) {
throw new Error("User not found");
}

const internalApikey = await getInternalApikey(dbUser._id);

if (!internalApikey) {
console.error("Internal apikey not found for user", dbUser._id); // eslint-disable-line no-console
throw new Error("We messed up. Please try again later.");
}
const apikey = await getApikeyFromKeyId(dbUser._id, keyid);

if (!apikey) {
throw new Error("Apikey not found");
}

try {
const response = await getMediaTotalSize({
apikey: apikey.key,
internalApikey: internalApikey.key,
});

return response.count;
} catch (e) {
console.error(e); // eslint-disable-line no-console
return 0;
}
}
13 changes: 12 additions & 1 deletion apps/web/app/app/[keyid]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import UpdateSettingsForm from "./update-settings-form";
import { getApikeyUsingKeyId } from "@/app/actions";
import DeleteAppButton from "./delete-app-button";
import { Separator } from "@/components/ui/separator";
import { getTotalSpaceByApikey } from "./actions";

export default async function Settings({
params,
Expand All @@ -27,8 +28,18 @@ export default async function Settings({
return null;
}

const totalSpaceOccupied = await getTotalSpaceByApikey(keyid);

return (
<section className="border border-muted-foreground border-slate-200 min-h-[480px] my-4 rounded p-2 flex flex-col gap-4">
<section className="border border-muted-foreground border-slate-200 min-h-[480px] my-4 rounded p-4 flex flex-col gap-4">
<div className="flex justify-between items-center">
<Label htmlFor="name" className="mb-2">
Storage
</Label>
<p className="font-semibold">
{(totalSpaceOccupied / 1024 / 1024).toFixed(2)} MB
</p>
</div>
<div>
<Label htmlFor="apikey" className="mb-2">
Apikey
Expand Down
24 changes: 19 additions & 5 deletions apps/web/lib/media-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,35 @@ export async function getMedia({
return response;
}

export async function getMediaCount({
export async function getMediaCount({ apikey }: { apikey: string }) {
let response: any = await fetch(`${medialitServer}/media/get/count`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
apikey,
}),
});
response = await response.json();
return response;
}

export async function getMediaTotalSize({
apikey,
userId,
internalApikey,
}: {
apikey: string;
userId: mongoose.Types.ObjectId;
internalApikey: string;
}) {
let response: any = await fetch(`${medialitServer}/media/get/count`, {
let response: any = await fetch(`${medialitServer}/media/get/size`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
apikey,
userId,
internalApikey,
}),
});
response = await response.json();
Expand Down
4 changes: 4 additions & 0 deletions apps/web/models/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MediaSchema } from "@medialit/models";
import mongoose from "mongoose";

export default mongoose.models?.Media || mongoose.model("Media", MediaSchema);
6 changes: 6 additions & 0 deletions docs/ownership.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ownership

MediaLit is divided into two part:

1. The core API `@medialit/api` which is responsible for managing media on a AWS S3 compatible storage.
2. The frontend app `@medialit/app` which is responsible for managing accounts on `@medialit/api`. This includes all the administrative tasks like creating apps, billing, showing statistics etc.
1 change: 1 addition & 0 deletions packages/models/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { default as UserSchema } from "./user-schema";
export { default as ApikeySchema } from "./api-key-schema";
export * as Constants from "./constants";
export { Media } from "./media";
export { default as MediaSchema } from "./media-schema";
30 changes: 30 additions & 0 deletions packages/models/src/media-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import mongoose from "mongoose";
import { Media } from "./media";

export type MediaWithUserId = Media & { userId: mongoose.Types.ObjectId };

const MediaSchema = new mongoose.Schema<MediaWithUserId>(
{
fileName: { type: String, required: true },
mediaId: { type: String, required: true },
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
apikey: { type: String, required: true },
originalFileName: { type: String, required: true },
mimeType: { type: String, required: true },
size: { type: Number, required: true },
thumbnailGenerated: { type: Boolean, required: true, default: false },
accessControl: { type: String, required: true, default: "private" },
group: { type: String },
caption: { type: String },
},
{
timestamps: true,
}
);

MediaSchema.index({
originalFileName: "text",
caption: "text",
});

export default MediaSchema;

0 comments on commit 9c49700

Please sign in to comment.