- Incase you got .tars for FE and BE load them using
docker load -i {fileName}and then you can skip to step 4. - Make sure docker is installed and running on your machine.
- In both FE and BE folders run:
npm run docker:build
- Make sure all the environment variables in the
docker-compose.ymlfile are set correctly - Make sure all the configuration files are set correctly.
- Run in the main folder:
docker compose up -d
- Be aware, frontend running on port 4000 and backend on port 5000 (BE port can be set via environment variable PORT).
- Make sure docker is installed and running on your machine.
- In BE folder run:
npm run tar- In FE folder, you need to decide which environment you want to build for, and then run one of the following commands:
npm run tar:{enviornment}All configuration files are located in the config folder inside the public folder of the FE.
These files control app behavior, appearance, and content.
Purpose: Global settings like redirects, routes, maps, and search configuration.
Structure: JSON object with clearly named keys.
How to maintain:
- Use existing key patterns for new routes/settings.
- For values that should be replaced dynamically, use
%%MACRO%%.
Purpose: Central source for UI text (labels, placeholders, messages, tooltips, etc.).
Structure: Flat JSON with key → value pairs.
How to maintain:
- Add new strings as new key-value pairs.
- Use descriptive, consistent keys.
- Avoid hardcoding strings directly in the UI.
Purpose: Configures response colors used for tags and map points.
Structure: Contains two properties:
responses→ mapsresponseId → colorName.colors→ mapscolorName → { background, font }.
How to maintain:
- Add new response IDs under
responses. - Ensure each referenced
colorNamehas a matching entry incolors.
Purpose: Defines all filters used in the app.
Structure:
- Responses: simple
key = response ID,value = display string. - Situations: structured inside
situationMap.- Key = group identifier.
- Value = array of situations belonging to that group.
- Each group also has a title with both its identifier and display name.
How to maintain:
- Add new filters to the appropriate group.
- Keep identifiers unique and consistent.
Purpose: Controls homepage search options.
Structure: Divided into groups. Each group includes:
group(identifier)situation_idgroup_link(for navigation)labels(array of display options)response_id: response backend filtersituation_id: situation backend filtertitle: text displayedquery: search query string, make sure you write with underscore
How to maintain:
- Add a new group for new homepage sections.
- Make sure
labelspoint to validsituation_idandresponse_idvalues.
Purpose: Defines footer links.
Structure: Array of objects with:
title(text displayed)- Either
modal(if opens a modal) orurl(if links externally).
How to maintain:
- Add/remove entries by appending objects.
- Only use one of
modalorurlin each entry.
Purpose: Controls page-specific meta tags for SEO and sharing.
Structure: Object per page, each containing meta fields (e.g. title, description, og:image).
Special feature: Supports dynamic macros like %%serviceDescription%%.
How to maintain:
- Add a new object for new pages.
- Use macros when fields should be dynamically replaced.
Purpose: Defines the modules used in the AddService modal.
Structure: Array of objects (each representing a module).
How to maintain:
- Add a new module as a new object with fields like
title,description, and optionallinks.
Purpose: Defines the default search options (shown before the user types).
Structure: Array of preset objects. Each object becomes a selectable SearchOption.
Required fields per preset:
label(string, displayed text in the list)query(string, the search query executed when selected)
Optional fields:
responseId(string) : Pre-filters results by a specific response taxonomy.situationId(string) : Pre-filters results by a situation taxonomy.cityName(string) : Used for location‑scoped context / display.bounds(array of 4 numbers) : Geographic bounding box[minLon, minLat, maxLon, maxLat].by(string) : Narrows search by provider/organization name.labelHighlighted(string) : Highlights what not marked with<em>in the label. for instance: if label is "Medical services in Jerusalem" and you want to highlight "Jerusalem" you can set labelHighlighted to "Medical services in" and the FE will render it as "Medical services in Jerusalem".
How the FE uses it:
- A preset is treated as "structured" if it has any of:
responseId,situationId,cityName,bounds,by. - The
queryvalue must be unique; it's used as the React list key. Avoid duplicates.
Editing guidelines:
- Keep the array valid JSON (no trailing commas, use
nullinstead of omitting only if you intentionally want an explicit null). - Prefer omitting optional keys rather than setting them to
nullunless downstream logic depends on key existence (current code is fine with both). - If adding geographic bounds, confirm the order: lon/lat pairs, not lat/lon.
- Large coordinate precision is fine; keep 6–7 decimals max for readability.
- Avoid bounds that are too broad or too narrow—they reduce result relevance.
- If both
responseIdandsituationIdare present, the search will be filtered by both (intended narrowing). Only include both when meaningful. - Use
byfor organization / provider scoped presets (e.g. a known NGO name) when you want free text plus a supplier constraint. - Remove an entry to hide it; commenting is not allowed in JSON.
- Order matters: the array order is the display order.
Testing a change locally:
- Edit
public/configs/presets.json. - Reload the page (hard refresh if needed). Because of the
cacheBuster, no additional cache clearing is required. - Verify new preset appears and clicking it triggers the expected structured search.
Example preset object:
{
"bounds": [35.0852011, 31.7096214, 35.2650458, 31.8826655],
"cityName": "ירושלים",
"label": "ציוד רפואי בירושלים",
"labelHighlighted": null,
"query": "ציוד רפואי בירושלים",
"responseId": "human_services:health:medical_supplies",
"situationId": null,
"by": null
}Common pitfalls:
- Using lat/lon instead of lon/lat (will shift map area).
- Duplicate
querycausing React key collisions (only last one effectively renders). - Forgetting to remove a trailing comma after deleting the last item.
Purpose: Environment-specific configuration files.
Structure: Identical across environments — only values differ.
How to maintain:
- Ensure all three contain the same keys.
- Never commit sensitive secrets.
Purpose: Determines which environment the app is currently running on.
Structure: Simple object pointing to stage, production, or local.
How to maintain:
- Keep in sync with environment files.
- This is the single source of truth the frontend actually uses.
- Always validate JSON before committing.
- Follow existing naming conventions.
- Test changes locally (
local.json) before moving to staging or production. - Use macros (
%%MACRO%%) instead of hardcoding values when possible.
The project enriches search results meta tags using synonym data for situations and responses. This allows dynamic, SEO-friendly descriptions and Open Graph tags that reflect the user's query context.
Script location: FE/scripts/updateSynonyms.cjs
What it does:
- Reads all
.csvfiles fromFE/public/synonyms/. - Parses each CSV (expects at least the columns:
id,synonyms,name). - Keeps only rows that have both
idandsynonyms. - Cleans the
synonymsfield (trims surrounding quotes, normalizes embedded quotes). - Writes a JSON file for each CSV into
FE/src/assets/synonyms/with the same base filename (e.g.Situations-synonyms.csv->Situations-synonyms.json).
Automatic execution:
- Runs automatically before every FE build via the
prebuildnpm script ("prebuild": "npm run synonyms:update").
Manual execution:
- From
FE/run:npm run synonyms:update.
Imported in code (e.g. FE/src/pages/results/getResultsMetaTags.ts):
FE/src/assets/synonyms/Situations-synonyms.jsonFE/src/assets/synonyms/Responses-synonyms.json
Source CSVs located at:
FE/public/synonyms/Situations-synonyms.csvFE/public/synonyms/Responses-synonyms.csv
IMPORTANT: Keep CSV filenames EXACT. Renaming them changes the generated JSON names and breaks static imports unless you update the import paths accordingly. Adding new synonym CSVs requires adding corresponding imports/usages.
To modify:
- Edit the CSV under
FE/public/synonyms/. - Ensure headers
id,name,synonymsexist (order unimportant). - Each row: unique
id, readablename. synonymscan be comma-separated phrases; script normalizes quotes.- Save file and run
npm run synonyms:update(or any build).
To add new set:
- Create
FE/public/synonyms/MyNewType-synonyms.csvwith required headers. - Run update script.
- Import
FE/src/assets/synonyms/MyNewType-synonyms.jsonwhere needed (e.g. extend meta tag logic).
- Definitions live in
FE/public/configs/metaTags.jsonusing macros like%%search%%,%%situations%%,%%responses%%,%%location%%. getResultsMetaTags.tsimports the generated JSON, matches active backend filters (situation,response,by), and builds replacement strings for macros.- The
MetaTagscomponent consumes{ metaTags, macrosAndReplacements }to inject<title>and<meta>tags.
- Add logic in
getResultsMetaTags.tsto create a new macro (e.g.%%myMacro%%). - Insert placeholder in
metaTags.jsonfor relevant pages. - Ensure returned
macrosAndReplacementsobject includes a mapping for the macro.
- Run
npm run synonyms:update; confirm new/updated JSON inFE/src/assets/synonyms/. - Start dev server (
npm run dev). - Visit results page; inspect
<head>for expanded meta tags (no raw%%macro%%). - Check browser network panel: JSON synonym files load (no 404).
- Renaming CSV without updating imports → build/runtime import error.
- Missing
idorsynonymscolumn → row omitted. - Trailing spaces in
id→ filter mismatch. - Forgetting to run update script before local test.
Maintain stable CSV filenames and headers. The build transforms CSV synonym data into JSON used to enrich dynamic meta tags, improving SEO/contextual relevance of search result pages.
while using the Hasadna cloud we do not have the possibility to mount volumes and update the mounted files.
- using BE to emulate files and rerouting nginx.conf to use BE as a file server.
- move confings to seperate vpulems (dev, stage, prod) and have the relevant one mounted to the FE container.
- site map have to be generated as part of ETL and mounted to all Environments.
- upload from dev to stage and from stage to prod should be copying the previus level bucket.
The BE of this project is built with Typescript and Node.js, using the Express framework. It is designed to handle requests from the frontend, process data, and interact with the database.
| Variable | Description | Default |
|---|---|---|
| ORIGIN | the front end origin for cors (Need to change default) | * |
| ENV | the environment you working on (prod/stage/local) | local |
| PORT | the port for the back end | 5000 |
| ELASTIC_URL | the elastic search URL (Need to change default) | http://localhost:9200 |
| ELASTIC_USERNAME | the elastic search username (Need to change default) | elastic |
| ELASTIC_PASS | the elastic search password (Need to change default) | your-password |
| ELASTIC_RECONNECT_TIMEOUT | the time to wait before reconnecting to elastic search (seconds) | 5 |
| VERBOSE | Default to false, if true will log more information to the console | false |
| LOG_TO_FILE | Default to false, if true will log to file | false |
| LOG_DURATION | The duration content of each file. (minutes) | 10 |
| SEARCHCARDS_FIRST_LENGTH | The amount of services it will pull initially from server in searchCards | 50 |
| AUTOCOMPLETE_MIN_SCORE | Minimum final score required for autocomplete results (higher = stricter) | 5000 |
Current automated behavior:
- Push to
mainupdates staging (FE + BE independently, only if their folders changed). - Creating a Git tag (and publishing a GitHub Release) triggers production build & deploy (again only for changed component paths).
Simple steps for staging (test) and production (live). No local build needed.
Use when: You want updated code on the staging environment. Steps:
- Ensure your feature branch was merged into
mainvia a Pull Request (PR). - After merge, go to GitHub → Repository → “Actions” tab.
- Look for workflows:
FE CI (staging)(runs ifFE/changed)BE CI (staging)(runs ifbe/changed)
- Open the workflow run (top of the list) and watch the steps. A green check means success.
- When finished, images are pushed to GHCR:
- FE:
ghcr.io/kolzchut/kol-sherut-fe:latestand:<commit-sha> - BE:
ghcr.io/kolzchut/kol-sherut-be:latestand:<commit-sha>
- FE:
- Helm values in
srm-devopsrepo are auto-updated for staging (values.auto-updated.yaml / etl/api & site). - Validate staging:
- Open staging URL (internal) & sanity-check: homepage, search, card page.
- Optional: Open browser dev tools → Network → confirm new commit SHA in
config.jsonrequest (if exposed) or inspect page footer/build indicator if present.
If something is wrong: Fix code → new PR → merge → staging redeploys automatically.
Use when: You approve staging and want to publish to production. Steps:
- Confirm staging is healthy (basic flows OK, no blocking bugs).
- Decide a new semantic version (e.g.
v1.3.0). Do not reuse an existing tag. - GitHub → “Releases” → “Draft a new release”.
- In “Tag version”: type the new tag (e.g.
v1.3.0) and target branch =main. - Title: e.g.
Release v1.3.0. - Description: bullet list of notable changes (copy from merged PR titles if needed).
- Click “Publish release”.
- This triggers:
FE Release (production)(ifFE/changed at/just before the tag)BE Release (production)(ifbe/changed)
- Monitor under “Actions” like staging. Wait for green check.
- Production images produced:
- FE:
ghcr.io/kolzchut/kol-sherut-fe:<commit-sha>and:<tag>-<commit-sha> - BE:
ghcr.io/kolzchut/kol-sherut-be:<commit-sha>and:<tag>-<commit-sha>
- FE:
- Helm production values file (
values.auto-updated.production.yaml) insrm-devopsrepo auto-updated to point to the<tag>-<sha>image. - Validate production: open site, run smoke checks (search, open card, any critical flows).
Rollback (simple):
- Create a new release with an older known-good tag name incremented (e.g. if
v1.3.0bad, re-release code from earlier commit asv1.3.1pointing to stable commit). The workflow will publish new images and update Helm. - OR (DevOps only) manually adjust image in production Helm values repo and deploy.
| Action | Folder Changed? | Workflow | Environment | History Tag Examples |
|---|---|---|---|---|
| Push to main | FE/ changed | FE CI (staging) | Staging | latest + |
| Push to main | be/ changed | BE CI (staging) | Staging | latest + |
| Tag (e.g. v1.2.0) | FE/ changed | FE Release (production) | Production | v1.2.0 |
| Tag (e.g. v1.2.0) | be/ changed | BE Release (production) | Production | v1.2.0 |
- Use
vMAJOR.MINOR.PATCH(e.g.v1.4.2). - Increment:
- MAJOR: breaking changes
- MINOR: new features (backward compatible)
- PATCH: fixes / small changes
- Never reuse or delete tags.
- Only components whose folders changed since last commit/tag run build & publish.
- Sitemaps are generated during FE workflows (stage vs production use environment variable
ENVIRONMENT).
Q: I pushed to main, but no FE workflow?
A: No changes under FE/ → workflow won’t run. Same for be/.
Q: Release created but FE didn’t deploy?
A: Tag commit had no FE changes relative to previous; only BE (or neither) deployed.
Q: Can I edit a release note after publishing?
A: Yes. Editing text doesn’t redeploy. To redeploy you must create a new tag.
Q: How do I know which image is live?
A: Check the updated values.auto-updated*.yaml in srm-devops repo (image field) or visiting argoCD.
Q: I can't find the correct json config file, where is it?
A: The config files are located in the public/configs folder of the FE project, but some of them, that are not frequently changed, are located in the src/assets folder of the FE project.
- All intended PRs merged to main
- Staging smoke tests pass
- Version chosen (unique tag)
- Release notes written
- No open critical issues
- Homepage loads
- Search returns results
- Card page loads
- (If changed) New sitemap served
- Analytics events visible (if applicable)