Skip to content

Commit d957d64

Browse files
committed
Updated setup for the search bar
1 parent 8aa7918 commit d957d64

6 files changed

Lines changed: 176 additions & 51 deletions

File tree

.env.example

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ TYPESENSE_COLLECTION_NAME=training-wiki
77

88
TYPESENSE_HOST=localhost
99
TYPESENSE_PORT=8108
10-
TYPESENSE_PROTOCOL=https
10+
TYPESENSE_PROTOCOL=http
1111

12-
TYPESENSE_SEARCH_API_KEY=replace-with-a-search-only-key
12+
TYPESENSE_ADMIN_API_KEY=xyz
13+
14+
TYPESENSE_SEARCH_API_KEY=xyz

.github/workflows/ci-deploy.yml

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,26 @@ on:
1212
- docker-compose.yml
1313
- docker-entrypoint.sh
1414
- .dockerignore
15+
- docsearch.config.json
16+
- .env.example
17+
- README.md
18+
- LICENSE
1519

1620
concurrency:
1721
group: deploy-${{ github.ref }}
1822
cancel-in-progress: true
1923

2024
jobs:
21-
changes:
22-
name: Detect changes
23-
runs-on: ubuntu-latest
24-
outputs:
25-
docker_changed: ${{ steps.filter.outputs.docker }}
26-
27-
steps:
28-
- name: Checkout
29-
uses: actions/checkout@v4
30-
31-
- name: Paths filter
32-
id: filter
33-
uses: dorny/paths-filter@v3
34-
with:
35-
filters: |
36-
docker:
37-
- 'Dockerfile'
38-
- 'docker-compose.yml'
39-
- 'docker-entrypoint.sh'
40-
- '.dockerignore'
41-
4225
build:
43-
needs: changes
44-
if: ${{ needs.changes.outputs.docker_changed != 'true' }}
4526
runs-on: ubuntu-latest
4627

28+
env:
29+
TYPESENSE_COLLECTION_NAME: ${{ secrets.TYPESENSE_COLLECTION_NAME }}
30+
TYPESENSE_HOST: ${{ secrets.TYPESENSE_HOST }}
31+
TYPESENSE_PORT: ${{ secrets.TYPESENSE_PORT }}
32+
TYPESENSE_PROTOCOL: ${{ secrets.TYPESENSE_PROTOCOL }}
33+
TYPESENSE_SEARCH_API_KEY: ${{ secrets.TYPESENSE_SEARCH_API_KEY }}
34+
4735
steps:
4836
- name: Checkout
4937
uses: actions/checkout@v4
@@ -65,7 +53,6 @@ jobs:
6553

6654
deploy:
6755
needs: build
68-
if: ${{ needs.build.result == 'success' }}
6956
runs-on: ubuntu-latest
7057

7158
steps:
@@ -86,15 +73,24 @@ jobs:
8673

8774
- name: Build on server (npm)
8875
uses: appleboy/ssh-action@v1.2.0
76+
env:
77+
TYPESENSE_COLLECTION_NAME: ${{ secrets.TYPESENSE_COLLECTION_NAME }}
78+
TYPESENSE_HOST: ${{ secrets.TYPESENSE_HOST }}
79+
TYPESENSE_PORT: ${{ secrets.TYPESENSE_PORT }}
80+
TYPESENSE_PROTOCOL: ${{ secrets.TYPESENSE_PROTOCOL }}
81+
TYPESENSE_SEARCH_API_KEY: ${{ secrets.TYPESENSE_SEARCH_API_KEY }}
8982
with:
9083
host: ${{ secrets.SSH_HOST }}
9184
port: 6007
9285
username: root
9386
password: ${{ secrets.ROOT_PASSWORD }}
87+
envs: TYPESENSE_COLLECTION_NAME,TYPESENSE_HOST,TYPESENSE_PORT,TYPESENSE_PROTOCOL,TYPESENSE_SEARCH_API_KEY
9488
script_stop: true
9589
command_timeout: 30m
9690
script: |
9791
set -euo pipefail
9892
cd /var/www/html
9993
npm ci
10094
npm run build
95+
touch /trigger/run
96+
echo "Scraper trigger set — re-index will start within seconds"

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
Wiki/knowledge base built with [Docusaurus](https://docusaurus.io/) with search powered by Typesense DocSearch.
88

9-
Prerequisites: Node.js >= 20, npm. For deployment: Docker + Docker Compose.
10-
119
## Local setup
1210

1311
```bash
@@ -25,4 +23,25 @@ npm run start
2523
docker compose up -d --build
2624
```
2725

28-
Note: `TYPESENSE_*` variables are read **at build time**; if you change them, you must rebuild (`--build`).
26+
3) Create the search-only API key (once):
27+
28+
```bash
29+
docker compose --profile init run --rm typesense-init
30+
```
31+
32+
4) Index the docs into Typesense (required for getting results):
33+
34+
```bash
35+
docker compose --profile scraper run --rm scraper
36+
```
37+
38+
Optional: keep a watcher running and trigger re-index when needed:
39+
40+
```bash
41+
docker compose up -d scraper-watcher
42+
43+
# trigger a re-index
44+
docker compose exec wiki sh -lc 'touch /trigger/run'
45+
```
46+
47+
Note: `TYPESENSE_*` variables are read **at build time**; if you change them, rebuild (`--build`).

docker-compose.yml

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
1+
x-scraper-common: &scraper-common
2+
image: typesense/docsearch-scraper:latest
3+
depends_on:
4+
- typesense
5+
- wiki
6+
environment:
7+
- TYPESENSE_API_KEY=${TYPESENSE_ADMIN_API_KEY:-xyz}
8+
- TYPESENSE_HOST=typesense
9+
- TYPESENSE_PORT=8108
10+
- TYPESENSE_PROTOCOL=http
11+
- CONFIG=/tmp/docsearch/docsearch.config.json
12+
volumes:
13+
- ./docsearch.config.json:/tmp/docsearch/docsearch.config.json:ro
14+
115
services:
216
typesense:
3-
image: typesense/typesense:0.25.2
17+
image: typesense/typesense:30.2
418
container_name: typesense
519
ports:
620
- "127.0.0.1:8108:8108"
721
volumes:
822
- typesense-data:/data
923
command: >-
1024
--data-dir=/data
11-
--api-key=${TYPESENSE_SEARCH_API_KEY:-xyz}
25+
--api-key=${TYPESENSE_ADMIN_API_KEY:-xyz}
1226
--enable-cors
1327
28+
typesense-init:
29+
image: curlimages/curl:8.8.0
30+
profiles: ["init"]
31+
depends_on:
32+
- typesense
33+
entrypoint: ["/bin/sh", "-c"]
34+
environment:
35+
- TYPESENSE_ADMIN_API_KEY=${TYPESENSE_ADMIN_API_KEY:-xyz}
36+
- TYPESENSE_SEARCH_API_KEY=${TYPESENSE_SEARCH_API_KEY:-xyz}
37+
- TYPESENSE_COLLECTION_NAME=${TYPESENSE_COLLECTION_NAME:-training-wiki}
38+
command: >-
39+
until curl -sf http://typesense:8108/health >/dev/null; do sleep 1; done;
40+
curl -sf -X POST http://typesense:8108/keys \
41+
-H "X-TYPESENSE-API-KEY: $$TYPESENSE_ADMIN_API_KEY" \
42+
-H "Content-Type: application/json" \
43+
-d "{\"description\":\"training-wiki search key\",\"actions\":[\"documents:search\"],\"collections\":[\"$$TYPESENSE_COLLECTION_NAME.*\"],\"value\":\"$$TYPESENSE_SEARCH_API_KEY\"}" \
44+
>/dev/null || true
45+
1446
wiki:
1547
build:
1648
context: .
@@ -26,6 +58,8 @@ services:
2658
ports:
2759
- "127.0.0.1:1337:3000"
2860
- "6007:22"
61+
volumes:
62+
- scraper-trigger:/trigger
2963
environment:
3064
- ROOT_PASSWORD=${ROOT_PASSWORD}
3165
- TYPESENSE_COLLECTION_NAME=${TYPESENSE_COLLECTION_NAME:-training-wiki}
@@ -34,5 +68,31 @@ services:
3468
- TYPESENSE_PROTOCOL=${TYPESENSE_PROTOCOL:-http}
3569
- TYPESENSE_SEARCH_API_KEY=${TYPESENSE_SEARCH_API_KEY:-xyz}
3670

71+
scraper:
72+
<<: *scraper-common
73+
profiles: ["scraper"]
74+
75+
scraper-watcher:
76+
<<: *scraper-common
77+
restart: unless-stopped
78+
volumes:
79+
- ./docsearch.config.json:/tmp/docsearch/docsearch.config.json:ro
80+
- scraper-trigger:/trigger
81+
entrypoint: ["/bin/sh", "-c"]
82+
command: >-
83+
set -eu;
84+
trigger="/trigger/run";
85+
echo "[scraper-watcher] waiting for trigger...";
86+
while true; do
87+
if [ -f "$$trigger" ]; then
88+
rm -f "$$trigger";
89+
echo "[scraper-watcher] trigger detected, scraping...";
90+
pipenv run python -m src.index || echo "[scraper-watcher] scrape failed";
91+
echo "[scraper-watcher] done, waiting for next trigger...";
92+
fi;
93+
sleep 5;
94+
done
95+
3796
volumes:
3897
typesense-data:
98+
scraper-trigger:

docsearch.config.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"index_name": "training-wiki",
3+
"start_urls": [
4+
{
5+
"url": "http://wiki:3000/"
6+
}
7+
],
8+
"sitemap_urls": [
9+
"http://wiki:3000/sitemap.xml"
10+
],
11+
"sitemap_alternate_links": true,
12+
"selectors": {
13+
"lvl0": {
14+
"selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class,'navbar__link--active')]/text())[last()]",
15+
"type": "xpath",
16+
"global": true,
17+
"default_value": "Documentation"
18+
},
19+
"lvl1": "article h1, header h1",
20+
"lvl2": "article h2",
21+
"lvl3": "article h3",
22+
"lvl4": "article h4",
23+
"lvl5": "article h5",
24+
"lvl6": "article h6",
25+
"text": "article p, article li, article td:last-child"
26+
},
27+
"strip_chars": " .,;:#",
28+
"custom_settings": {
29+
"separatorsToIndex": "_",
30+
"attributesForFaceting": ["language", "version", "type", "docusaurus_tag"],
31+
"attributesToRetrieve": [
32+
"hierarchy",
33+
"content",
34+
"anchor",
35+
"url",
36+
"url_without_anchor",
37+
"type"
38+
]
39+
}
40+
}

docusaurus.config.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import type * as Preset from '@docusaurus/preset-classic';
44

55
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
66

7+
const typesenseEnabled = Boolean(
8+
process.env.TYPESENSE_HOST && process.env.TYPESENSE_SEARCH_API_KEY,
9+
);
10+
711
const config: Config = {
812
title: 'PascalCTF Wiki',
913
tagline: 'A wiki made for beginners to learn CTF and cybersecurity',
1014
favicon: 'img/favicon.ico',
1115

12-
themes: ['docusaurus-theme-search-typesense'],
16+
themes: typesenseEnabled ? ['docusaurus-theme-search-typesense'] : [],
1317

1418
// Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future
1519
future: {
@@ -62,26 +66,30 @@ const config: Config = {
6266
colorMode: {
6367
respectPrefersColorScheme: true,
6468
},
65-
typesense: {
66-
// Must match `index_name` in the scraper config.
67-
typesenseCollectionName:
68-
process.env.TYPESENSE_COLLECTION_NAME ?? 'training-wiki',
69+
...(typesenseEnabled
70+
? {
71+
typesense: {
72+
// Must match `index_name` in the scraper config.
73+
typesenseCollectionName:
74+
process.env.TYPESENSE_COLLECTION_NAME ?? 'training-wiki',
6975

70-
typesenseServerConfig: {
71-
nodes: [
72-
{
73-
host: process.env.TYPESENSE_HOST ?? 'localhost',
74-
port: Number(process.env.TYPESENSE_PORT ?? '8108'),
75-
protocol: (process.env.TYPESENSE_PROTOCOL ?? 'http') as
76-
| 'http'
77-
| 'https',
76+
typesenseServerConfig: {
77+
nodes: [
78+
{
79+
host: process.env.TYPESENSE_HOST!,
80+
port: Number(process.env.TYPESENSE_PORT ?? '8108'),
81+
protocol: (process.env.TYPESENSE_PROTOCOL ?? 'http') as
82+
| 'http'
83+
| 'https',
84+
},
85+
],
86+
apiKey: process.env.TYPESENSE_SEARCH_API_KEY!,
87+
},
88+
typesenseSearchParameters: {},
89+
contextualSearch: true,
7890
},
79-
],
80-
apiKey: process.env.TYPESENSE_SEARCH_API_KEY ?? 'xyz',
81-
},
82-
typesenseSearchParameters: {},
83-
contextualSearch: true,
84-
},
91+
}
92+
: {}),
8593
navbar: {
8694
title: 'Training Wiki',
8795
logo: {
@@ -179,7 +187,7 @@ const config: Config = {
179187
darkTheme: prismThemes.dracula,
180188
},
181189
} satisfies (Preset.ThemeConfig & {
182-
typesense: {
190+
typesense?: {
183191
typesenseCollectionName: string;
184192
typesenseServerConfig: {
185193
nodes: Array<{host: string; port: number; protocol: 'http' | 'https'}>;

0 commit comments

Comments
 (0)