diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts
index 66ce51037d7858..54a5957276b6d4 100644
--- a/lib/routes/daily/discussed.ts
+++ b/lib/routes/daily/discussed.ts
@@ -59,21 +59,10 @@ const graphqlQuery = {
export const route: Route = {
path: '/discussed',
- categories: ['social-media'],
example: '/daily/discussed',
- parameters: {},
- features: {
- requireConfig: false,
- requirePuppeteer: false,
- antiCrawler: false,
- supportBT: false,
- supportPodcast: false,
- supportScihub: false,
- },
radar: [
{
source: ['daily.dev/popular'],
- target: '',
},
],
name: 'Most Discussed',
diff --git a/lib/routes/daily/index.ts b/lib/routes/daily/index.ts
index b10418e9160a43..031f47c0a773eb 100644
--- a/lib/routes/daily/index.ts
+++ b/lib/routes/daily/index.ts
@@ -69,13 +69,13 @@ const graphqlQuery = {
export const route: Route = {
path: '/',
+ example: '/daily',
radar: [
{
source: ['daily.dev/popular'],
- target: '',
},
],
- name: 'Unknown',
+ name: 'Popular',
maintainers: ['Rjnishant530'],
handler,
url: 'daily.dev/popular',
diff --git a/lib/routes/daily/namespace.ts b/lib/routes/daily/namespace.ts
index acddafe867b238..2dbd9bf58eba9c 100644
--- a/lib/routes/daily/namespace.ts
+++ b/lib/routes/daily/namespace.ts
@@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Daily.dev',
url: 'daily.dev',
+ categories: ['social-media'],
};
diff --git a/lib/routes/daily/templates/posts.art b/lib/routes/daily/templates/posts.art
new file mode 100644
index 00000000000000..b8d94338e03448
--- /dev/null
+++ b/lib/routes/daily/templates/posts.art
@@ -0,0 +1,7 @@
+{{ if image }}
+
+{{ /if }}
+
+{{ if content }}
+{{@ content }}
+{{ /if }}
diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts
index 1b2c2ab9783691..68a2a19d6cc784 100644
--- a/lib/routes/daily/upvoted.ts
+++ b/lib/routes/daily/upvoted.ts
@@ -16,7 +16,7 @@ const query = `
...FeedPostConnection
}
}
-
+
fragment FeedPostConnection on PostConnection {
edges {
node {
@@ -24,11 +24,11 @@ const query = `
}
}
}
-
+
fragment FeedPost on Post {
...SharedPostInfo
}
-
+
fragment SharedPostInfo on Post {
id
title
@@ -44,7 +44,7 @@ const query = `
}
tags
}
-
+
fragment UserShortInfo on User {
id
name
@@ -53,7 +53,7 @@ const query = `
username
bio
}
-
+
`;
const graphqlQuery = {
@@ -63,21 +63,10 @@ const graphqlQuery = {
export const route: Route = {
path: '/upvoted',
- categories: ['social-media'],
example: '/daily/upvoted',
- parameters: {},
- features: {
- requireConfig: false,
- requirePuppeteer: false,
- antiCrawler: false,
- supportBT: false,
- supportPodcast: false,
- supportScihub: false,
- },
radar: [
{
source: ['daily.dev/popular'],
- target: '',
},
],
name: 'Most upvoted',
diff --git a/lib/routes/daily/user.ts b/lib/routes/daily/user.ts
new file mode 100644
index 00000000000000..7abaf15a029cb8
--- /dev/null
+++ b/lib/routes/daily/user.ts
@@ -0,0 +1,227 @@
+import { Route } from '@/types';
+import { baseUrl, getBuildId, getData } from './utils';
+import ofetch from '@/utils/ofetch';
+import cache from '@/utils/cache';
+import { config } from '@/config';
+import { parseDate } from '@/utils/parse-date';
+import { art } from '@/utils/render';
+import path from 'path';
+import { getCurrentPath } from '@/utils/helpers';
+const __dirname = getCurrentPath(import.meta.url);
+
+const userPostQuery = `
+ query AuthorFeed(
+ $loggedIn: Boolean! = false
+ $userId: ID!
+ $after: String
+ $first: Int
+ $supportedTypes: [String!] = [
+ "article"
+ "share"
+ "freeform"
+ "video:youtube"
+ "collection"
+ ]
+ ) {
+ page: authorFeed(
+ author: $userId
+ after: $after
+ first: $first
+ ranking: TIME
+ supportedTypes: $supportedTypes
+ ) {
+ ...FeedPostConnection
+ }
+ }
+ fragment FeedPostConnection on PostConnection {
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ edges {
+ node {
+ ...FeedPost
+ contentHtml
+ ...UserPost @include(if: $loggedIn)
+ }
+ }
+ }
+ fragment FeedPost on Post {
+ ...SharedPostInfo
+ sharedPost {
+ ...SharedPostInfo
+ }
+ trending
+ feedMeta
+ collectionSources {
+ handle
+ image
+ }
+ numCollectionSources
+ updatedAt
+ slug
+ }
+ fragment SharedPostInfo on Post {
+ id
+ title
+ titleHtml
+ image
+ readTime
+ permalink
+ commentsPermalink
+ summary
+ createdAt
+ private
+ upvoted
+ commented
+ bookmarked
+ views
+ numUpvotes
+ numComments
+ videoId
+ scout {
+ ...UserShortInfo
+ }
+ author {
+ ...UserShortInfo
+ }
+ type
+ tags
+ source {
+ ...SourceBaseInfo
+ }
+ downvoted
+ flags {
+ promoteToPublic
+ }
+ userState {
+ vote
+ flags {
+ feedbackDismiss
+ }
+ }
+ slug
+ }
+ fragment SourceBaseInfo on Source {
+ id
+ active
+ handle
+ name
+ permalink
+ public
+ type
+ description
+ image
+ membersCount
+ privilegedMembers {
+ user {
+ id
+ }
+ role
+ }
+ currentMember {
+ ...CurrentMember
+ }
+ memberPostingRole
+ memberInviteRole
+ }
+ fragment CurrentMember on SourceMember {
+ user {
+ id
+ }
+ permissions
+ role
+ referralToken
+ flags {
+ hideFeedPosts
+ collapsePinnedPosts
+ }
+ }
+ fragment UserShortInfo on User {
+ id
+ name
+ image
+ permalink
+ username
+ bio
+ createdAt
+ reputation
+ }
+ fragment UserPost on Post {
+ read
+ upvoted
+ commented
+ bookmarked
+ downvoted
+ }`;
+
+const render = (data) => art(path.join(__dirname, 'templates/posts.art'), data);
+
+export const route: Route = {
+ path: '/user/:userId',
+ example: '/daily/user/kramer',
+ radar: [
+ {
+ source: ['daily.dev/:userId/posts', 'daily.dev/:userId'],
+ },
+ ],
+ name: 'User Posts',
+ maintainers: ['TonyRL'],
+ handler,
+ url: 'daily.dev',
+};
+
+async function handler(ctx) {
+ const userId = ctx.req.param('userId');
+ const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 7;
+
+ const buildId = await getBuildId();
+
+ const userData = await cache.tryGet(`daily:user:${userId}`, async () => {
+ const resposne = await ofetch(`${baseUrl}/_next/data/${buildId}/en/${userId}.json`, {
+ query: {
+ userId,
+ },
+ });
+ return resposne.pageProps;
+ });
+ const user = (userData as any).user;
+
+ const items = await cache.tryGet(
+ `daily:user:${userId}:posts`,
+ async () => {
+ const edges = await getData({
+ query: userPostQuery,
+ variables: {
+ userId: user.id,
+ first: limit,
+ loggedIn: false,
+ },
+ });
+ return edges.map(({ node }) => ({
+ title: node.title,
+ description: render({
+ image: node.image,
+ content: node.contentHtml?.replaceAll('\n', '
') ?? node.summary,
+ }),
+ link: node.permalink,
+ author: node.author?.name,
+ category: node.tags,
+ pubDate: parseDate(node.createdAt),
+ }));
+ },
+ config.cache.routeExpire,
+ false
+ );
+
+ return {
+ title: `${user.name} | daily.dev`,
+ description: user.bio,
+ link: `${baseUrl}/${userId}/posts`,
+ item: items,
+ image: user.image,
+ logo: user.image,
+ icon: user.image,
+ language: 'en-us',
+ };
+}
diff --git a/lib/routes/daily/utils.ts b/lib/routes/daily/utils.ts
index d9c67025b85d2b..8f203c744d15a3 100644
--- a/lib/routes/daily/utils.ts
+++ b/lib/routes/daily/utils.ts
@@ -1,19 +1,34 @@
-import dayjs from 'dayjs';
-import got from '@/utils/got';
+import { parseDate } from '@/utils/parse-date';
+import ofetch from '@/utils/ofetch';
+import cache from '@/utils/cache';
+import { config } from '@/config';
-const getData = async (graphqlQuery) =>
- (
- await got
- .post('https://app.daily.dev/api/graphql', {
- json: graphqlQuery,
- })
- .json()
- ).data.page.edges;
+const baseUrl = 'https://app.daily.dev';
+
+const getBuildId = () =>
+ cache.tryGet(
+ 'daily:buildId',
+ async () => {
+ const response = await ofetch(`${baseUrl}/onboarding`);
+ const buildId = response.match(/"buildId":"(.*?)"/)[1];
+ return buildId;
+ },
+ config.cache.routeExpire,
+ false
+ );
+
+const getData = async (graphqlQuery) => {
+ const response = await ofetch(`${baseUrl}/api/graphql`, {
+ method: 'POST',
+ body: graphqlQuery,
+ });
+ return response.data.page.edges;
+};
const getList = (data) =>
data.map((value) => {
const { id, title, image, permalink, summary, createdAt, numUpvotes, author, tags, numComments } = value.node;
- const pubDate = dayjs(createdAt);
+ const pubDate = parseDate(createdAt);
return {
id,
title,
@@ -30,10 +45,12 @@ const getList = (data) =>
const getRedirectedLink = (data) =>
Promise.all(
- data.map(async (v) => {
- const resp = await got(v.link);
- return { ...v, link: resp.headers.link.split(/[<>]/g)[1] };
- })
+ data.map((v) =>
+ cache.tryGet(v.link, async () => {
+ const resp = await ofetch.raw(v.link);
+ return { ...v, link: resp.headers.get('location') };
+ })
+ )
);
-export { getData, getList, getRedirectedLink };
+export { baseUrl, getBuildId, getData, getList, getRedirectedLink };