feat(route): daily.dev sources (#17464)
This commit is contained in:
parent
ee2f07d17b
commit
d64c7620f9
|
|
@ -5,7 +5,7 @@ import { parseDate } from '@/utils/parse-date';
|
|||
import timezone from '@/utils/timezone';
|
||||
import cache from '@/utils/cache';
|
||||
import { art } from '@/utils/render';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { Route } from '@/types';
|
||||
import { getData, getList, getRedirectedLink } from './utils.js';
|
||||
|
||||
const variables = {
|
||||
first: 15,
|
||||
};
|
||||
import { baseUrl, getData, getList } from './utils.js';
|
||||
|
||||
const query = `
|
||||
query MostDiscussedFeed(
|
||||
|
|
@ -19,6 +15,7 @@ const query = `
|
|||
edges {
|
||||
node {
|
||||
...FeedPost
|
||||
contentHtml
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +30,7 @@ const query = `
|
|||
image
|
||||
readTime
|
||||
permalink
|
||||
commentsPermalink
|
||||
summary
|
||||
createdAt
|
||||
numUpvotes
|
||||
|
|
@ -52,37 +50,40 @@ const query = `
|
|||
bio
|
||||
}
|
||||
`;
|
||||
const graphqlQuery = {
|
||||
query,
|
||||
variables,
|
||||
};
|
||||
|
||||
export const route: Route = {
|
||||
path: '/discussed',
|
||||
example: '/daily/discussed',
|
||||
radar: [
|
||||
{
|
||||
source: ['daily.dev/popular'],
|
||||
source: ['app.daily.dev/discussed'],
|
||||
},
|
||||
],
|
||||
name: 'Most Discussed',
|
||||
maintainers: ['Rjnishant530'],
|
||||
handler,
|
||||
url: 'daily.dev/popular',
|
||||
url: 'app.daily.dev/discussed',
|
||||
};
|
||||
|
||||
async function handler() {
|
||||
const baseUrl = 'https://app.daily.dev/discussed';
|
||||
const data = await getData(graphqlQuery);
|
||||
const list = getList(data);
|
||||
const items = await getRedirectedLink(list);
|
||||
async function handler(ctx) {
|
||||
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
|
||||
const link = `${baseUrl}/discussed`;
|
||||
|
||||
const data = await getData({
|
||||
query,
|
||||
variables: {
|
||||
first: limit,
|
||||
},
|
||||
});
|
||||
const items = getList(data);
|
||||
|
||||
return {
|
||||
title: 'Most Discussed',
|
||||
link: baseUrl,
|
||||
title: 'Real-time discussions in the developer community | daily.dev',
|
||||
link,
|
||||
item: items,
|
||||
description: 'Most Discussed Posts on Daily.dev',
|
||||
logo: 'https://app.daily.dev/favicon-32x32.png',
|
||||
icon: 'https://app.daily.dev/favicon-32x32.png',
|
||||
description: 'Stay on top of real-time developer discussions on daily.dev. Join conversations happening now and engage with the most active community members.',
|
||||
logo: `${baseUrl}/favicon-32x32.png`,
|
||||
icon: `${baseUrl}/favicon-32x32.png`,
|
||||
language: 'en-us',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Route } from '@/types';
|
||||
import { getData, getList, getRedirectedLink } from './utils.js';
|
||||
import { baseUrl, getData, getList } from './utils.js';
|
||||
|
||||
const variables = {
|
||||
version: 11,
|
||||
|
|
@ -28,6 +28,7 @@ const query = `
|
|||
edges {
|
||||
node {
|
||||
...FeedPost
|
||||
contentHtml
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +43,7 @@ const query = `
|
|||
image
|
||||
readTime
|
||||
permalink
|
||||
commentsPermalink
|
||||
summary
|
||||
createdAt
|
||||
numUpvotes
|
||||
|
|
@ -72,27 +74,28 @@ export const route: Route = {
|
|||
example: '/daily',
|
||||
radar: [
|
||||
{
|
||||
source: ['daily.dev/popular'],
|
||||
source: ['app.daily.dev/popular'],
|
||||
},
|
||||
],
|
||||
name: 'Popular',
|
||||
maintainers: ['Rjnishant530'],
|
||||
handler,
|
||||
url: 'daily.dev/popular',
|
||||
url: 'app.daily.dev/popular',
|
||||
};
|
||||
|
||||
async function handler() {
|
||||
const baseUrl = 'https://app.daily.dev/popular';
|
||||
const link = `${baseUrl}/popular`;
|
||||
|
||||
const data = await getData(graphqlQuery);
|
||||
const list = getList(data);
|
||||
const items = await getRedirectedLink(list);
|
||||
const items = getList(data);
|
||||
|
||||
return {
|
||||
title: 'Popular',
|
||||
link: baseUrl,
|
||||
title: 'Popular posts on daily.dev',
|
||||
link,
|
||||
item: items,
|
||||
description: 'Popular Posts on Daily.dev',
|
||||
logo: 'https://app.daily.dev/favicon-32x32.png',
|
||||
icon: 'https://app.daily.dev/favicon-32x32.png',
|
||||
description: 'daily.dev is the easiest way to stay updated on the latest programming news. Get the best content from the top tech publications on any topic you want.',
|
||||
logo: `${baseUrl}/favicon-32x32.png`,
|
||||
icon: `${baseUrl}/favicon-32x32.png`,
|
||||
language: 'en-us',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Namespace } from '@/types';
|
|||
|
||||
export const namespace: Namespace = {
|
||||
name: 'Daily.dev',
|
||||
url: 'daily.dev',
|
||||
url: 'app.daily.dev',
|
||||
categories: ['social-media'],
|
||||
lang: 'en',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
import { Route } from '@/types';
|
||||
import { baseUrl, getBuildId, getData, getList } from './utils';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import cache from '@/utils/cache';
|
||||
import { config } from '@/config';
|
||||
|
||||
interface Source {
|
||||
id: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
image: string;
|
||||
permalink: string;
|
||||
description: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
const sourceFeedQuery = `
|
||||
query SourceFeed($source: ID!, $loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $supportedTypes: [String!]) {
|
||||
page: sourceFeed(
|
||||
source: $source
|
||||
first: $first
|
||||
after: $after
|
||||
ranking: $ranking
|
||||
supportedTypes: $supportedTypes
|
||||
) {
|
||||
...FeedPostConnection
|
||||
}
|
||||
}
|
||||
|
||||
fragment FeedPostConnection on PostConnection {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
...FeedPost
|
||||
pinnedAt
|
||||
contentHtml
|
||||
...UserPost @include(if: $loggedIn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment FeedPost on Post {
|
||||
...FeedPostInfo
|
||||
sharedPost {
|
||||
id
|
||||
title
|
||||
image
|
||||
readTime
|
||||
permalink
|
||||
commentsPermalink
|
||||
createdAt
|
||||
type
|
||||
tags
|
||||
source {
|
||||
id
|
||||
handle
|
||||
permalink
|
||||
image
|
||||
}
|
||||
slug
|
||||
}
|
||||
trending
|
||||
feedMeta
|
||||
collectionSources {
|
||||
handle
|
||||
image
|
||||
}
|
||||
numCollectionSources
|
||||
updatedAt
|
||||
slug
|
||||
}
|
||||
|
||||
fragment FeedPostInfo on Post {
|
||||
id
|
||||
title
|
||||
image
|
||||
readTime
|
||||
permalink
|
||||
commentsPermalink
|
||||
createdAt
|
||||
commented
|
||||
bookmarked
|
||||
views
|
||||
numUpvotes
|
||||
numComments
|
||||
summary
|
||||
bookmark {
|
||||
remindAt
|
||||
}
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
username
|
||||
permalink
|
||||
}
|
||||
type
|
||||
tags
|
||||
source {
|
||||
id
|
||||
handle
|
||||
name
|
||||
permalink
|
||||
image
|
||||
type
|
||||
}
|
||||
userState {
|
||||
vote
|
||||
flags {
|
||||
feedbackDismiss
|
||||
}
|
||||
}
|
||||
slug
|
||||
}
|
||||
|
||||
fragment UserPost on Post {
|
||||
read
|
||||
upvoted
|
||||
commented
|
||||
bookmarked
|
||||
downvoted
|
||||
}`;
|
||||
|
||||
export const route: Route = {
|
||||
path: '/source/:sourceId',
|
||||
example: '/daily/source/hn',
|
||||
parameters: {
|
||||
sourceId: 'The source id',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: ['app.daily.dev/sources/:sourceId'],
|
||||
},
|
||||
],
|
||||
name: 'Source Posts',
|
||||
maintainers: ['TonyRL'],
|
||||
handler,
|
||||
url: 'app.daily.dev',
|
||||
};
|
||||
|
||||
async function handler(ctx) {
|
||||
const sourceId = ctx.req.param('sourceId');
|
||||
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10;
|
||||
const link = `${baseUrl}/sources/${sourceId}`;
|
||||
|
||||
const buildId = await getBuildId();
|
||||
|
||||
const userData = (await cache.tryGet(`daily:source:${sourceId}`, async () => {
|
||||
const response = await ofetch(`${baseUrl}/_next/data/${buildId}/en/sources/${sourceId}.json`);
|
||||
return response.pageProps.source;
|
||||
})) as Source;
|
||||
|
||||
const items = await cache.tryGet(
|
||||
`daily:source:${sourceId}:posts`,
|
||||
async () => {
|
||||
const edges = await getData({
|
||||
query: sourceFeedQuery,
|
||||
variables: {
|
||||
source: sourceId,
|
||||
supportedTypes: ['article', 'video:youtube', 'collection'],
|
||||
period: 30,
|
||||
first: limit,
|
||||
after: '',
|
||||
loggedIn: false,
|
||||
},
|
||||
});
|
||||
return getList(edges);
|
||||
},
|
||||
config.cache.routeExpire,
|
||||
false
|
||||
);
|
||||
|
||||
return {
|
||||
title: `${userData.name} posts on daily.dev`,
|
||||
description: userData.description,
|
||||
link,
|
||||
item: items,
|
||||
image: userData.image,
|
||||
logo: userData.image,
|
||||
icon: userData.image,
|
||||
language: 'en-us',
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Route } from '@/types';
|
||||
import { getData, getList, getRedirectedLink } from './utils.js';
|
||||
import { baseUrl, getData, getList } from './utils.js';
|
||||
|
||||
const variables = {
|
||||
period: 7,
|
||||
|
|
@ -21,6 +21,7 @@ const query = `
|
|||
edges {
|
||||
node {
|
||||
...FeedPost
|
||||
contentHtml
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +36,7 @@ const query = `
|
|||
image
|
||||
readTime
|
||||
permalink
|
||||
commentsPermalink
|
||||
summary
|
||||
createdAt
|
||||
numUpvotes
|
||||
|
|
@ -56,37 +58,35 @@ const query = `
|
|||
|
||||
`;
|
||||
|
||||
const graphqlQuery = {
|
||||
query,
|
||||
variables,
|
||||
};
|
||||
|
||||
export const route: Route = {
|
||||
path: '/upvoted',
|
||||
example: '/daily/upvoted',
|
||||
radar: [
|
||||
{
|
||||
source: ['daily.dev/popular'],
|
||||
source: ['app.daily.dev/upvoted'],
|
||||
},
|
||||
],
|
||||
name: 'Most upvoted',
|
||||
maintainers: ['Rjnishant530'],
|
||||
handler,
|
||||
url: 'daily.dev/popular',
|
||||
url: 'app.daily.dev/upvoted',
|
||||
};
|
||||
|
||||
async function handler() {
|
||||
const baseUrl = 'https://app.daily.dev/upvoted';
|
||||
const data = await getData(graphqlQuery);
|
||||
const list = getList(data);
|
||||
const items = await getRedirectedLink(list);
|
||||
const link = `${baseUrl}/upvoted`;
|
||||
const data = await getData({
|
||||
query,
|
||||
variables,
|
||||
});
|
||||
const items = getList(data);
|
||||
|
||||
return {
|
||||
title: 'Most Upvoted',
|
||||
link: baseUrl,
|
||||
title: 'Most upvoted posts for developers | daily.dev',
|
||||
link,
|
||||
item: items,
|
||||
description: 'Most Upvoted Posts on Daily.dev',
|
||||
logo: 'https://app.daily.dev/favicon-32x32.png',
|
||||
icon: 'https://app.daily.dev/favicon-32x32.png',
|
||||
description: 'Find the most upvoted developer posts on daily.dev. Explore top-rated content in coding, tutorials, and tech news from the largest developer network in the world.',
|
||||
logo: `${baseUrl}/favicon-32x32.png`,
|
||||
icon: `${baseUrl}/favicon-32x32.png`,
|
||||
language: 'en-us',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import { Route } from '@/types';
|
||||
import { baseUrl, getBuildId, getData } from './utils';
|
||||
import { baseUrl, getBuildId, getData, getList } 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(
|
||||
|
|
@ -155,20 +150,18 @@ const userPostQuery = `
|
|||
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'],
|
||||
source: ['app.daily.dev/:userId/posts', 'app.daily.dev/:userId'],
|
||||
},
|
||||
],
|
||||
name: 'User Posts',
|
||||
maintainers: ['TonyRL'],
|
||||
handler,
|
||||
url: 'daily.dev',
|
||||
url: 'app.daily.dev',
|
||||
};
|
||||
|
||||
async function handler(ctx) {
|
||||
|
|
@ -178,12 +171,12 @@ async function handler(ctx) {
|
|||
const buildId = await getBuildId();
|
||||
|
||||
const userData = await cache.tryGet(`daily:user:${userId}`, async () => {
|
||||
const resposne = await ofetch(`${baseUrl}/_next/data/${buildId}/en/${userId}.json`, {
|
||||
const response = await ofetch(`${baseUrl}/_next/data/${buildId}/en/${userId}.json`, {
|
||||
query: {
|
||||
userId,
|
||||
},
|
||||
});
|
||||
return resposne.pageProps;
|
||||
return response.pageProps;
|
||||
});
|
||||
const user = (userData as any).user;
|
||||
|
||||
|
|
@ -198,17 +191,7 @@ async function handler(ctx) {
|
|||
loggedIn: false,
|
||||
},
|
||||
});
|
||||
return edges.map(({ node }) => ({
|
||||
title: node.title,
|
||||
description: render({
|
||||
image: node.image,
|
||||
content: node.contentHtml?.replaceAll('\n', '<br>') ?? node.summary,
|
||||
}),
|
||||
link: node.permalink,
|
||||
author: node.author?.name,
|
||||
category: node.tags,
|
||||
pubDate: parseDate(node.createdAt),
|
||||
}));
|
||||
return getList(edges);
|
||||
},
|
||||
config.cache.routeExpire,
|
||||
false
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@ import { parseDate } from '@/utils/parse-date';
|
|||
import ofetch from '@/utils/ofetch';
|
||||
import cache from '@/utils/cache';
|
||||
import { config } from '@/config';
|
||||
import { art } from '@/utils/render';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
||||
const baseUrl = 'https://app.daily.dev';
|
||||
export const baseUrl = 'https://app.daily.dev';
|
||||
const gqlUrl = `https://api.daily.dev/graphql`;
|
||||
|
||||
const getBuildId = () =>
|
||||
export const getBuildId = () =>
|
||||
cache.tryGet(
|
||||
'daily:buildId',
|
||||
async () => {
|
||||
|
|
@ -17,40 +22,30 @@ const getBuildId = () =>
|
|||
false
|
||||
);
|
||||
|
||||
const getData = async (graphqlQuery) => {
|
||||
const response = await ofetch(`${baseUrl}/api/graphql`, {
|
||||
export const getData = async (graphqlQuery) => {
|
||||
const response = await ofetch(gqlUrl, {
|
||||
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 = parseDate(createdAt);
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
link: permalink,
|
||||
description: summary,
|
||||
author: author?.name,
|
||||
itunes_item_image: image,
|
||||
pubDate,
|
||||
upvotes: numUpvotes,
|
||||
comments: numComments,
|
||||
category: tags,
|
||||
};
|
||||
});
|
||||
const render = (data) => art(path.join(__dirname, 'templates/posts.art'), data);
|
||||
|
||||
const getRedirectedLink = (data) =>
|
||||
Promise.all(
|
||||
data.map((v) =>
|
||||
cache.tryGet(v.link, async () => {
|
||||
const resp = await ofetch.raw(v.link);
|
||||
return { ...v, link: resp.headers.get('location') };
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
export { baseUrl, getBuildId, getData, getList, getRedirectedLink };
|
||||
export const getList = (edges) =>
|
||||
edges.map(({ node }) => ({
|
||||
id: node.id,
|
||||
title: node.title,
|
||||
link: node.commentsPermalink ?? node.permalink,
|
||||
guid: node.permalink,
|
||||
description: render({
|
||||
image: node.image,
|
||||
content: node.contentHtml?.replaceAll('\n', '<br>') ?? node.summary,
|
||||
}),
|
||||
author: node.author?.name,
|
||||
itunes_item_image: node.image,
|
||||
pubDate: parseDate(node.createdAt),
|
||||
upvotes: node.numUpvotes,
|
||||
comments: node.numComments,
|
||||
category: node.tags,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Route } from '@/types';
|
|||
import cache from '@/utils/cache';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import * as cheerio from 'cheerio';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { art } from '@/utils/render';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ofetch from '@/utils/ofetch';
|
|||
import * as cheerio from 'cheerio';
|
||||
import type { Context } from 'hono';
|
||||
import { ChartTitleSearchConnection } from './types';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { art } from '@/utils/render';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ofetch from '@/utils/ofetch';
|
|||
import { parseDate } from '@/utils/parse-date';
|
||||
import timezone from '@/utils/timezone';
|
||||
import md5 from '@/utils/md5';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { art } from '@/utils/render';
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'dayjs/locale/zh-cn';
|
|||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { CrowdfundingData, CrowdfundingDetailData, CrowdfundingDetailInfo, CrowdfundingItem, CrowdfundingList, DataResponse } from './types';
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import cache from '@/utils/cache';
|
|||
import ofetch from '@/utils/ofetch';
|
||||
import { load } from 'cheerio';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { art } from '@/utils/render';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ofetch from '@/utils/ofetch';
|
|||
import { load } from 'cheerio';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import cache from '@/utils/cache';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { art } from '@/utils/render';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { parseDate } from '@/utils/parse-date';
|
|||
import { baseUrl, getCitys, getDistricts } from './utils';
|
||||
import InvalidParameterError from '@/errors/types/invalid-parameter';
|
||||
import { art } from '@/utils/render';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
|
|
|||
Loading…
Reference in New Issue