mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +00:00 
			
		
		
		
	Make Github stars dynamic and improve database init (#5000)
I extracted the init database logic into its own file. You can now run it with yarn database:init. Added database entry for GitHub stars. Do you want me to remove the init route or is it used for something else ? --------- Co-authored-by: Ady Beraud <a.beraud96@gmail.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
		| @@ -308,6 +308,7 @@ | |||||||
|     "ts-loader": "^9.2.3", |     "ts-loader": "^9.2.3", | ||||||
|     "ts-node": "10.9.1", |     "ts-node": "10.9.1", | ||||||
|     "tsconfig-paths": "^4.2.0", |     "tsconfig-paths": "^4.2.0", | ||||||
|  |     "tsx": "^4.7.2", | ||||||
|     "typescript": "5.3.3", |     "typescript": "5.3.3", | ||||||
|     "vite": "^5.0.0", |     "vite": "^5.0.0", | ||||||
|     "vite-plugin-checker": "^0.6.2", |     "vite-plugin-checker": "^0.6.2", | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ | |||||||
|     "build": "npx next build", |     "build": "npx next build", | ||||||
|     "start": "npx next start", |     "start": "npx next start", | ||||||
|     "lint": "npx next lint", |     "lint": "npx next lint", | ||||||
|  |     "github:sync": "npx tsx src/github-sync/github-sync.ts", | ||||||
|  |     "database:migrate": "npx tsx src/database/migrate-database.ts", | ||||||
|     "database:generate:pg": "npx drizzle-kit generate:pg --config=src/database/postgres/drizzle-posgres.config.ts", |     "database:generate:pg": "npx drizzle-kit generate:pg --config=src/database/postgres/drizzle-posgres.config.ts", | ||||||
|     "database:generate:sqlite": "npx drizzle-kit generate:sqlite --config=src/database/sqlite/drizzle-sqlite.config.ts" |     "database:generate:sqlite": "npx drizzle-kit generate:sqlite --config=src/database/sqlite/drizzle-sqlite.config.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -11,8 +11,13 @@ import { | |||||||
|   LogoContainer, |   LogoContainer, | ||||||
| } from '@/app/_components/ui/layout/header/styled'; | } from '@/app/_components/ui/layout/header/styled'; | ||||||
| import { Logo } from '@/app/_components/ui/layout/Logo'; | import { Logo } from '@/app/_components/ui/layout/Logo'; | ||||||
|  | import { formatNumberOfStars } from '@/shared-utils/formatNumberOfStars'; | ||||||
|  |  | ||||||
| export const HeaderDesktop = () => { | type Props = { | ||||||
|  |   numberOfStars: number; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const HeaderDesktop = ({ numberOfStars }: Props) => { | ||||||
|   return ( |   return ( | ||||||
|     <DesktopNav> |     <DesktopNav> | ||||||
|       <LogoContainer> |       <LogoContainer> | ||||||
| @@ -26,7 +31,9 @@ export const HeaderDesktop = () => { | |||||||
|           Docs <ExternalArrow /> |           Docs <ExternalArrow /> | ||||||
|         </ListItem> |         </ListItem> | ||||||
|         <ListItem href="https://github.com/twentyhq/twenty"> |         <ListItem href="https://github.com/twentyhq/twenty"> | ||||||
|           <GithubIcon color="rgb(71,71,71)" /> 8.3k <ExternalArrow /> |           <GithubIcon color="rgb(71,71,71)" /> | ||||||
|  |           {formatNumberOfStars(numberOfStars)} | ||||||
|  |           <ExternalArrow /> | ||||||
|         </ListItem> |         </ListItem> | ||||||
|       </LinkList> |       </LinkList> | ||||||
|       <CallToAction /> |       <CallToAction /> | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ import { | |||||||
|   NavOpen, |   NavOpen, | ||||||
| } from '@/app/_components/ui/layout/header/styled'; | } from '@/app/_components/ui/layout/header/styled'; | ||||||
| import { Logo } from '@/app/_components/ui/layout/Logo'; | import { Logo } from '@/app/_components/ui/layout/Logo'; | ||||||
|  | import { formatNumberOfStars } from '@/shared-utils/formatNumberOfStars'; | ||||||
|  |  | ||||||
| const IBMPlexMono = IBM_Plex_Mono({ | const IBMPlexMono = IBM_Plex_Mono({ | ||||||
|   weight: '500', |   weight: '500', | ||||||
| @@ -25,7 +26,11 @@ const IBMPlexMono = IBM_Plex_Mono({ | |||||||
|   display: 'swap', |   display: 'swap', | ||||||
| }); | }); | ||||||
|  |  | ||||||
| export const HeaderMobile = () => { | type Props = { | ||||||
|  |   numberOfStars: number; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const HeaderMobile = ({ numberOfStars }: Props) => { | ||||||
|   const isTwentyDev = false; |   const isTwentyDev = false; | ||||||
|  |  | ||||||
|   const [menuOpen, setMenuOpen] = useState(false); |   const [menuOpen, setMenuOpen] = useState(false); | ||||||
| @@ -64,7 +69,8 @@ export const HeaderMobile = () => { | |||||||
|             Docs <ExternalArrow /> |             Docs <ExternalArrow /> | ||||||
|           </ListItem> |           </ListItem> | ||||||
|           <ListItem href="https://github.com/twentyhq/twenty"> |           <ListItem href="https://github.com/twentyhq/twenty"> | ||||||
|             <GithubIcon color="rgb(71,71,71)" /> 8.3k <ExternalArrow /> |             <GithubIcon color="rgb(71,71,71)" />{' '} | ||||||
|  |             {formatNumberOfStars(numberOfStars)} <ExternalArrow /> | ||||||
|           </ListItem> |           </ListItem> | ||||||
|         </MobileLinkList> |         </MobileLinkList> | ||||||
|         <CallToAction /> |         <CallToAction /> | ||||||
|   | |||||||
| @@ -1,13 +1,20 @@ | |||||||
| 'use client'; | import { desc } from 'drizzle-orm'; | ||||||
|  |  | ||||||
| import { HeaderDesktop } from '@/app/_components/ui/layout/header/HeaderDesktop'; | import { HeaderDesktop } from '@/app/_components/ui/layout/header/HeaderDesktop'; | ||||||
| import { HeaderMobile } from '@/app/_components/ui/layout/header/HeaderMobile'; | import { HeaderMobile } from '@/app/_components/ui/layout/header/HeaderMobile'; | ||||||
|  | import { findOne } from '@/database/database'; | ||||||
|  | import { githubStarsModel } from '@/database/model'; | ||||||
|  |  | ||||||
|  | export const AppHeader = async () => { | ||||||
|  |   const githubStars = await findOne( | ||||||
|  |     githubStarsModel, | ||||||
|  |     desc(githubStarsModel.timestamp), | ||||||
|  |   ); | ||||||
|  |  | ||||||
| export const AppHeader = () => { |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeaderDesktop /> |       <HeaderDesktop numberOfStars={githubStars?.[0]?.numberOfStars} /> | ||||||
|       <HeaderMobile /> |       <HeaderMobile numberOfStars={githubStars?.[0]?.numberOfStars} /> | ||||||
|     </> |     </> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								packages/twenty-website/src/app/api/github-stars/route.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/twenty-website/src/app/api/github-stars/route.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | import { desc } from 'drizzle-orm'; | ||||||
|  |  | ||||||
|  | import { findOne } from '@/database/database'; | ||||||
|  | import { githubStarsModel } from '@/database/model'; | ||||||
|  | import { formatNumberOfStars } from '@/shared-utils/formatNumberOfStars'; | ||||||
|  |  | ||||||
|  | export const dynamic = 'force-dynamic'; | ||||||
|  |  | ||||||
|  | export async function GET() { | ||||||
|  |   try { | ||||||
|  |     const githubStars = await findOne( | ||||||
|  |       githubStarsModel, | ||||||
|  |       desc(githubStarsModel.timestamp), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const formattedGithubNumberOfStars = formatNumberOfStars( | ||||||
|  |       githubStars[0].numberOfStars, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return Response.json(formattedGithubNumberOfStars); | ||||||
|  |   } catch (error: any) { | ||||||
|  |     return new Response(`Github stars error: ${error?.message}`, { | ||||||
|  |       status: 500, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| export const dynamic = 'force-dynamic'; |  | ||||||
|  |  | ||||||
| import { global } from '@apollo/client/utilities/globals'; |  | ||||||
| import { graphql } from '@octokit/graphql'; |  | ||||||
|  |  | ||||||
| import { fetchAssignableUsers } from '@/app/contributors/api/fetch-assignable-users'; |  | ||||||
| import { fetchIssuesPRs } from '@/app/contributors/api/fetch-issues-prs'; |  | ||||||
| import { saveIssuesToDB } from '@/app/contributors/api/save-issues-to-db'; |  | ||||||
| import { savePRsToDB } from '@/app/contributors/api/save-prs-to-db'; |  | ||||||
| import { IssueNode, PullRequestNode } from '@/app/contributors/api/types'; |  | ||||||
| import { migrate } from '@/database/database'; |  | ||||||
|  |  | ||||||
| export async function GET() { |  | ||||||
|   if (!global.process.env.GITHUB_TOKEN) { |  | ||||||
|     return new Response('No GitHub token provided', { status: 500 }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const query = graphql.defaults({ |  | ||||||
|     headers: { |  | ||||||
|       Authorization: 'bearer ' + global.process.env.GITHUB_TOKEN, |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   await migrate(); |  | ||||||
|  |  | ||||||
|   const assignableUsers = await fetchAssignableUsers(query); |  | ||||||
|   const fetchedPRs = (await fetchIssuesPRs( |  | ||||||
|     query, |  | ||||||
|     null, |  | ||||||
|     false, |  | ||||||
|     [], |  | ||||||
|   )) as Array<PullRequestNode>; |  | ||||||
|   const fetchedIssues = (await fetchIssuesPRs( |  | ||||||
|     query, |  | ||||||
|     null, |  | ||||||
|     true, |  | ||||||
|     [], |  | ||||||
|   )) as Array<IssueNode>; |  | ||||||
|  |  | ||||||
|   savePRsToDB(fetchedPRs, assignableUsers); |  | ||||||
|   saveIssuesToDB(fetchedIssues, assignableUsers); |  | ||||||
|  |  | ||||||
|   return new Response('Data synced', { |  | ||||||
|     status: 200, |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| @@ -1,13 +1,13 @@ | |||||||
| import { graphql } from '@octokit/graphql'; | import { graphql } from '@octokit/graphql'; | ||||||
| import { desc } from 'drizzle-orm'; | import { desc } from 'drizzle-orm'; | ||||||
|  |  | ||||||
| import { fetchAssignableUsers } from '@/app/contributors/api/fetch-assignable-users'; |  | ||||||
| import { saveIssuesToDB } from '@/app/contributors/api/save-issues-to-db'; |  | ||||||
| import { savePRsToDB } from '@/app/contributors/api/save-prs-to-db'; |  | ||||||
| import { searchIssuesPRs } from '@/app/contributors/api/search-issues-prs'; |  | ||||||
| import { IssueNode, PullRequestNode } from '@/app/contributors/api/types'; |  | ||||||
| import { findOne } from '@/database/database'; | import { findOne } from '@/database/database'; | ||||||
| import { issueModel, pullRequestModel } from '@/database/model'; | import { issueModel, pullRequestModel } from '@/database/model'; | ||||||
|  | import { fetchAssignableUsers } from '@/github-sync/contributors/fetch-assignable-users'; | ||||||
|  | import { saveIssuesToDB } from '@/github-sync/contributors/save-issues-to-db'; | ||||||
|  | import { savePRsToDB } from '@/github-sync/contributors/save-prs-to-db'; | ||||||
|  | import { searchIssuesPRs } from '@/github-sync/contributors/search-issues-prs'; | ||||||
|  | import { IssueNode, PullRequestNode } from '@/github-sync/contributors/types'; | ||||||
|  |  | ||||||
| export async function GET() { | export async function GET() { | ||||||
|   if (!global.process.env.GITHUB_TOKEN) { |   if (!global.process.env.GITHUB_TOKEN) { | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								packages/twenty-website/src/database/migrate-database.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/twenty-website/src/database/migrate-database.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import { migrate } from '@/database/database'; | ||||||
|  |  | ||||||
|  | export const migrateDatabase = async () => { | ||||||
|  |   await migrate(); | ||||||
|  |   process.exit(0); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | migrateDatabase(); | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { | import { | ||||||
|  |   pgGithubStars, | ||||||
|   pgIssueLabels, |   pgIssueLabels, | ||||||
|   pgIssues, |   pgIssues, | ||||||
|   pgLabels, |   pgLabels, | ||||||
| @@ -31,17 +32,19 @@ export const issueLabelModel = isSqliteDriver | |||||||
|   ? sqlLiteIssueLabels |   ? sqlLiteIssueLabels | ||||||
|   : pgIssueLabels; |   : pgIssueLabels; | ||||||
|  |  | ||||||
| export type User = typeof sqlLiteUsers.$inferSelect; | export const githubStarsModel = pgGithubStars; | ||||||
| export type PullRequest = typeof sqlLitePullRequests.$inferSelect; |  | ||||||
| export type Issue = typeof sqlLiteIssues.$inferSelect; |  | ||||||
| export type Label = typeof sqlLiteLabels.$inferSelect; |  | ||||||
| export type PullRequestLabel = typeof sqlLitePullRequestLabels.$inferSelect; |  | ||||||
| export type IssueLabel = typeof sqlLiteIssueLabels.$inferSelect; |  | ||||||
|  |  | ||||||
| export type UserInsert = typeof sqlLiteUsers.$inferInsert; | export type User = typeof pgUsers.$inferSelect; | ||||||
| export type PullRequestInsert = typeof sqlLitePullRequests.$inferInsert; | export type PullRequest = typeof pgPullRequests.$inferSelect; | ||||||
| export type IssueInsert = typeof sqlLiteIssues.$inferInsert; | export type Issue = typeof pgIssues.$inferSelect; | ||||||
| export type LabelInsert = typeof sqlLiteLabels.$inferInsert; | export type Label = typeof pgLabels.$inferSelect; | ||||||
| export type PullRequestLabelInsert = | export type PullRequestLabel = typeof pgPullRequestLabels.$inferSelect; | ||||||
|   typeof sqlLitePullRequestLabels.$inferInsert; | export type IssueLabel = typeof pgIssueLabels.$inferSelect; | ||||||
| export type IssueLabelInsert = typeof sqlLiteIssueLabels.$inferInsert; |  | ||||||
|  | export type UserInsert = typeof pgUsers.$inferInsert; | ||||||
|  | export type PullRequestInsert = typeof pgPullRequests.$inferInsert; | ||||||
|  | export type IssueInsert = typeof pgIssues.$inferInsert; | ||||||
|  | export type LabelInsert = typeof pgLabels.$inferInsert; | ||||||
|  | export type PullRequestLabelInsert = typeof pgPullRequestLabels.$inferInsert; | ||||||
|  | export type IssueLabelInsert = typeof pgIssueLabels.$inferInsert; | ||||||
|  | export type GithubStars = typeof pgGithubStars.$inferInsert; | ||||||
|   | |||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | CREATE TABLE IF NOT EXISTS "githubStars" ( | ||||||
|  | 	"timestamp" timestamp DEFAULT now() NOT NULL, | ||||||
|  | 	"numberOfStars" integer | ||||||
|  | ); | ||||||
| @@ -27,12 +27,8 @@ | |||||||
|           "name": "issueLabels_issueId_issues_id_fk", |           "name": "issueLabels_issueId_issues_id_fk", | ||||||
|           "tableFrom": "issueLabels", |           "tableFrom": "issueLabels", | ||||||
|           "tableTo": "issues", |           "tableTo": "issues", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["issueId"], | ||||||
|             "issueId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         }, |         }, | ||||||
| @@ -40,12 +36,8 @@ | |||||||
|           "name": "issueLabels_labelId_labels_id_fk", |           "name": "issueLabels_labelId_labels_id_fk", | ||||||
|           "tableFrom": "issueLabels", |           "tableFrom": "issueLabels", | ||||||
|           "tableTo": "labels", |           "tableTo": "labels", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["labelId"], | ||||||
|             "labelId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         } |         } | ||||||
| @@ -118,12 +110,8 @@ | |||||||
|           "name": "issues_authorId_users_id_fk", |           "name": "issues_authorId_users_id_fk", | ||||||
|           "tableFrom": "issues", |           "tableFrom": "issues", | ||||||
|           "tableTo": "users", |           "tableTo": "users", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["authorId"], | ||||||
|             "authorId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         } |         } | ||||||
| @@ -194,12 +182,8 @@ | |||||||
|           "name": "pullRequestLabels_pullRequestExternalId_pullRequests_id_fk", |           "name": "pullRequestLabels_pullRequestExternalId_pullRequests_id_fk", | ||||||
|           "tableFrom": "pullRequestLabels", |           "tableFrom": "pullRequestLabels", | ||||||
|           "tableTo": "pullRequests", |           "tableTo": "pullRequests", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["pullRequestExternalId"], | ||||||
|             "pullRequestExternalId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         }, |         }, | ||||||
| @@ -207,12 +191,8 @@ | |||||||
|           "name": "pullRequestLabels_labelId_labels_id_fk", |           "name": "pullRequestLabels_labelId_labels_id_fk", | ||||||
|           "tableFrom": "pullRequestLabels", |           "tableFrom": "pullRequestLabels", | ||||||
|           "tableTo": "labels", |           "tableTo": "labels", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["labelId"], | ||||||
|             "labelId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         } |         } | ||||||
| @@ -285,12 +265,8 @@ | |||||||
|           "name": "pullRequests_authorId_users_id_fk", |           "name": "pullRequests_authorId_users_id_fk", | ||||||
|           "tableFrom": "pullRequests", |           "tableFrom": "pullRequests", | ||||||
|           "tableTo": "users", |           "tableTo": "users", | ||||||
|           "columnsFrom": [ |           "columnsFrom": ["authorId"], | ||||||
|             "authorId" |           "columnsTo": ["id"], | ||||||
|           ], |  | ||||||
|           "columnsTo": [ |  | ||||||
|             "id" |  | ||||||
|           ], |  | ||||||
|           "onDelete": "no action", |           "onDelete": "no action", | ||||||
|           "onUpdate": "no action" |           "onUpdate": "no action" | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -8,6 +8,13 @@ | |||||||
|       "when": 1707921820164, |       "when": 1707921820164, | ||||||
|       "tag": "0000_absent_giant_man", |       "tag": "0000_absent_giant_man", | ||||||
|       "breakpoints": true |       "breakpoints": true | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "idx": 1, | ||||||
|  |       "version": "5", | ||||||
|  |       "when": 1713792223113, | ||||||
|  |       "tag": "0001_marvelous_eddie_brock", | ||||||
|  |       "breakpoints": true | ||||||
|     } |     } | ||||||
|   ] |   ] | ||||||
| } | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { pgTable, text } from 'drizzle-orm/pg-core'; | import { integer, pgTable, text, timestamp } from 'drizzle-orm/pg-core'; | ||||||
|  |  | ||||||
| export const pgUsers = pgTable('users', { | export const pgUsers = pgTable('users', { | ||||||
|   id: text('id').primaryKey(), |   id: text('id').primaryKey(), | ||||||
| @@ -50,3 +50,8 @@ export const pgIssueLabels = pgTable('issueLabels', { | |||||||
|   issueId: text('issueId').references(() => pgIssues.id), |   issueId: text('issueId').references(() => pgIssues.id), | ||||||
|   labelId: text('labelId').references(() => pgLabels.id), |   labelId: text('labelId').references(() => pgLabels.id), | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | export const pgGithubStars = pgTable('githubStars', { | ||||||
|  |   timestamp: timestamp('timestamp').notNull().defaultNow(), | ||||||
|  |   numberOfStars: integer('numberOfStars'), | ||||||
|  | }); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { graphql } from '@octokit/graphql'; | import { graphql } from '@octokit/graphql'; | ||||||
| 
 | 
 | ||||||
| import { Repository } from '@/app/contributors/api/types'; | import { Repository } from '@/github-sync/contributors/types'; | ||||||
| 
 | 
 | ||||||
| export async function fetchAssignableUsers( | export async function fetchAssignableUsers( | ||||||
|   query: typeof graphql, |   query: typeof graphql, | ||||||
| @@ -4,7 +4,7 @@ import { | |||||||
|   IssueNode, |   IssueNode, | ||||||
|   PullRequestNode, |   PullRequestNode, | ||||||
|   Repository, |   Repository, | ||||||
| } from '@/app/contributors/api/types'; | } from '@/github-sync/contributors/types'; | ||||||
| 
 | 
 | ||||||
| export async function fetchIssuesPRs( | export async function fetchIssuesPRs( | ||||||
|   query: typeof graphql, |   query: typeof graphql, | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| import { IssueNode } from '@/app/contributors/api/types'; |  | ||||||
| import { insertMany } from '@/database/database'; | import { insertMany } from '@/database/database'; | ||||||
| import { | import { | ||||||
|   issueLabelModel, |   issueLabelModel, | ||||||
| @@ -6,6 +5,7 @@ import { | |||||||
|   labelModel, |   labelModel, | ||||||
|   userModel, |   userModel, | ||||||
| } from '@/database/model'; | } from '@/database/model'; | ||||||
|  | import { IssueNode } from '@/github-sync/contributors/types'; | ||||||
| 
 | 
 | ||||||
| export async function saveIssuesToDB( | export async function saveIssuesToDB( | ||||||
|   issues: Array<IssueNode>, |   issues: Array<IssueNode>, | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| import { PullRequestNode } from '@/app/contributors/api/types'; |  | ||||||
| import { insertMany } from '@/database/database'; | import { insertMany } from '@/database/database'; | ||||||
| import { | import { | ||||||
|   labelModel, |   labelModel, | ||||||
| @@ -6,6 +5,7 @@ import { | |||||||
|   pullRequestModel, |   pullRequestModel, | ||||||
|   userModel, |   userModel, | ||||||
| } from '@/database/model'; | } from '@/database/model'; | ||||||
|  | import { PullRequestNode } from '@/github-sync/contributors/types'; | ||||||
| 
 | 
 | ||||||
| export async function savePRsToDB( | export async function savePRsToDB( | ||||||
|   prs: Array<PullRequestNode>, |   prs: Array<PullRequestNode>, | ||||||
| @@ -4,7 +4,7 @@ import { | |||||||
|   IssueNode, |   IssueNode, | ||||||
|   PullRequestNode, |   PullRequestNode, | ||||||
|   SearchIssuesPRsQuery, |   SearchIssuesPRsQuery, | ||||||
| } from '@/app/contributors/api/types'; | } from '@/github-sync/contributors/types'; | ||||||
| 
 | 
 | ||||||
| export async function searchIssuesPRs( | export async function searchIssuesPRs( | ||||||
|   query: typeof graphql, |   query: typeof graphql, | ||||||
| @@ -64,11 +64,16 @@ export interface AssignableUsers { | |||||||
|   nodes: AssignableUserNode[]; |   nodes: AssignableUserNode[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface Stargazers { | ||||||
|  |   totalCount: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export interface Repository { | export interface Repository { | ||||||
|   repository: { |   repository: { | ||||||
|     pullRequests: PullRequests; |     pullRequests: PullRequests; | ||||||
|     issues: Issues; |     issues: Issues; | ||||||
|     assignableUsers: AssignableUsers; |     assignableUsers: AssignableUsers; | ||||||
|  |     stargazers: Stargazers; | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | import { global } from '@apollo/client/utilities/globals'; | ||||||
|  | import { graphql } from '@octokit/graphql'; | ||||||
|  |  | ||||||
|  | import { fetchAssignableUsers } from '@/github-sync/contributors/fetch-assignable-users'; | ||||||
|  | import { fetchIssuesPRs } from '@/github-sync/contributors/fetch-issues-prs'; | ||||||
|  | import { saveIssuesToDB } from '@/github-sync/contributors/save-issues-to-db'; | ||||||
|  | import { savePRsToDB } from '@/github-sync/contributors/save-prs-to-db'; | ||||||
|  | import { IssueNode, PullRequestNode } from '@/github-sync/contributors/types'; | ||||||
|  | import { fetchAndSaveGithubStars } from '@/github-sync/github-stars/fetch-and-save-github-stars'; | ||||||
|  |  | ||||||
|  | export const fetchAndSaveGithubData = async () => { | ||||||
|  |   if (!global.process.env.GITHUB_TOKEN) { | ||||||
|  |     return new Error('No GitHub token provided'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   console.log('Synching data..'); | ||||||
|  |  | ||||||
|  |   const query = graphql.defaults({ | ||||||
|  |     headers: { | ||||||
|  |       Authorization: 'bearer ' + global.process.env.GITHUB_TOKEN, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   await fetchAndSaveGithubStars(query); | ||||||
|  |  | ||||||
|  |   const assignableUsers = await fetchAssignableUsers(query); | ||||||
|  |   const fetchedPRs = (await fetchIssuesPRs( | ||||||
|  |     query, | ||||||
|  |     null, | ||||||
|  |     false, | ||||||
|  |     [], | ||||||
|  |   )) as Array<PullRequestNode>; | ||||||
|  |   const fetchedIssues = (await fetchIssuesPRs( | ||||||
|  |     query, | ||||||
|  |     null, | ||||||
|  |     true, | ||||||
|  |     [], | ||||||
|  |   )) as Array<IssueNode>; | ||||||
|  |  | ||||||
|  |   savePRsToDB(fetchedPRs, assignableUsers); | ||||||
|  |   saveIssuesToDB(fetchedIssues, assignableUsers); | ||||||
|  |  | ||||||
|  |   console.log('data synched!'); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | import { graphql } from '@octokit/graphql'; | ||||||
|  |  | ||||||
|  | import { insertMany } from '@/database/database'; | ||||||
|  | import { githubStarsModel } from '@/database/model'; | ||||||
|  | import { Repository } from '@/github-sync/contributors/types'; | ||||||
|  |  | ||||||
|  | export const fetchAndSaveGithubStars = async ( | ||||||
|  |   query: typeof graphql, | ||||||
|  | ): Promise<void> => { | ||||||
|  |   const { repository } = await query<Repository>(` | ||||||
|  |         query { | ||||||
|  |           repository(owner: "twentyhq", name: "twenty") { | ||||||
|  |             stargazers { | ||||||
|  |               totalCount | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     `); | ||||||
|  |  | ||||||
|  |   const numberOfStars = repository.stargazers.totalCount; | ||||||
|  |  | ||||||
|  |   await insertMany(githubStarsModel, [ | ||||||
|  |     { | ||||||
|  |       numberOfStars, | ||||||
|  |     }, | ||||||
|  |   ]); | ||||||
|  | }; | ||||||
							
								
								
									
										8
									
								
								packages/twenty-website/src/github-sync/github-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/twenty-website/src/github-sync/github-sync.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import { fetchAndSaveGithubData } from '@/github-sync/fetch-and-save-github-data'; | ||||||
|  |  | ||||||
|  | export const githubSync = async () => { | ||||||
|  |   await fetchAndSaveGithubData(); | ||||||
|  |   process.exit(0); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | githubSync(); | ||||||
| @@ -0,0 +1,3 @@ | |||||||
|  | export const formatNumberOfStars = (numberOfStars: number) => { | ||||||
|  |   return Math.floor(numberOfStars / 100) / 10 + 'k'; | ||||||
|  | }; | ||||||
							
								
								
									
										28
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -25843,7 +25843,7 @@ __metadata: | |||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
| "esbuild@npm:^0.19.7": | "esbuild@npm:^0.19.7, esbuild@npm:~0.19.10": | ||||||
|   version: 0.19.12 |   version: 0.19.12 | ||||||
|   resolution: "esbuild@npm:0.19.12" |   resolution: "esbuild@npm:0.19.12" | ||||||
|   dependencies: |   dependencies: | ||||||
| @@ -28372,6 +28372,15 @@ __metadata: | |||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
|  | "get-tsconfig@npm:^4.7.2": | ||||||
|  |   version: 4.7.3 | ||||||
|  |   resolution: "get-tsconfig@npm:4.7.3" | ||||||
|  |   dependencies: | ||||||
|  |     resolve-pkg-maps: "npm:^1.0.0" | ||||||
|  |   checksum: b15ca9d5d0887ebfccadc9fe88b6ff3827a5691ec90e7608a5e9c74bef959c14aba62f6bb88ac7f50322395731789a2cf654244f00e10f4f76349911b6846d6f | ||||||
|  |   languageName: node | ||||||
|  |   linkType: hard | ||||||
|  | 
 | ||||||
| "getpass@npm:^0.1.1": | "getpass@npm:^0.1.1": | ||||||
|   version: 0.1.7 |   version: 0.1.7 | ||||||
|   resolution: "getpass@npm:0.1.7" |   resolution: "getpass@npm:0.1.7" | ||||||
| @@ -46211,6 +46220,22 @@ __metadata: | |||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
|  | "tsx@npm:^4.7.2": | ||||||
|  |   version: 4.7.2 | ||||||
|  |   resolution: "tsx@npm:4.7.2" | ||||||
|  |   dependencies: | ||||||
|  |     esbuild: "npm:~0.19.10" | ||||||
|  |     fsevents: "npm:~2.3.3" | ||||||
|  |     get-tsconfig: "npm:^4.7.2" | ||||||
|  |   dependenciesMeta: | ||||||
|  |     fsevents: | ||||||
|  |       optional: true | ||||||
|  |   bin: | ||||||
|  |     tsx: dist/cli.mjs | ||||||
|  |   checksum: 0338598cc3b7b01a47939297797dfb77a1d675acb33bf71e816faf2b8cb76da3994d341d2920d105dbe98cd01a4babd80ca4b9a5a36120813dd79c4fc1c32df1 | ||||||
|  |   languageName: node | ||||||
|  |   linkType: hard | ||||||
|  | 
 | ||||||
| "tty-browserify@npm:0.0.1, tty-browserify@npm:^0.0.1": | "tty-browserify@npm:0.0.1, tty-browserify@npm:^0.0.1": | ||||||
|   version: 0.0.1 |   version: 0.0.1 | ||||||
|   resolution: "tty-browserify@npm:0.0.1" |   resolution: "tty-browserify@npm:0.0.1" | ||||||
| @@ -46647,6 +46672,7 @@ __metadata: | |||||||
|     tsconfig-paths: "npm:^4.2.0" |     tsconfig-paths: "npm:^4.2.0" | ||||||
|     tslib: "npm:^2.3.0" |     tslib: "npm:^2.3.0" | ||||||
|     tsup: "npm:^8.0.1" |     tsup: "npm:^8.0.1" | ||||||
|  |     tsx: "npm:^4.7.2" | ||||||
|     type-fest: "npm:4.10.1" |     type-fest: "npm:4.10.1" | ||||||
|     typeorm: "npm:^0.3.17" |     typeorm: "npm:^0.3.17" | ||||||
|     typescript: "npm:5.3.3" |     typescript: "npm:5.3.3" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ady Beraud
					Ady Beraud