Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
U
upgrade-data-crawler-be
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ThinhNC
upgrade-data-crawler-be
Commits
c917a34b
Commit
c917a34b
authored
Sep 06, 2026
by
ThinhNC
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: crawl job repository, server initialization, health service, and database seed script
parent
b5e2e132
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
490 additions
and
41 deletions
+490
-41
seed.ts
prisma/seed.ts
+391
-2
crawl-job.repository.ts
src/modules/crawl-jobs/crawl-job.repository.ts
+31
-3
health.service.ts
src/modules/health/health.service.ts
+28
-4
server.ts
src/server.ts
+40
-32
No files found.
prisma/seed.ts
View file @
c917a34b
This diff is collapsed.
Click to expand it.
src/modules/crawl-jobs/crawl-job.repository.ts
View file @
c917a34b
...
...
@@ -49,9 +49,31 @@ export class CrawlJobRepository {
where
.
mode
=
query
.
mode
;
}
if
(
query
.
search
)
{
const
trimmedSearch
=
query
.
search
.
trim
();
let
matchingIds
:
string
[]
=
[];
try
{
const
searchPattern
=
`%
${
trimmedSearch
}
%`
;
const
matched
=
await
prisma
.
$queryRaw
<
{
id
:
string
}[]
>
`
SELECT id FROM "crawl_jobs"
WHERE id::text ILIKE
${
searchPattern
}
LIMIT 100
`
;
matchingIds
=
matched
.
map
((
r
)
=>
r
.
id
);
}
catch
{
const
isFullUuid
=
/^
[
0-9a-fA-F
]{8}
-
[
0-9a-fA-F
]{4}
-
[
0-9a-fA-F
]{4}
-
[
0-9a-fA-F
]{4}
-
[
0-9a-fA-F
]{12}
$/
.
test
(
trimmedSearch
,
);
if
(
isFullUuid
)
{
matchingIds
=
[
trimmedSearch
];
}
}
where
.
OR
=
[
{
startUrl
:
{
contains
:
query
.
search
,
mode
:
"insensitive"
}
},
{
domain
:
{
contains
:
query
.
search
,
mode
:
"insensitive"
}
},
{
startUrl
:
{
contains
:
trimmedSearch
,
mode
:
"insensitive"
}
},
{
domain
:
{
contains
:
trimmedSearch
,
mode
:
"insensitive"
}
},
...(
matchingIds
.
length
>
0
?
[{
id
:
{
in
:
matchingIds
}
}]
:
[]),
];
}
...
...
@@ -113,13 +135,19 @@ export class CrawlJobRepository {
prisma
.
crawlJob
.
count
({
where
}),
]);
const
totalPages
=
Math
.
max
(
1
,
Math
.
ceil
(
total
/
limit
));
return
{
items
,
total
,
page
,
limit
,
pageSize
:
limit
,
totalPages
,
meta
:
{
total
,
page
,
limit
,
totalPages
:
Math
.
ceil
(
total
/
limit
)
,
totalPages
,
},
};
}
...
...
src/modules/health/health.service.ts
View file @
c917a34b
...
...
@@ -51,12 +51,22 @@ export class HealthService {
if
(
crawlQueue
)
{
const
redisStart
=
Date
.
now
();
try
{
const
client
=
await
crawlQueue
.
client
;
const
client
=
await
Promise
.
race
([
crawlQueue
.
client
,
new
Promise
<
never
>
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Redis connection timeout"
)),
1500
)
),
]);
if
(
"ping"
in
client
&&
typeof
(
client
as
{
ping
:
()
=>
Promise
<
string
>
}).
ping
===
"function"
)
{
await
(
client
as
{
ping
:
()
=>
Promise
<
string
>
}).
ping
();
await
Promise
.
race
([
(
client
as
{
ping
:
()
=>
Promise
<
string
>
}).
ping
(),
new
Promise
<
never
>
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Redis ping timeout"
)),
1500
)
),
]);
}
checks
.
redis
=
{
status
:
"up"
,
...
...
@@ -88,12 +98,19 @@ export class HealthService {
if
(
crawlQueue
)
{
try
{
const
[
waiting
,
active
,
completed
,
failed
]
=
await
Promise
.
all
([
const
metricsPromise
=
Promise
.
all
([
crawlQueue
.
getWaitingCount
(),
crawlQueue
.
getActiveCount
(),
crawlQueue
.
getCompletedCount
(),
crawlQueue
.
getFailedCount
(),
]);
const
timeoutPromise
=
new
Promise
<
never
>
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Queue metrics timeout"
)),
1500
)
);
const
[
waiting
,
active
,
completed
,
failed
]
=
await
Promise
.
race
([
metricsPromise
,
timeoutPromise
,
]);
crawlQueueMetrics
=
{
waiting
,
active
,
completed
,
failed
};
}
catch
{
crawlQueueMetrics
=
"unavailable"
;
...
...
@@ -102,12 +119,19 @@ export class HealthService {
if
(
webhookQueue
)
{
try
{
const
[
waiting
,
active
,
completed
,
failed
]
=
await
Promise
.
all
([
const
metricsPromise
=
Promise
.
all
([
webhookQueue
.
getWaitingCount
(),
webhookQueue
.
getActiveCount
(),
webhookQueue
.
getCompletedCount
(),
webhookQueue
.
getFailedCount
(),
]);
const
timeoutPromise
=
new
Promise
<
never
>
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Webhook queue metrics timeout"
)),
1500
)
);
const
[
waiting
,
active
,
completed
,
failed
]
=
await
Promise
.
race
([
metricsPromise
,
timeoutPromise
,
]);
webhookQueueMetrics
=
{
waiting
,
active
,
completed
,
failed
};
}
catch
{
webhookQueueMetrics
=
"unavailable"
;
...
...
src/server.ts
View file @
c917a34b
...
...
@@ -3,48 +3,56 @@ import { envConfig } from "./config/env.config";
import
Redis
from
"ioredis"
;
async
function
bootstrap
()
{
if
(
!
envConfig
.
redis
.
enabled
)
{
console
.
error
(
"[Server] REDIS_ENABLED is not set to true. Redis is required."
,
);
console
.
error
(
"[Server] Start Docker and set REDIS_ENABLED=true in .env, then try again."
,
);
process
.
exit
(
1
);
}
let
isRedisAvailable
=
false
;
if
(
envConfig
.
redis
.
enabled
)
{
const
redis
=
new
Redis
({
host
:
envConfig
.
redis
.
host
,
port
:
envConfig
.
redis
.
port
,
maxRetriesPerRequest
:
0
,
lazyConnect
:
true
,
connectTimeout
:
1500
,
retryStrategy
:
()
=>
null
,
enableOfflineQueue
:
false
,
});
const
redis
=
new
Redis
({
host
:
envConfig
.
redis
.
host
,
port
:
envConfig
.
redis
.
port
,
maxRetriesPerRequest
:
0
,
lazyConnect
:
true
,
});
redis
.
on
(
"error"
,
()
=>
{});
redis
.
on
(
"error"
,
()
=>
{});
try
{
await
redis
.
connect
();
await
redis
.
ping
();
await
redis
.
quit
();
console
.
log
(
"[Server] Redis connection confirmed."
);
}
catch
{
console
.
error
(
"[Server] Cannot connect to Redis. Is Docker running?"
);
console
.
error
(
"[Server] Run: docker compose up -d"
);
process
.
exit
(
1
);
try
{
await
Promise
.
race
([
redis
.
connect
(),
new
Promise
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Redis connection timeout"
)),
1500
)),
]);
await
Promise
.
race
([
redis
.
ping
(),
new
Promise
((
_
,
reject
)
=>
setTimeout
(()
=>
reject
(
new
Error
(
"Redis ping timeout"
)),
1500
)),
]);
await
redis
.
quit
();
isRedisAvailable
=
true
;
console
.
log
(
"[Server] Redis connection confirmed."
);
}
catch
{
try
{
redis
.
disconnect
();
}
catch
{}
console
.
warn
(
"[Server] Redis is offline. Running in degraded mode without queue workers (Database & APIs active)."
);
}
}
else
{
console
.
warn
(
"[Server] REDIS_ENABLED is false. Running in degraded mode without queue workers (Database & APIs active)."
);
}
//
Only import app AFTER Redis is confirmed — this delays crawlQueue instantiation
//
Import app so Database endpoints and Express routes are fully available
const
{
default
:
app
}
=
await
import
(
"./app"
);
const
{
initLocalStorage
}
=
await
import
(
"./common/helpers/file.helper"
);
initLocalStorage
();
await
import
(
"./queues/webhook.worker"
);
console
.
log
(
"[Server] Webhook worker initialized in background."
);
if
(
isRedisAvailable
)
{
await
import
(
"./queues/webhook.worker"
);
console
.
log
(
"[Server] Webhook worker initialized in background."
);
const
{
startScheduleWorker
}
=
await
import
(
"./queues/schedule.worker"
);
startScheduleWorker
();
console
.
log
(
"[Server] Schedule worker initialized in background."
);
const
{
startScheduleWorker
}
=
await
import
(
"./queues/schedule.worker"
);
startScheduleWorker
();
console
.
log
(
"[Server] Schedule worker initialized in background."
);
}
app
.
listen
(
envConfig
.
port
,
()
=>
{
console
.
log
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment