style: apply prettier formatting to all files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,13 @@
|
||||
import {
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
index,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, timestamp, boolean, index, uniqueIndex } from "drizzle-orm/pg-core";
|
||||
import { users } from "./users";
|
||||
import { files } from "./files";
|
||||
|
||||
export const articles = pgTable(
|
||||
"articles",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
slug: text("slug").notNull(),
|
||||
title: text("title").notNull(),
|
||||
excerpt: text("excerpt"),
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
index,
|
||||
integer,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, timestamp, index, integer } from "drizzle-orm/pg-core";
|
||||
import { users } from "./users";
|
||||
|
||||
export const comments = pgTable(
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import {
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
bigint,
|
||||
integer,
|
||||
index,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, timestamp, bigint, integer, index } from "drizzle-orm/pg-core";
|
||||
|
||||
export const files = pgTable(
|
||||
"files",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
title: text("title"),
|
||||
description: text("description"),
|
||||
filename: text("filename").notNull(),
|
||||
|
||||
@@ -11,15 +11,14 @@ import {
|
||||
import { users } from "./users";
|
||||
import { recordings } from "./recordings";
|
||||
|
||||
export const achievementStatusEnum = pgEnum("achievement_status", [
|
||||
"draft",
|
||||
"published",
|
||||
]);
|
||||
export const achievementStatusEnum = pgEnum("achievement_status", ["draft", "published"]);
|
||||
|
||||
export const achievements = pgTable(
|
||||
"achievements",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
code: text("code").notNull(),
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
|
||||
@@ -12,16 +12,14 @@ import {
|
||||
import { users } from "./users";
|
||||
import { videos } from "./videos";
|
||||
|
||||
export const recordingStatusEnum = pgEnum("recording_status", [
|
||||
"draft",
|
||||
"published",
|
||||
"archived",
|
||||
]);
|
||||
export const recordingStatusEnum = pgEnum("recording_status", ["draft", "published", "archived"]);
|
||||
|
||||
export const recordings = pgTable(
|
||||
"recordings",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
title: text("title").notNull(),
|
||||
description: text("description"),
|
||||
slug: text("slug").notNull(),
|
||||
@@ -53,7 +51,9 @@ export const recordings = pgTable(
|
||||
export const recording_plays = pgTable(
|
||||
"recording_plays",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
recording_id: text("recording_id")
|
||||
.notNull()
|
||||
.references(() => recordings.id, { onDelete: "cascade" }),
|
||||
|
||||
@@ -15,7 +15,9 @@ export const roleEnum = pgEnum("user_role", ["model", "viewer", "admin"]);
|
||||
export const users = pgTable(
|
||||
"users",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
email: text("email").notNull(),
|
||||
password_hash: text("password_hash").notNull(),
|
||||
first_name: text("first_name"),
|
||||
|
||||
@@ -14,7 +14,9 @@ import { files } from "./files";
|
||||
export const videos = pgTable(
|
||||
"videos",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
slug: text("slug").notNull(),
|
||||
title: text("title").notNull(),
|
||||
description: text("description"),
|
||||
@@ -50,7 +52,9 @@ export const video_models = pgTable(
|
||||
export const video_likes = pgTable(
|
||||
"video_likes",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
video_id: text("video_id")
|
||||
.notNull()
|
||||
.references(() => videos.id, { onDelete: "cascade" }),
|
||||
@@ -68,7 +72,9 @@ export const video_likes = pgTable(
|
||||
export const video_plays = pgTable(
|
||||
"video_plays",
|
||||
{
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
video_id: text("video_id")
|
||||
.notNull()
|
||||
.references(() => videos.id, { onDelete: "cascade" }),
|
||||
|
||||
@@ -21,7 +21,12 @@ builder.queryField("commentsForVideo", (t) =>
|
||||
return Promise.all(
|
||||
commentList.map(async (c: any) => {
|
||||
const user = await ctx.db
|
||||
.select({ id: users.id, first_name: users.first_name, last_name: users.last_name, avatar: users.avatar })
|
||||
.select({
|
||||
id: users.id,
|
||||
first_name: users.first_name,
|
||||
last_name: users.last_name,
|
||||
avatar: users.avatar,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, c.user_id))
|
||||
.limit(1);
|
||||
@@ -57,7 +62,12 @@ builder.mutationField("createCommentForVideo", (t) =>
|
||||
await checkAchievements(ctx.db, ctx.currentUser.id, "social");
|
||||
|
||||
const user = await ctx.db
|
||||
.select({ id: users.id, first_name: users.first_name, last_name: users.last_name, avatar: users.avatar })
|
||||
.select({
|
||||
id: users.id,
|
||||
first_name: users.first_name,
|
||||
last_name: users.last_name,
|
||||
avatar: users.avatar,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, ctx.currentUser.id))
|
||||
.limit(1);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { builder } from "../builder";
|
||||
import { LeaderboardEntryType, UserGamificationType, AchievementType } from "../types/index";
|
||||
import { user_stats, users, user_achievements, achievements, user_points } from "../../db/schema/index";
|
||||
import {
|
||||
user_stats,
|
||||
users,
|
||||
user_achievements,
|
||||
achievements,
|
||||
user_points,
|
||||
} from "../../db/schema/index";
|
||||
import { eq, desc, gt, count, isNotNull, and } from "drizzle-orm";
|
||||
|
||||
builder.queryField("leaderboard", (t) =>
|
||||
@@ -73,7 +79,12 @@ builder.queryField("userGamification", (t) =>
|
||||
})
|
||||
.from(user_achievements)
|
||||
.leftJoin(achievements, eq(user_achievements.achievement_id, achievements.id))
|
||||
.where(and(eq(user_achievements.user_id, args.userId), isNotNull(user_achievements.date_unlocked)))
|
||||
.where(
|
||||
and(
|
||||
eq(user_achievements.user_id, args.userId),
|
||||
isNotNull(user_achievements.date_unlocked),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(user_achievements.date_unlocked));
|
||||
|
||||
const recentPoints = await ctx.db
|
||||
|
||||
@@ -162,11 +162,13 @@ builder.mutationField("updateRecording", (t) =>
|
||||
updates.title = args.title;
|
||||
updates.slug = slugify(args.title);
|
||||
}
|
||||
if (args.description !== null && args.description !== undefined) updates.description = args.description;
|
||||
if (args.description !== null && args.description !== undefined)
|
||||
updates.description = args.description;
|
||||
if (args.tags !== null && args.tags !== undefined) updates.tags = args.tags;
|
||||
if (args.status !== null && args.status !== undefined) updates.status = args.status;
|
||||
if (args.public !== null && args.public !== undefined) updates.public = args.public;
|
||||
if (args.linkedVideoId !== null && args.linkedVideoId !== undefined) updates.linked_video = args.linkedVideoId;
|
||||
if (args.linkedVideoId !== null && args.linkedVideoId !== undefined)
|
||||
updates.linked_video = args.linkedVideoId;
|
||||
|
||||
const updated = await ctx.db
|
||||
.update(recordings)
|
||||
@@ -319,11 +321,20 @@ builder.mutationField("updateRecordingPlay", (t) =>
|
||||
|
||||
await ctx.db
|
||||
.update(recording_plays)
|
||||
.set({ duration_played: args.durationPlayed, completed: args.completed, date_updated: new Date() })
|
||||
.set({
|
||||
duration_played: args.durationPlayed,
|
||||
completed: args.completed,
|
||||
date_updated: new Date(),
|
||||
})
|
||||
.where(eq(recording_plays.id, args.playId));
|
||||
|
||||
if (args.completed && !wasCompleted && ctx.currentUser) {
|
||||
await awardPoints(ctx.db, ctx.currentUser.id, "RECORDING_COMPLETE", existing[0].recording_id);
|
||||
await awardPoints(
|
||||
ctx.db,
|
||||
ctx.currentUser.id,
|
||||
"RECORDING_COMPLETE",
|
||||
existing[0].recording_id,
|
||||
);
|
||||
await checkAchievements(ctx.db, ctx.currentUser.id, "playback");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ builder.queryField("stats", (t) =>
|
||||
.select({ count: count() })
|
||||
.from(users)
|
||||
.where(eq(users.role, "viewer"));
|
||||
const videosCount = await ctx.db
|
||||
.select({ count: count() })
|
||||
.from(videos);
|
||||
const videosCount = await ctx.db.select({ count: count() }).from(videos);
|
||||
|
||||
return {
|
||||
models_count: modelsCount[0]?.count || 0,
|
||||
|
||||
@@ -28,11 +28,7 @@ builder.queryField("userProfile", (t) =>
|
||||
id: t.arg.string({ required: true }),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
const user = await ctx.db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, args.id))
|
||||
.limit(1);
|
||||
const user = await ctx.db.select().from(users).where(eq(users.id, args.id)).limit(1);
|
||||
return user[0] || null;
|
||||
},
|
||||
}),
|
||||
@@ -53,13 +49,19 @@ builder.mutationField("updateProfile", (t) =>
|
||||
if (!ctx.currentUser) throw new GraphQLError("Unauthorized");
|
||||
|
||||
const updates: Record<string, unknown> = { date_updated: new Date() };
|
||||
if (args.firstName !== undefined && args.firstName !== null) updates.first_name = args.firstName;
|
||||
if (args.firstName !== undefined && args.firstName !== null)
|
||||
updates.first_name = args.firstName;
|
||||
if (args.lastName !== undefined && args.lastName !== null) updates.last_name = args.lastName;
|
||||
if (args.artistName !== undefined && args.artistName !== null) updates.artist_name = args.artistName;
|
||||
if (args.description !== undefined && args.description !== null) updates.description = args.description;
|
||||
if (args.artistName !== undefined && args.artistName !== null)
|
||||
updates.artist_name = args.artistName;
|
||||
if (args.description !== undefined && args.description !== null)
|
||||
updates.description = args.description;
|
||||
if (args.tags !== undefined && args.tags !== null) updates.tags = args.tags;
|
||||
|
||||
await ctx.db.update(users).set(updates as any).where(eq(users.id, ctx.currentUser.id));
|
||||
await ctx.db
|
||||
.update(users)
|
||||
.set(updates as any)
|
||||
.where(eq(users.id, ctx.currentUser.id));
|
||||
|
||||
const updated = await ctx.db
|
||||
.select()
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { builder } from "../builder";
|
||||
import { VideoType, VideoLikeResponseType, VideoPlayResponseType, VideoLikeStatusType } from "../types/index";
|
||||
import { videos, video_models, video_likes, video_plays, users, files } from "../../db/schema/index";
|
||||
import {
|
||||
VideoType,
|
||||
VideoLikeResponseType,
|
||||
VideoPlayResponseType,
|
||||
VideoLikeStatusType,
|
||||
} from "../types/index";
|
||||
import {
|
||||
videos,
|
||||
video_models,
|
||||
video_likes,
|
||||
video_plays,
|
||||
users,
|
||||
files,
|
||||
} from "../../db/schema/index";
|
||||
import { eq, and, lte, desc, inArray, count } from "drizzle-orm";
|
||||
|
||||
async function enrichVideo(db: any, video: any) {
|
||||
@@ -25,8 +37,14 @@ async function enrichVideo(db: any, video: any) {
|
||||
}
|
||||
|
||||
// Count likes
|
||||
const likesCount = await db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, video.id));
|
||||
const playsCount = await db.select({ count: count() }).from(video_plays).where(eq(video_plays.video_id, video.id));
|
||||
const likesCount = await db
|
||||
.select({ count: count() })
|
||||
.from(video_likes)
|
||||
.where(eq(video_likes.video_id, video.id));
|
||||
const playsCount = await db
|
||||
.select({ count: count() })
|
||||
.from(video_plays)
|
||||
.where(eq(video_plays.video_id, video.id));
|
||||
|
||||
return {
|
||||
...video,
|
||||
@@ -63,10 +81,15 @@ builder.queryField("videos", (t) =>
|
||||
query = ctx.db
|
||||
.select({ v: videos })
|
||||
.from(videos)
|
||||
.where(and(
|
||||
lte(videos.upload_date, new Date()),
|
||||
inArray(videos.id, videoIds.map((v: any) => v.video_id)),
|
||||
))
|
||||
.where(
|
||||
and(
|
||||
lte(videos.upload_date, new Date()),
|
||||
inArray(
|
||||
videos.id,
|
||||
videoIds.map((v: any) => v.video_id),
|
||||
),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(videos.upload_date));
|
||||
}
|
||||
|
||||
@@ -74,10 +97,7 @@ builder.queryField("videos", (t) =>
|
||||
query = ctx.db
|
||||
.select({ v: videos })
|
||||
.from(videos)
|
||||
.where(and(
|
||||
lte(videos.upload_date, new Date()),
|
||||
eq(videos.featured, args.featured),
|
||||
))
|
||||
.where(and(lte(videos.upload_date, new Date()), eq(videos.featured, args.featured)))
|
||||
.orderBy(desc(videos.upload_date));
|
||||
}
|
||||
|
||||
@@ -123,7 +143,9 @@ builder.queryField("videoLikeStatus", (t) =>
|
||||
const existing = await ctx.db
|
||||
.select()
|
||||
.from(video_likes)
|
||||
.where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
|
||||
.where(
|
||||
and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)),
|
||||
)
|
||||
.limit(1);
|
||||
return { liked: existing.length > 0 };
|
||||
},
|
||||
@@ -142,7 +164,9 @@ builder.mutationField("likeVideo", (t) =>
|
||||
const existing = await ctx.db
|
||||
.select()
|
||||
.from(video_likes)
|
||||
.where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
|
||||
.where(
|
||||
and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing.length > 0) throw new GraphQLError("Already liked");
|
||||
@@ -154,10 +178,22 @@ builder.mutationField("likeVideo", (t) =>
|
||||
|
||||
await ctx.db
|
||||
.update(videos)
|
||||
.set({ likes_count: (await ctx.db.select({ c: videos.likes_count }).from(videos).where(eq(videos.id, args.videoId)).limit(1))[0]?.c as number + 1 || 1 })
|
||||
.set({
|
||||
likes_count:
|
||||
((
|
||||
await ctx.db
|
||||
.select({ c: videos.likes_count })
|
||||
.from(videos)
|
||||
.where(eq(videos.id, args.videoId))
|
||||
.limit(1)
|
||||
)[0]?.c as number) + 1 || 1,
|
||||
})
|
||||
.where(eq(videos.id, args.videoId));
|
||||
|
||||
const likesCount = await ctx.db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, args.videoId));
|
||||
const likesCount = await ctx.db
|
||||
.select({ count: count() })
|
||||
.from(video_likes)
|
||||
.where(eq(video_likes.video_id, args.videoId));
|
||||
return { liked: true, likes_count: likesCount[0]?.count || 1 };
|
||||
},
|
||||
}),
|
||||
@@ -175,21 +211,39 @@ builder.mutationField("unlikeVideo", (t) =>
|
||||
const existing = await ctx.db
|
||||
.select()
|
||||
.from(video_likes)
|
||||
.where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
|
||||
.where(
|
||||
and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing.length === 0) throw new GraphQLError("Not liked");
|
||||
|
||||
await ctx.db
|
||||
.delete(video_likes)
|
||||
.where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)));
|
||||
.where(
|
||||
and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)),
|
||||
);
|
||||
|
||||
await ctx.db
|
||||
.update(videos)
|
||||
.set({ likes_count: Math.max(((await ctx.db.select({ c: videos.likes_count }).from(videos).where(eq(videos.id, args.videoId)).limit(1))[0]?.c as number || 1) - 1, 0) })
|
||||
.set({
|
||||
likes_count: Math.max(
|
||||
(((
|
||||
await ctx.db
|
||||
.select({ c: videos.likes_count })
|
||||
.from(videos)
|
||||
.where(eq(videos.id, args.videoId))
|
||||
.limit(1)
|
||||
)[0]?.c as number) || 1) - 1,
|
||||
0,
|
||||
),
|
||||
})
|
||||
.where(eq(videos.id, args.videoId));
|
||||
|
||||
const likesCount = await ctx.db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, args.videoId));
|
||||
const likesCount = await ctx.db
|
||||
.select({ count: count() })
|
||||
.from(video_likes)
|
||||
.where(eq(video_likes.video_id, args.videoId));
|
||||
return { liked: false, likes_count: likesCount[0]?.count || 0 };
|
||||
},
|
||||
}),
|
||||
@@ -203,13 +257,19 @@ builder.mutationField("recordVideoPlay", (t) =>
|
||||
sessionId: t.arg.string(),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
const play = await ctx.db.insert(video_plays).values({
|
||||
video_id: args.videoId,
|
||||
user_id: ctx.currentUser?.id || null,
|
||||
session_id: args.sessionId || null,
|
||||
}).returning({ id: video_plays.id });
|
||||
const play = await ctx.db
|
||||
.insert(video_plays)
|
||||
.values({
|
||||
video_id: args.videoId,
|
||||
user_id: ctx.currentUser?.id || null,
|
||||
session_id: args.sessionId || null,
|
||||
})
|
||||
.returning({ id: video_plays.id });
|
||||
|
||||
const playsCount = await ctx.db.select({ count: count() }).from(video_plays).where(eq(video_plays.video_id, args.videoId));
|
||||
const playsCount = await ctx.db
|
||||
.select({ count: count() })
|
||||
.from(video_plays)
|
||||
.where(eq(video_plays.video_id, args.videoId));
|
||||
|
||||
await ctx.db
|
||||
.update(videos)
|
||||
@@ -237,7 +297,11 @@ builder.mutationField("updateVideoPlay", (t) =>
|
||||
resolve: async (_root, args, ctx) => {
|
||||
await ctx.db
|
||||
.update(video_plays)
|
||||
.set({ duration_watched: args.durationWatched, completed: args.completed, date_updated: new Date() })
|
||||
.set({
|
||||
duration_watched: args.durationWatched,
|
||||
completed: args.completed,
|
||||
date_updated: new Date(),
|
||||
})
|
||||
.where(eq(video_plays.id, args.playId));
|
||||
return true;
|
||||
},
|
||||
@@ -262,13 +326,26 @@ builder.queryField("analytics", (t) =>
|
||||
.where(eq(video_models.user_id, userId));
|
||||
|
||||
if (modelVideoIds.length === 0) {
|
||||
return { total_videos: 0, total_likes: 0, total_plays: 0, plays_by_date: {}, likes_by_date: {}, videos: [] };
|
||||
return {
|
||||
total_videos: 0,
|
||||
total_likes: 0,
|
||||
total_plays: 0,
|
||||
plays_by_date: {},
|
||||
likes_by_date: {},
|
||||
videos: [],
|
||||
};
|
||||
}
|
||||
|
||||
const videoIds = modelVideoIds.map((v: any) => v.video_id);
|
||||
const videoList = await ctx.db.select().from(videos).where(inArray(videos.id, videoIds));
|
||||
const plays = await ctx.db.select().from(video_plays).where(inArray(video_plays.video_id, videoIds));
|
||||
const likes = await ctx.db.select().from(video_likes).where(inArray(video_likes.video_id, videoIds));
|
||||
const plays = await ctx.db
|
||||
.select()
|
||||
.from(video_plays)
|
||||
.where(inArray(video_plays.video_id, videoIds));
|
||||
const likes = await ctx.db
|
||||
.select()
|
||||
.from(video_likes)
|
||||
.where(inArray(video_likes.video_id, videoIds));
|
||||
|
||||
const totalLikes = videoList.reduce((sum, v) => sum + (v.likes_count || 0), 0);
|
||||
const totalPlays = videoList.reduce((sum, v) => sum + (v.plays_count || 0), 0);
|
||||
@@ -290,9 +367,10 @@ builder.queryField("analytics", (t) =>
|
||||
const videoAnalytics = videoList.map((video) => {
|
||||
const vPlays = plays.filter((p) => p.video_id === video.id);
|
||||
const completedPlays = vPlays.filter((p) => p.completed).length;
|
||||
const avgWatchTime = vPlays.length > 0
|
||||
? vPlays.reduce((sum, p) => sum + (p.duration_watched || 0), 0) / vPlays.length
|
||||
: 0;
|
||||
const avgWatchTime =
|
||||
vPlays.length > 0
|
||||
? vPlays.reduce((sum, p) => sum + (p.duration_watched || 0), 0) / vPlays.length
|
||||
: 0;
|
||||
|
||||
return {
|
||||
id: video.id,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,12 @@ async function main() {
|
||||
decorateReply: true,
|
||||
});
|
||||
|
||||
const yoga = createYoga<{ req: FastifyRequest; reply: FastifyReply; db: typeof db; redis: typeof redis }>({
|
||||
const yoga = createYoga<{
|
||||
req: FastifyRequest;
|
||||
reply: FastifyReply;
|
||||
db: typeof db;
|
||||
redis: typeof redis;
|
||||
}>({
|
||||
schema,
|
||||
context: buildContext,
|
||||
graphqlEndpoint: "/graphql",
|
||||
@@ -101,7 +106,12 @@ async function main() {
|
||||
if (!existsSync(cacheFile)) {
|
||||
const originalPath = path.join(UPLOAD_DIR, id, filename);
|
||||
await sharp(originalPath)
|
||||
.resize({ width: preset.width, height: preset.height, fit: preset.fit ?? "inside", withoutEnlargement: true })
|
||||
.resize({
|
||||
width: preset.width,
|
||||
height: preset.height,
|
||||
fit: preset.fit ?? "inside",
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.webp({ quality: 92 })
|
||||
.toFile(cacheFile);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST || "localhost",
|
||||
port: parseInt(process.env.SMTP_PORT || "587"),
|
||||
secure: process.env.SMTP_SECURE === "true",
|
||||
auth: process.env.SMTP_USER ? {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
} : undefined,
|
||||
auth: process.env.SMTP_USER
|
||||
? {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
const FROM = process.env.EMAIL_FROM || "noreply@sexy.pivoine.art";
|
||||
|
||||
@@ -79,7 +79,10 @@ export async function updateUserStats(db: DB, userId: string): Promise<void> {
|
||||
const playbacksResult = await db.execute(sql`
|
||||
SELECT COUNT(*) as count FROM recording_plays
|
||||
WHERE user_id = ${userId}
|
||||
AND recording_id NOT IN (${sql.join(ownIds.map(id => sql`${id}`), sql`, `)})
|
||||
AND recording_id NOT IN (${sql.join(
|
||||
ownIds.map((id) => sql`${id}`),
|
||||
sql`, `,
|
||||
)})
|
||||
`);
|
||||
playbacksCount = parseInt((playbacksResult.rows[0] as any)?.count || "0");
|
||||
} else {
|
||||
@@ -135,11 +138,7 @@ export async function updateUserStats(db: DB, userId: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAchievements(
|
||||
db: DB,
|
||||
userId: string,
|
||||
category?: string,
|
||||
): Promise<void> {
|
||||
export async function checkAchievements(db: DB, userId: string, category?: string): Promise<void> {
|
||||
let achievementsQuery = db
|
||||
.select()
|
||||
.from(achievements)
|
||||
@@ -176,7 +175,7 @@ export async function checkAchievements(
|
||||
.update(user_achievements)
|
||||
.set({
|
||||
progress,
|
||||
date_unlocked: isUnlocked ? (existing[0].date_unlocked || new Date()) : null,
|
||||
date_unlocked: isUnlocked ? existing[0].date_unlocked || new Date() : null,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
|
||||
@@ -128,7 +128,9 @@ async function migrateUsers() {
|
||||
? tagsRes.rows[0].tags
|
||||
: JSON.parse(String(tagsRes.rows[0].tags || "[]"));
|
||||
}
|
||||
} catch { /* tags column may not exist on older Directus installs */ }
|
||||
} catch {
|
||||
/* tags column may not exist on older Directus installs */
|
||||
}
|
||||
|
||||
await query(
|
||||
`INSERT INTO users (id, email, password_hash, first_name, last_name, artist_name, slug,
|
||||
@@ -279,9 +281,7 @@ async function migrateVideoModels() {
|
||||
|
||||
async function migrateVideoLikes() {
|
||||
console.log("❤️ Migrating video likes...");
|
||||
const { rows } = await query(
|
||||
`SELECT id, video_id, user_id, date_created FROM sexy_video_likes`,
|
||||
);
|
||||
const { rows } = await query(`SELECT id, video_id, user_id, date_created FROM sexy_video_likes`);
|
||||
|
||||
let migrated = 0;
|
||||
for (const row of rows) {
|
||||
|
||||
Reference in New Issue
Block a user