@@ -3,6 +3,7 @@ import { DequeuedJob, Runner } from "liteque";
3
3
import cron from "node-cron" ;
4
4
import Parser from "rss-parser" ;
5
5
import { buildImpersonatingTRPCClient } from "trpc" ;
6
+ import { z } from "zod" ;
6
7
7
8
import type { ZFeedRequestSchema } from "@hoarder/shared/queues" ;
8
9
import { db } from "@hoarder/db" ;
@@ -123,19 +124,31 @@ async function run(req: DequeuedJob<ZFeedRequestSchema>) {
123
124
item : [ "id" ] ,
124
125
} ,
125
126
} ) ;
126
- const feedData = await parser . parseString ( xmlData ) ;
127
+ const unparseFeedData = await parser . parseString ( xmlData ) ;
128
+
129
+ // Apparently, we can't trust the output of the xml parser. So let's do our own type
130
+ // validation.
131
+ const feedItemsSchema = z . object ( {
132
+ id : z . coerce . string ( ) ,
133
+ link : z . string ( ) . optional ( ) ,
134
+ guid : z . string ( ) . optional ( ) ,
135
+ } ) ;
136
+
137
+ const feedItems = unparseFeedData . items
138
+ . map ( ( i ) => feedItemsSchema . safeParse ( i ) )
139
+ . flatMap ( ( i ) => ( i . success ? [ i . data ] : [ ] ) ) ;
127
140
128
141
logger . info (
129
- `[feed][${ jobId } ] Found ${ feedData . items . length } entries in feed "${ feed . name } " (${ feed . id } ) ...` ,
142
+ `[feed][${ jobId } ] Found ${ feedItems . length } entries in feed "${ feed . name } " (${ feed . id } ) ...` ,
130
143
) ;
131
144
132
- if ( feedData . items . length === 0 ) {
145
+ if ( feedItems . length === 0 ) {
133
146
logger . info ( `[feed][${ jobId } ] No entries found.` ) ;
134
147
return ;
135
148
}
136
149
137
150
// For feeds that don't have guids, use the link as the id
138
- feedData . items . forEach ( ( item ) => {
151
+ feedItems . forEach ( ( item ) => {
139
152
item . guid = item . guid ?? `${ item . id } ` ?? item . link ;
140
153
} ) ;
141
154
@@ -144,14 +157,12 @@ async function run(req: DequeuedJob<ZFeedRequestSchema>) {
144
157
eq ( rssFeedImportsTable . rssFeedId , feed . id ) ,
145
158
inArray (
146
159
rssFeedImportsTable . entryId ,
147
- feedData . items
148
- . map ( ( item ) => item . guid )
149
- . filter ( ( id ) : id is string => ! ! id ) ,
160
+ feedItems . map ( ( item ) => item . guid ) . filter ( ( id ) : id is string => ! ! id ) ,
150
161
) ,
151
162
) ,
152
163
} ) ;
153
164
154
- const newEntries = feedData . items . filter (
165
+ const newEntries = feedItems . filter (
155
166
( item ) =>
156
167
! exitingEntries . some ( ( entry ) => entry . entryId === item . guid ) &&
157
168
item . link &&
0 commit comments