@@ -3,13 +3,13 @@ import { HttpService } from "@nestjs/axios";
3
3
import { Resvg } from "@resvg/resvg-js" ;
4
4
import { Repository , Language } from "@octokit/graphql-schema" ;
5
5
import fs from "node:fs/promises" ;
6
+ import { firstValueFrom } from "rxjs" ;
6
7
7
8
import { GithubService } from "../../github/github.service" ;
8
9
import { S3FileStorageService } from "../../s3-file-storage/s3-file-storage.service" ;
9
10
import userLangs from "../templates/shared/user-langs" ;
10
11
import userProfileRepos from "../templates/shared/user-repos" ;
11
12
import tailwindConfig from "../templates/tailwind.config" ;
12
- import { firstValueFrom } from "rxjs" ;
13
13
import highlightCardTemplate from "../templates/highlight-card.template" ;
14
14
import { DbUserHighlight } from "../../github/entities/db-user-highlight.entity" ;
15
15
import { DbReaction } from "../../github/entities/db-reaction.entity" ;
@@ -32,6 +32,7 @@ interface HighlightCardData {
32
32
@Injectable ( )
33
33
export class HighlightCardService {
34
34
private readonly logger = new Logger ( this . constructor . name ) ;
35
+ private fonts : Buffer [ ] = [ ] ;
35
36
36
37
constructor (
37
38
private readonly httpService : HttpService ,
@@ -40,20 +41,20 @@ export class HighlightCardService {
40
41
) { }
41
42
42
43
private async getHighlightData ( highlightId : number ) : Promise < HighlightCardData > {
43
- const highlightReq = await firstValueFrom (
44
- this . httpService . get < DbUserHighlight > ( `https://api.opensauced.pizza /v1/user/highlights/${ highlightId } ` ) ,
44
+ const highlightReq = firstValueFrom (
45
+ this . httpService . get < DbUserHighlight > ( `${ process . env . API_BASE_URL ! } /v1/user/highlights/${ highlightId } ` ) ,
45
46
) ;
46
- const { login, updated_at, url, highlight : body } = highlightReq . data ;
47
47
48
- const reactionsReq = await firstValueFrom (
49
- this . httpService . get < DbReaction [ ] > ( `https://api.opensauced.pizza /v1/highlights/${ highlightId } /reactions` ) ,
48
+ const reactionsReq = firstValueFrom (
49
+ this . httpService . get < DbReaction [ ] > ( `${ process . env . API_BASE_URL ! } /v1/highlights/${ highlightId } /reactions` ) ,
50
50
) ;
51
- const reactions = reactionsReq . data . reduce < number > ( ( acc , curr ) => acc + Number ( curr . reaction_count ) , 0 ) ;
52
51
52
+ const [ highlight , highlightReactions ] = await Promise . all ( [ highlightReq , reactionsReq ] ) ;
53
+ const { login, updated_at, url, highlight : body } = highlight . data ;
53
54
const [ owner , repoName ] = url . replace ( "https://github.com/" , "" ) . split ( "/" ) ;
54
55
55
- const user = await this . githubService . getUser ( login ) ;
56
56
const repo = await this . githubService . getRepo ( owner , repoName ) ;
57
+ const reactions = highlightReactions . data . reduce < number > ( ( acc , curr ) => acc + Number ( curr . reaction_count ) , 0 ) ;
57
58
58
59
const langList = repo . languages ?. edges ?. flatMap ( edge => {
59
60
if ( edge ) {
@@ -68,7 +69,7 @@ export class HighlightCardService {
68
69
body,
69
70
login,
70
71
reactions,
71
- avatarUrl : `${ String ( user . avatarUrl ) } & size=150` ,
72
+ avatarUrl : `https://github.com/ ${ login } .png? size=150` ,
72
73
langs : langList ,
73
74
langTotal : repo . languages ?. totalSize ?? 0 ,
74
75
repo,
@@ -77,6 +78,17 @@ export class HighlightCardService {
77
78
} ;
78
79
}
79
80
81
+ private async getFonts ( ) {
82
+ if ( this . fonts . length === 0 ) {
83
+ const interArrayBufferReq = fs . readFile ( "node_modules/@fontsource/inter/files/inter-all-400-normal.woff" ) ;
84
+ const interArrayBufferMediumReq = fs . readFile ( "node_modules/@fontsource/inter/files/inter-all-500-normal.woff" ) ;
85
+
86
+ this . fonts = await Promise . all ( [ interArrayBufferReq , interArrayBufferMediumReq ] ) ;
87
+ }
88
+
89
+ return this . fonts ;
90
+ }
91
+
80
92
// public only to be used in local scripts. Not for controller direct use.
81
93
async generateCardBuffer ( highlightId : number , highlightData ?: HighlightCardData ) {
82
94
const { html } = await import ( "satori-html" ) ;
@@ -90,8 +102,7 @@ export class HighlightCardService {
90
102
highlightCardTemplate ( avatarUrl , body , userLangs ( langs , langTotal ) , userProfileRepos ( [ repo ] , 2 ) , reactions ) ,
91
103
) ;
92
104
93
- const interArrayBuffer = await fs . readFile ( "node_modules/@fontsource/inter/files/inter-all-400-normal.woff" ) ;
94
- const interArrayBufferMedium = await fs . readFile ( "node_modules/@fontsource/inter/files/inter-all-500-normal.woff" ) ;
105
+ const [ interArrayBuffer , interArrayBufferMedium ] = await this . getFonts ( ) ;
95
106
96
107
const svg = await satori ( template , {
97
108
width : 1200 ,
@@ -133,12 +144,16 @@ export class HighlightCardService {
133
144
} ;
134
145
135
146
if ( hasFile ) {
136
- const lastModified = await this . s3FileStorageService . getFileLastModified ( hash ) ;
147
+ const lastModifiedReq = this . s3FileStorageService . getFileLastModified ( hash ) ;
148
+ const highlightReq = this . getHighlightData ( id ) ;
149
+ const metadataReq = this . s3FileStorageService . getFileMeta ( hash ) ;
150
+
151
+ const [ lastModified , highlight , metadata ] = await Promise . all ( [ lastModifiedReq , highlightReq , metadataReq ] ) ;
137
152
138
153
returnVal . lastModified = lastModified ;
139
154
140
- const { updated_at, reactions } = await this . getHighlightData ( id ) ;
141
- const metadata = await this . s3FileStorageService . getFileMeta ( hash ) ;
155
+ const { updated_at, reactions } = highlight ;
156
+
142
157
const savedReactions = metadata ?. [ "reactions-count" ] ?? "0" ;
143
158
144
159
if ( lastModified && lastModified > updated_at && savedReactions === String ( reactions ) ) {
@@ -152,7 +167,7 @@ export class HighlightCardService {
152
167
return returnVal ;
153
168
}
154
169
155
- async getHighlightCard ( id : number ) : Promise < string > {
170
+ async getHighlightCard ( id : number ) : Promise < Buffer > {
156
171
const { remaining } = await this . githubService . rateLimit ( ) ;
157
172
158
173
if ( remaining < 1000 ) {
@@ -162,16 +177,9 @@ export class HighlightCardService {
162
177
const highlightData = await this . getHighlightData ( id ) ;
163
178
164
179
try {
165
- const hash = `highlights/${ String ( id ) } .png` ;
166
- const fileUrl = `${ this . s3FileStorageService . getCdnEndpoint ( ) } ${ hash } ` ;
167
-
168
180
const { png } = await this . generateCardBuffer ( id , highlightData ) ;
169
181
170
- await this . s3FileStorageService . uploadFile ( png , hash , "image/png" , { "reactions-count" : String ( highlightData . reactions ) } ) ;
171
-
172
- this . logger . debug ( `Highlight ${ id } did not exist in S3, generated image and uploaded to S3, redirecting` ) ;
173
-
174
- return fileUrl ;
182
+ return png ;
175
183
} catch ( e ) {
176
184
this . logger . error ( `Error generating highlight card for ${ id } ` , e ) ;
177
185
0 commit comments