+18
apps/api/src/util/api.ts
+18
apps/api/src/util/api.ts
···
4
4
import { ActorIdentifier, AtprotoDid, Handle, isHandle } from '@atcute/lexicons/syntax';
5
5
import { isAtprotoDid } from '@atcute/identity';
6
6
import { RedisClient } from 'bun';
7
+
import { ProfileViewBasic } from '../../../../libs/lexicons/dist/types/blue/recipes/actor/defs.js';
8
+
import { Blob, LegacyBlob } from '@atcute/lexicons';
9
+
import { buildCdnUrl } from './cdn.js';
7
10
8
11
const handleResolver = new CompositeHandleResolver({
9
12
strategy: 'race',
···
43
46
44
47
return handle;
45
48
}
49
+
50
+
export const buildProfileViewBasic = async (author: {
51
+
did: AtprotoDid;
52
+
displayName: string;
53
+
pronouns: string | null;
54
+
avatarRef: Blob | LegacyBlob | null;
55
+
createdAt: Date;
56
+
}, redis: RedisClient): Promise<ProfileViewBasic> => ({
57
+
did: author.did,
58
+
handle: await getHandle(author.did, redis),
59
+
displayName: author.displayName,
60
+
pronouns: author.pronouns ?? undefined,
61
+
avatar: author.avatarRef ? buildCdnUrl('avatar', author.did, author.avatarRef) : undefined,
62
+
createdAt: author.createdAt.toISOString(),
63
+
});
+34
-25
apps/api/src/xrpc/blue.recipes.feed.getRecipe.ts
+34
-25
apps/api/src/xrpc/blue.recipes.feed.getRecipe.ts
···
1
1
import { json, XRPCRouter, XRPCError } from '@atcute/xrpc-server';
2
-
import { BlueRecipesFeedGetRecipe, BlueRecipesFeedRecipe, BlueRecipesActorDefs } from '@cookware/lexicons';
2
+
import { BlueRecipesFeedGetRecipe, BlueRecipesFeedRecipe } from '@cookware/lexicons';
3
3
import { db, and, or, eq } from '@cookware/database';
4
-
import { parseDid } from '../util/api.js';
4
+
import { buildProfileViewBasic, parseDid } from '../util/api.js';
5
5
import { Logger } from 'pino';
6
6
import { parseResourceUri, ResourceUri } from '@atcute/lexicons';
7
7
import { recipeTable } from '@cookware/database/schema';
8
8
import { isLegacyBlob } from '@atcute/lexicons/interfaces';
9
9
import { RedisClient } from 'bun';
10
+
import { buildCdnUrl } from '../util/cdn.js';
10
11
11
12
const invalidUriError = (uri: string) => new XRPCError({
12
13
status: 400,
···
14
15
description: `The provided URI is invalid: ${uri}`,
15
16
});
16
17
17
-
export const registerGetRecipe = (router: XRPCRouter, _logger: Logger, _redis: RedisClient) => {
18
+
export const registerGetRecipe = (router: XRPCRouter, _logger: Logger, redis: RedisClient) => {
18
19
router.addQuery(BlueRecipesFeedGetRecipe.mainSchema, {
19
20
async handler({ params: { uris } }) {
20
21
const whereClauses = [];
···
36
37
const recipes = await db.query.recipeTable.findMany({
37
38
orderBy: recipeTable.createdAt,
38
39
where: or(...whereClauses),
40
+
with: {
41
+
author: {
42
+
columns: {
43
+
did: true,
44
+
displayName: true,
45
+
pronouns: true,
46
+
avatarRef: true,
47
+
createdAt: true,
48
+
},
49
+
},
50
+
},
39
51
});
40
52
41
53
return json({
42
-
recipes: recipes.map((recipe) => ({
43
-
author: {
44
-
$type: BlueRecipesActorDefs.profileViewBasicSchema.shape.$type.wrapped.expected,
45
-
did: recipe.did,
46
-
handle: 'hayden.moe',
47
-
createdAt: new Date().toISOString(),
48
-
},
49
-
cid: '',
50
-
indexedAt: new Date().toISOString(),
51
-
record: {
52
-
$type: BlueRecipesFeedRecipe.mainSchema.object.shape.$type.expected,
53
-
title: recipe.title,
54
-
description: recipe.description ?? undefined,
55
-
time: recipe.time ?? undefined,
56
-
serves: recipe.serves ?? undefined,
57
-
ingredients: recipe.ingredients as BlueRecipesFeedRecipe.Ingredient[],
58
-
steps: recipe.steps as BlueRecipesFeedRecipe.Step[],
59
-
image: isLegacyBlob(recipe.imageRef) ? undefined : recipe.imageRef ?? undefined,
60
-
createdAt: recipe.createdAt.toISOString(),
61
-
},
62
-
uri: recipe.uri as ResourceUri,
63
-
})),
54
+
recipes: await Promise.all(
55
+
recipes.map(async ({ author, ...recipe }) => ({
56
+
uri: recipe.uri as ResourceUri,
57
+
author: await buildProfileViewBasic(author, redis),
58
+
cid: recipe.cid,
59
+
indexedAt: recipe.ingestedAt.toISOString(),
60
+
record: {
61
+
$type: BlueRecipesFeedRecipe.mainSchema.object.shape.$type.expected,
62
+
title: recipe.title,
63
+
description: recipe.description ?? undefined,
64
+
time: recipe.time ?? undefined,
65
+
serves: recipe.serves ?? undefined,
66
+
ingredients: recipe.ingredients as BlueRecipesFeedRecipe.Ingredient[],
67
+
steps: recipe.steps as BlueRecipesFeedRecipe.Step[],
68
+
image: isLegacyBlob(recipe.imageRef) ? undefined : recipe.imageRef ?? undefined,
69
+
createdAt: recipe.createdAt.toISOString(),
70
+
},
71
+
}))
72
+
),
64
73
});
65
74
},
66
75
});
+12
-3
apps/api/src/xrpc/blue.recipes.feed.getRecipes.ts
+12
-3
apps/api/src/xrpc/blue.recipes.feed.getRecipes.ts
···
7
7
import { Logger } from 'pino';
8
8
import { isLegacyBlob } from '@atcute/lexicons/interfaces';
9
9
import { RedisClient } from 'bun';
10
+
import { buildCdnUrl } from '../util/cdn.js';
10
11
11
12
export const registerGetRecipes = (router: XRPCRouter, _logger: Logger, redis: RedisClient) => {
12
13
router.addQuery(BlueRecipesFeedGetRecipes.mainSchema, {
···
27
28
limit: limit,
28
29
where: whereClauses ? and(...whereClauses) : undefined,
29
30
with: {
30
-
author: true,
31
+
author: {
32
+
columns: {
33
+
did: true,
34
+
displayName: true,
35
+
pronouns: true,
36
+
avatarRef: true,
37
+
createdAt: true,
38
+
},
39
+
},
31
40
},
32
41
});
33
42
···
44
53
did: recipe.author.did,
45
54
handle: await getHandle(recipe.author.did, redis),
46
55
displayName: recipe.author.displayName,
47
-
avatar: isLegacyBlob(recipe.author.avatarRef) ? undefined : recipe.author.avatarRef ?? undefined,
56
+
avatar: recipe.author.avatarRef ? buildCdnUrl('avatar', recipe.author.did, recipe.author.avatarRef) : undefined,
48
57
pronouns: recipe.author.pronouns ?? undefined,
49
58
createdAt: recipe.author.createdAt.toISOString(),
50
59
},
51
-
cid: '',
60
+
cid: recipe.cid,
52
61
indexedAt: recipe.ingestedAt.toISOString(),
53
62
record: {
54
63
$type: BlueRecipesFeedRecipe.mainSchema.object.shape.$type.expected,
+1
-1
libs/lexicons/lexicons/profiles/defs.tsp
+1
-1
libs/lexicons/lexicons/profiles/defs.tsp
+1
-1
libs/lexicons/lib/types/blue/recipes/actor/defs.ts
+1
-1
libs/lexicons/lib/types/blue/recipes/actor/defs.ts
···
5
5
$type: /*#__PURE__*/ v.optional(
6
6
/*#__PURE__*/ v.literal("blue.recipes.actor.defs#profileViewBasic"),
7
7
),
8
-
avatar: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
8
+
avatar: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
9
9
createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
10
10
did: /*#__PURE__*/ v.didString(),
11
11
/**