├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
├── dependabot.yml
└── workflows
│ ├── build-and-push.yml
│ ├── check-releases.yml
│ └── lint.yml
├── .last_release
├── Dockerfile
├── LICENSE
├── README.md
├── docker-compose.build.yml
├── docker-compose.dev.yml
├── docker-compose.yml
├── src
├── config.sh
├── logging.sh
└── process.sh
└── start-photon.sh
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: rtuszik
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | ### **`photon-docker` configuration:**
20 | Please add your docker compose and environment variables.
21 |
22 | ### System Info
23 | - **Host OS:**
24 | - **Host Type:** [e.g., Bare-metal, LXC, VM, Synology]
25 | - **Hardware details:**
26 | - **CPU:** [e.g., Intel Core i7-9700K]
27 | - **Available RAM:**
28 | - **Storage Type:** [e.g., SSD, NVME, NFS, SAMBA]
29 | - **Storage Size:**
30 |
31 |
32 | **Debug Logs**
33 | Please provide any relevant logs. To get more detailed logs, you can set the `LOG_LEVEL` environment variable to `DEBUG` in your `docker-compose.yml` file.
34 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
8 | - package-ecosystem: "docker"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-push.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish Docker Image
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | release_tag:
7 | description: "Release version to build"
8 | required: true
9 | revision:
10 | description: "Revision number (optional)"
11 | required: false
12 |
13 | jobs:
14 | build-and-push:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout Repository
18 | uses: actions/checkout@v4
19 |
20 | - name: Set up Docker Buildx
21 | uses: docker/setup-buildx-action@v3
22 |
23 | - name: Login to DockerHub
24 | uses: docker/login-action@v3
25 | with:
26 | username: ${{ secrets.DOCKER_USERNAME }}
27 | password: ${{ secrets.DOCKER_PASSWORD }}
28 |
29 | - name: Login to GitHub Container Registry
30 | uses: docker/login-action@v3
31 | with:
32 | registry: ghcr.io
33 | username: ${{ github.repository_owner }}
34 | password: ${{ secrets.GITHUB_TOKEN }}
35 |
36 | - name: Determine tag
37 | id: determine_tag
38 | run: |
39 | revision="${{ github.event.inputs.revision }}"
40 | if [ "$revision" ]; then
41 | echo "RELEASE_TAG=${{ github.event.inputs.release_tag }}-rev.$revision" >> "$GITHUB_ENV"
42 | else
43 | echo "RELEASE_TAG=${{ github.event.inputs.release_tag }}" >> "$GITHUB_ENV"
44 | fi
45 | echo "PHOTON_VERSION=${{ github.event.inputs.release_tag }}" >> "$GITHUB_ENV"
46 |
47 | - name: Build and push Docker image
48 | uses: docker/build-push-action@v6
49 | with:
50 | build-args: |
51 | PHOTON_VERSION=${{ env.PHOTON_VERSION }}
52 | push: true
53 | tags: |
54 | rtuszik/photon-docker:${{ env.RELEASE_TAG }}
55 | rtuszik/photon-docker:latest
56 | ghcr.io/rtuszik/photon-docker:${{ env.RELEASE_TAG }}
57 | ghcr.io/rtuszik/photon-docker:latest
58 | platforms: linux/amd64,linux/arm64
59 |
60 | - name: Store the latest release
61 | run: echo ${{ env.RELEASE_TAG }} > .last_release
62 | if: success()
63 | continue-on-error: false
64 |
--------------------------------------------------------------------------------
/.github/workflows/check-releases.yml:
--------------------------------------------------------------------------------
1 | name: Check for New Releases and Propose Update
2 |
3 | on:
4 | schedule:
5 | - cron: "0 * * * *"
6 | workflow_dispatch:
7 |
8 | jobs:
9 | check_and_propose_update:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | pull-requests: write
14 |
15 | steps:
16 | - name: Checkout Repository
17 | uses: actions/checkout@v4
18 |
19 | - name: Check for new Photon release
20 | id: check_release
21 | run: |
22 | latest_release=$(curl -s https://github.com/komoot/photon/releases.atom | grep '
' | sed -n '2p' | sed -E 's/.*Release ([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
23 | if [ -z "$latest_release" ]; then
24 | echo "Error: Failed to fetch the latest Photon release version."
25 | exit 1
26 | else
27 | echo "Latest Photon release version: $latest_release"
28 | echo "latest_release_version=${latest_release}" >> "$GITHUB_ENV"
29 | fi
30 |
31 | - name: Get last processed release from file
32 | id: get_last_release
33 | run: |
34 | if [ -f .last_release ]; then
35 | current_version_in_file=$(cat .last_release)
36 | echo "Current version in .last_release file: $current_version_in_file"
37 | echo "last_processed_version=$current_version_in_file" >> "$GITHUB_ENV"
38 | else
39 | echo ".last_release file not found."
40 | exit 1
41 | fi
42 |
43 | - name: Determine if update is needed
44 | id: prepare_update
45 | run: |
46 | if [[ -n "${{ env.latest_release_version }}" && "${{ env.latest_release_version }}" != "${{ env.last_processed_version }}" ]]; then
47 | echo "New version found: ${{ env.latest_release_version }}. (Previous: ${{ env.last_processed_version }})"
48 | {
49 | echo "update_needed=true"
50 | echo "new_version=${{ env.latest_release_version }}"
51 | echo "new_branch_name=update-photon-${{ env.latest_release_version }}"
52 | } >> "$GITHUB_OUTPUT"
53 | else
54 | echo "No new Photon release detected or version is already up-to-date. Latest fetched: '${{ env.latest_release_version }}', last processed: '${{ env.last_processed_version }}'."
55 | {
56 | echo "update_needed=false"
57 | echo "new_version=${{ env.last_processed_version }}"
58 | } >> "$GITHUB_OUTPUT"
59 | fi
60 |
61 | - name: Update release file(s) locally
62 | if: steps.prepare_update.outputs.update_needed == 'true'
63 | run: |
64 | echo "Updating .last_release to ${{ steps.prepare_update.outputs.new_version }}"
65 | echo "${{ steps.prepare_update.outputs.new_version }}" > .last_release
66 |
67 | - name: Create Pull Request
68 | if: steps.prepare_update.outputs.update_needed == 'true'
69 | uses: peter-evans/create-pull-request@v6
70 | with:
71 | token: ${{ secrets.MY_PAT_TOKEN }}
72 | commit-message: |
73 | Update Photon version to ${{ steps.prepare_update.outputs.new_version }}
74 |
75 | Automated update of the .last_release file (and potentially other version files)
76 | to reflect the new Photon release: ${{ steps.prepare_update.outputs.new_version }}.
77 | committer: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
78 | author: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
79 | branch: ${{ steps.prepare_update.outputs.new_branch_name }}
80 | delete-branch: true
81 | title: "Update Photon to version ${{ steps.prepare_update.outputs.new_version }}"
82 | body: |
83 | A new version of Photon (${{ steps.prepare_update.outputs.new_version }}) has been released.
84 |
85 | This Pull Request proposes updating our tracked version.
86 |
87 | **Release File(s) Updated:**
88 | * `.last_release` has been updated to `${{ steps.prepare_update.outputs.new_version }}`.
89 | * (Mention any other files updated here, e.g., Dockerfile, if applicable)
90 |
91 | **Next Steps:**
92 | 1. Review the changes in this PR.
93 | 2. Merge this PR if everything looks good.
94 | 3. Build New Image: Merging this PR should (ideally) trigger the separate workflow responsible for building and publishing the new Docker image with Photon version `${{ steps.prepare_update.outputs.new_version }}`.
95 |
96 | ---
97 | Upstream release notes for Photon ${{ steps.prepare_update.outputs.new_version }}: https://github.com/komoot/photon/releases/tag/${{ steps.prepare_update.outputs.new_version }}
98 | labels: |
99 | update
100 | automated-pr
101 |
102 | - name: No update needed
103 | if: steps.prepare_update.outputs.update_needed == 'false'
104 | run: echo "No new Photon release was found or the version is already current. No action taken."
105 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on:
3 | pull_request:
4 | workflow_dispatch:
5 |
6 | permissions: {}
7 |
8 | jobs:
9 | lint:
10 | runs-on: ubuntu-latest
11 |
12 | permissions:
13 | contents: read
14 | packages: read
15 | statuses: write
16 |
17 | steps:
18 | - name: Checkout Code
19 | uses: actions/checkout@v4
20 | with:
21 | fetch-depth: 0
22 |
23 | - name: Lint Code Base
24 | uses: github/super-linter/slim@v7
25 | env:
26 | VALIDATE_ALL_CODEBASE: true
27 | FIX_MARKDOWN_PRETTIER: true
28 | VALIDATE_MARKDOWN_PRETTIER: true
29 | VALIDATE_GITLEAKS: true
30 | VALIDATE_GITHUB_ACTIONS: true
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.last_release:
--------------------------------------------------------------------------------
1 | 0.7.0
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM eclipse-temurin:21.0.5_11-jre-noble
2 |
3 | ARG PHOTON_VERSION
4 |
5 | RUN apt-get update \
6 | && apt-get -y install --no-install-recommends \
7 | pbzip2 \
8 | wget \
9 | procps \
10 | coreutils \
11 | tree \
12 | && rm -rf /var/lib/apt/lists/*
13 |
14 | WORKDIR /photon
15 |
16 | RUN mkdir -p /photon/photon_data
17 |
18 | ADD https://github.com/komoot/photon/releases/download/${PHOTON_VERSION}/photon-opensearch-${PHOTON_VERSION}.jar /photon/photon.jar
19 |
20 | COPY start-photon.sh ./start-photon.sh
21 | COPY src/ ./src/
22 | RUN chmod +x start-photon.sh src/*.sh
23 |
24 |
25 | VOLUME /photon/photon_data
26 | EXPOSE 2322
27 |
28 | ENTRYPOINT ["/photon/start-photon.sh"]
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |     
2 |
3 | # Photon Docker Image
4 |
5 | ## Overview
6 |
7 | This is an _unofficial_ docker image for [Photon](https://github.com/komoot/photon)
8 |
9 | Photon is an open-source geocoding solution built for OpenStreetMap (OSM) data, providing features such as search-as-you-type and reverse geocoding.
10 | This repository offers a Docker image for running Photon locally, enhancing data privacy and integration capabilities with services like [Dawarich](https://github.com/Freika/dawarich).
11 |
12 | ## Important Notes
13 |
14 | ⚠️ **Warning: Large File Sizes** ⚠️
15 |
16 | - The Photon index file is fairly large and growing steadily. As of beginning of 2025, around 200GB is needed for the full index. Growing 10-20GB per year.
17 | - Ensure you have sufficient disk space available before running the container.
18 | - The initial download and extraction process may take a considerable amount of time. Depending on your hardware, checksum verification and decompression may take multiple hours.
19 |
20 | ## Usage
21 |
22 | ### Configuration Options
23 |
24 | The container can be configured using the following environment variables:
25 |
26 | | Variable | Parameters | Default | Description |
27 | | ----------------- | ----------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28 | | `UPDATE_STRATEGY` | `PARALLEL`, `SEQUENTIAL`, `DISABLED` | `SEQUENTIAL` | Controls how index updates are handled. `PARALLEL` downloads new index in background then swaps with minimal downtime (requires 2x space). `SEQUENTIAL` stops Photon, deletes existing index, downloads new one, then restarts. `DISABLED` prevents automatic updates. |
29 | | `UPDATE_INTERVAL` | Time string (e.g., "24h", "60m", "3600s") | `24h` | How often to check for updates |
30 | | `COUNTRY_CODE` | Two-letter country code | - | Optional country code for smaller index ([available codes](https://download1.graphhopper.com/public/extracts/by-country-code/)). Only one country code is supported at a time. |
31 | | `LOG_LEVEL` | `DEBUG`, `INFO`, `ERROR` | `INFO` | Controls logging verbosity |
32 | | `FORCE_UPDATE` | `TRUE`, `FALSE` | `FALSE` | Forces index update on container startup, regardless of UPDATE_STRATEGY |
33 | | `BASE_URL` | Valid URL | `https://download1.graphhopper.com/public` | Custom base URL for index data downloads. Should point to parent directory of index files. |
34 | | `SKIP_MD5_CHECK` | `TRUE`, `FALSE` | `FALSE` | Optionally skip MD5 verification of downloaded index files |
35 |
36 | ### Example Docker Compose
37 |
38 | ```yaml
39 | services:
40 | photon:
41 | image: rtuszik/photon-docker:latest
42 | environment:
43 | - UPDATE_STRATEGY=PARALLEL
44 | - UPDATE_INTERVAL=24h
45 | # - COUNTRY_CODE=zw # Optional: country-specific index
46 | volumes:
47 | - photon_data:/photon/photon_data
48 | restart: unless-stopped
49 | ports:
50 | - "2322:2322"
51 | volumes:
52 | photon_data:
53 | ```
54 |
55 | ```bash
56 | docker-compose up -d
57 | ```
58 |
59 | ### Use with Dawarich
60 |
61 | This docker container for photon can be used as your reverse-geocoder for the [Dawarich Location History Tracker](https://github.com/Freika/dawarich)
62 |
63 | To connect dawarich to your photon instance, the following environment variables need to be set in your dawarich docker-compose.yml:
64 |
65 | ```yaml
66 | PHOTON_API_HOST={PHOTON-IP}:{PORT}
67 | PHOTON_API_USE_HTTPS=false
68 | ```
69 |
70 | for example:
71 |
72 | ```yaml
73 | PHOTON_API_HOST=192.168.10.10:2322
74 | PHOTON_API_USE_HTTPS=false
75 | ```
76 |
77 | - Do _not_ set PHOTON_API_USE_HTTPS to true unless your photon instance is available using HTTPS.
78 | - Only use the host address for your photon instance. Do not append `/api`
79 |
80 | ### Build and Run Locally
81 |
82 | ```bash
83 | docker compose -f docker-compose.build.yml build --build-arg PHOTON_VERSION=0.6.2
84 | ```
85 |
86 | ### Accessing the API
87 |
88 | The Photon API is available at:
89 |
90 | ```
91 | http://localhost:2322/api?q=Harare
92 | ```
93 |
94 | ## Contributing
95 |
96 | Contributions are welcome. Please submit pull requests or open issues for suggestions and improvements.
97 |
98 | ## License
99 |
100 | This project is licensed under the Apache License, Version 2.0.
101 |
102 | ## Acknowledgments
103 |
104 | - [Photon](https://github.com/komoot/photon)
105 | - [Dawarich](https://github.com/Freika/dawarich)
106 |
--------------------------------------------------------------------------------
/docker-compose.build.yml:
--------------------------------------------------------------------------------
1 | services:
2 | photon:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile
6 | args:
7 | - PHOTON_VERSION=${PHOTON_VERSION}
8 | environment:
9 | - UPDATE_STRATEGY=SEQUENTIAL
10 | - UPDATE_INTERVAL=3m
11 | # - LOG_LEVEL=DEBUG
12 | # - FORCE_UPDATE=TRUE
13 | # - COUNTRY_CODE=zw
14 | # - BASE_URL=https://download1.graphhopper.com/public
15 | # - SKIP_MD5_CHECK=TRUE
16 | volumes:
17 | - photon_data:/photon/photon_data
18 | restart: unless-stopped
19 | ports:
20 | - "2322:2322"
21 | volumes:
22 | photon_data:
23 |
--------------------------------------------------------------------------------
/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | services:
2 | photon:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile
6 | args:
7 | - PHOTON_VERSION=0.6.2
8 | image: rtuszik/photon-docker:dev
9 | environment:
10 | - UPDATE_STRATEGY=PARALLEL
11 | - UPDATE_INTERVAL=1m
12 | - LOG_LEVEL=DEBUG
13 | - COUNTRY_CODE=ad
14 | # - BASE_URL=https://download1.graphhopper.com/public
15 |
16 | volumes:
17 | - photon_data:/photon/photon_data
18 | restart: unless-stopped
19 | ports:
20 | - "2322:2322"
21 | volumes:
22 | photon_data:
23 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | photon:
3 | image: ghcr.io/rtuszik/photon-docker:latest
4 | container_name: "photon-docker"
5 | environment:
6 | - UPDATE_STRATEGY=PARALLEL
7 | - UPDATE_INTERVAL=24h
8 | - LOG_LEVEL=INFO # Options: DEBUG, INFO, ERROR
9 | # - COUNTRY_CODE=zw
10 | volumes:
11 | - photon_data:/photon/photon_data
12 | restart: unless-stopped
13 | ports:
14 | - "2322:2322"
15 | volumes:
16 | photon_data:
17 |
--------------------------------------------------------------------------------
/src/config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Fixed paths
4 | PHOTON_DIR="/photon"
5 | PHOTON_DATA_DIR="${PHOTON_DIR}/photon_data"
6 | PHOTON_JAR="${PHOTON_DIR}/photon.jar"
7 | OS_DATA_DIR="${PHOTON_DATA_DIR}/node_1"
8 | INDEX_DIR="${OS_DATA_DIR}"
9 | ES_DATA_DIR="${PHOTON_DATA_DIR}/elasticsearch"
10 | TEMP_DIR="${PHOTON_DATA_DIR}/temp"
11 | PID_FILE="${PHOTON_DIR}/photon.pid"
12 |
13 | # Environment variables with defaults
14 | UPDATE_STRATEGY=${UPDATE_STRATEGY:-SEQUENTIAL}
15 | UPDATE_INTERVAL=${UPDATE_INTERVAL:-24h}
16 | LOG_LEVEL=${LOG_LEVEL:-INFO}
17 | BASE_URL=${BASE_URL:-https://download1.graphhopper.com/public/experimental}
18 | FORCE_UPDATE=${FORCE_UPDATE:-FALSE}
19 | SKIP_MD5_CHECK=${SKIP_MD5_CHECK:-FALSE}
20 |
21 | # Validate UPDATE_STRATEGY
22 | if [[ ! "${UPDATE_STRATEGY}" =~ ^(SEQUENTIAL|PARALLEL|DISABLED)$ ]]; then
23 | echo "ERROR: Invalid UPDATE_STRATEGY: ${UPDATE_STRATEGY}"
24 | echo "Valid options are: SEQUENTIAL, PARALLEL, DISABLED"
25 | exit 1
26 | fi
27 |
--------------------------------------------------------------------------------
/src/logging.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ANSI color codes
4 | GREEN='\033[0;32m'
5 | RED='\033[0;31m'
6 | BLUE='\033[0;34m'
7 | NC='\033[0m'
8 |
9 | log_info() {
10 | if [[ "${LOG_LEVEL:-INFO}" != "ERROR" ]]; then
11 | echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $*"
12 | fi
13 | }
14 |
15 | log_error() {
16 | echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
17 | }
18 |
19 | log_debug() {
20 | if [[ "${LOG_LEVEL:-INFO}" == "DEBUG" ]]; then
21 | echo -e "${BLUE}[DEBUG]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $*"
22 | fi
23 | }
24 |
--------------------------------------------------------------------------------
/src/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "src/config.sh"
4 | source "src/logging.sh"
5 |
6 | stop_photon() {
7 | if [ -f "$PID_FILE" ]; then
8 | local pid
9 | pid=$(cat "$PID_FILE")
10 |
11 | if ps -p "$pid" > /dev/null; then
12 | log_info "Stopping Photon service (PID: $pid)"
13 | if ! kill -15 "$pid"; then
14 | log_error "Failed to stop Photon service gracefully, attempting force kill"
15 | kill -9 "$pid" || true
16 | fi
17 |
18 | # Wait for process to stop
19 | local count=0
20 | while ps -p "$pid" > /dev/null && [ $count -lt 10 ]; do
21 | sleep 1
22 | ((count++))
23 | done
24 |
25 | if ps -p "$pid" > /dev/null; then
26 | log_error "Failed to stop Photon service"
27 | return 1
28 | fi
29 | else
30 | log_info "Photon service not running"
31 | fi
32 | rm -f "$PID_FILE"
33 | fi
34 | return 0
35 | }
36 |
37 | start_photon() {
38 | # Check if already running
39 | if [ -f "$PID_FILE" ]; then
40 | local pid
41 | pid=$(cat "$PID_FILE")
42 | if ps -p "$pid" > /dev/null; then
43 | log_info "Photon service already running with PID: $pid"
44 | return 0
45 | else
46 | log_info "Removing stale PID file"
47 | rm -f "$PID_FILE"
48 | fi
49 | fi
50 |
51 | log_info "Starting Photon service"
52 | java -jar "$PHOTON_JAR" -data-dir "$PHOTON_DIR" &
53 | local new_pid=$!
54 | echo $new_pid > "$PID_FILE"
55 |
56 | log_info "Photon service started successfully with PID: $new_pid"
57 | return 0
58 | }
59 |
--------------------------------------------------------------------------------
/start-photon.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Source modules
4 | source "src/logging.sh"
5 | source "src/config.sh"
6 | source "src/process.sh"
7 |
8 | # Log environment variables
9 | log_info "Environment variables:"
10 | log_info "UPDATE_STRATEGY=$UPDATE_STRATEGY"
11 | log_info "UPDATE_INTERVAL=$UPDATE_INTERVAL"
12 | log_info "LOG_LEVEL=$LOG_LEVEL"
13 | log_info "BASE_URL=$BASE_URL"
14 | log_info "FORCE_UPDATE=$FORCE_UPDATE"
15 | log_info "SKIP_MD5_CHECK=$SKIP_MD5_CHECK"
16 | log_info "COUNTRY_CODE=${COUNTRY_CODE:-not set}"
17 |
18 | ES_UID="${ES_UID:-1000}"
19 | ES_GID="${ES_GID:-1000}"
20 |
21 | # Define DATA_DIR from config
22 | DATA_DIR="$PHOTON_DIR"
23 |
24 | # Error handling
25 | set -euo pipefail
26 | trap 'handle_error $? $LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]:-})' ERR
27 |
28 |
29 | handle_error() {
30 | local exit_code=$1
31 | local line_no=$2
32 | local last_command=$4
33 | local func_trace=$5
34 | log_error "Error $exit_code occurred on line $line_no: $last_command"
35 | log_error "Function trace: $func_trace"
36 | cleanup_and_exit
37 | }
38 |
39 | # Cleanup function
40 | cleanup_and_exit() {
41 | log_info "Cleaning up temporary files..."
42 |
43 | # Stop service if running
44 | stop_photon
45 |
46 | # Remove temporary files
47 | if [ -d "$TEMP_DIR" ]; then
48 | if ! rm -rf "${TEMP_DIR:?}"/*; then
49 | log_error "Failed to clean up temporary directory"
50 | fi
51 | fi
52 |
53 | # Remove PID file if it exists
54 | rm -f /photon/photon.pid
55 |
56 | exit 1
57 | }
58 |
59 | # Check available disk space against remote file size
60 | check_disk_space() {
61 | local url=$1
62 | local available
63 | local full_url="${url}.tar.bz2"
64 |
65 | log_debug "Checking disk space for URL: $full_url"
66 | log_debug "URL components - Protocol: ${full_url%%://*}, Host: ${full_url#*://}, Path: ${full_url#*://*/}"
67 |
68 | # Get remote file size using wget spider
69 | log_debug "Executing wget spider command: wget --spider --server-response \"$full_url\""
70 | local wget_output
71 | wget_output=$(wget --spider --server-response "$full_url" 2>&1)
72 | local wget_status=$?
73 | log_debug "wget spider command output: $(echo "$wget_output" | head -20)"
74 |
75 | local remote_size
76 | if ! remote_size=$(echo "$wget_output" | grep "Content-Length" | awk '{print $2}' | tail -1); then
77 | log_error "Failed to execute wget spider command"
78 | log_debug "wget spider command failed with status: $wget_status"
79 | log_debug "Full wget output: $wget_output"
80 | return 1
81 | fi
82 |
83 | if [ -z "$remote_size" ]; then
84 | log_error "Failed to get remote file size"
85 | log_debug "No Content-Length found in wget output"
86 | return 1
87 | fi
88 |
89 | log_debug "Remote file size detected: $remote_size bytes"
90 |
91 | # Check available space in photon_data directory
92 | log_debug "Creating data directory structure at $DATA_DIR/photon_data"
93 | mkdir -p "$DATA_DIR/photon_data"
94 | log_debug "Directory created. Contents: $(ls -l $DATA_DIR/photon_data 2>/dev/null || echo '')"
95 | available=$(df -B1 "$DATA_DIR/photon_data" | awk 'NR==2 {print $4}')
96 | log_debug "Available disk space: $available bytes"
97 |
98 | if [ "$available" -lt "$remote_size" ]; then
99 | log_error "Insufficient disk space. Required: ${remote_size}B , Available: ${available}B"
100 | return 1
101 | fi
102 |
103 | log_info "Sufficient disk space available. Required: ${remote_size}B, Available: ${available}B"
104 | }
105 |
106 | # Verify directory structure and index
107 | verify_structure() {
108 | local dir=$1
109 | log_debug "Verifying directory structure at: $dir/photon_data"
110 | if [ ! -d "$dir/photon_data/node_1" ]; then
111 | log_error "Directory structure failed verification. Existing paths: $(find "$dir" -maxdepth 3 -type d | tr '\n' ' ')"
112 | log_error "Invalid structure: missing index directory"
113 | return 1
114 | fi
115 |
116 |
117 | return 0
118 | }
119 |
120 | set_permissions() {
121 |
122 | local dir=$1
123 | log_info "Ensuring correct ownership and permissions for $dir"
124 | log_debug "Current state for $dir: $(stat -c 'Perms: %a, Owner: %U (%u), Group: %G (%g), Name: %n' "$dir" 2>/dev/null || echo "Path $dir not found or stat error")"
125 |
126 | # Change ownership
127 | # The -R flag makes it recursive.
128 | if ! chown -R "$ES_UID:$ES_GID" "$dir"; then
129 | log_info "WARNING: Failed to chown $dir to $ES_UID:$ES_GID. This might be due to host volume restrictions. Opensearch may encounter permission issues if not run as root or if host permissions are incorrect."
130 | else
131 | log_debug "Successfully changed ownership of $dir to $ES_UID:$ES_GID."
132 | fi
133 |
134 | # Change permissions
135 | # 755 means: Owner (opensearch user): Read, Write, Execute (rwx)
136 | # Group (opensearch group): Read, Execute (r-x)
137 | # Others: Read, Execute (r-x)
138 |
139 | if ! chmod -R 755 "$dir"; then
140 | log_info "WARNING: Failed to chmod $dir to 755. This might be due to host volume restrictions."
141 | else
142 | log_debug "Successfully changed permissions of $dir to 755."
143 | fi
144 |
145 | log_info "Post-permission state for $dir: $(stat -c 'Perms: %a, Owner: %U (%u), Group: %G (%g), Name: %n' "$dir" 2>/dev/null || echo "Path $dir not found or stat error after changes")"
146 | }
147 |
148 |
149 | # Check if remote index is newer than local
150 | check_remote_index() {
151 | local url=$1
152 | local full_url="${url}.tar.bz2"
153 |
154 | log_debug "Checking if remote index is newer than local"
155 | log_debug "Remote URL: $full_url"
156 |
157 | # If FORCE_UPDATE is TRUE, skip timestamp check
158 | if [ "${FORCE_UPDATE}" = "TRUE" ]; then
159 | log_info "Force update requested, skipping timestamp check"
160 | return 0
161 | fi
162 |
163 | local remote_time
164 | local wget_output
165 |
166 | # Get remote file timestamp using HEAD request
167 | log_debug "Executing: wget --spider -S \"$full_url\""
168 | if ! wget_output=$(wget --spider -S "$full_url" 2>&1); then
169 | log_error "Failed to check remote index timestamp"
170 | log_debug "Full wget output:\n$wget_output"
171 | return 2
172 | fi
173 |
174 | log_debug "Full wget spider response:\n$wget_output"
175 |
176 | remote_time=$(echo "$wget_output" | grep -i "Last-Modified:" | cut -d' ' -f4-)
177 | if [ -z "$remote_time" ]; then
178 | log_error "Failed to get remote index timestamp"
179 | log_debug "No Last-Modified header found in wget output"
180 | return 2
181 | fi
182 |
183 | log_debug "Remote timestamp: $remote_time"
184 |
185 | # Convert remote time to epoch
186 | remote_epoch=$(date -d "$remote_time" +%s 2>/dev/null)
187 |
188 | # Get local index timestamp if it exists
189 | if [ -d "$INDEX_DIR" ]; then
190 | local_epoch=$(stat -c %Y "$INDEX_DIR" 2>/dev/null)
191 |
192 | log_debug "Remote index timestamp: $remote_time (${remote_epoch})"
193 | log_debug "Local index timestamp: $(date -d "@$local_epoch" 2>/dev/null) (${local_epoch})"
194 |
195 | # Compare timestamps with 1 day tolerance
196 | local time_diff=$((remote_epoch - local_epoch))
197 | if [ "${time_diff#-}" -lt 86400 ]; then
198 | log_info "Local index is up to date (within 1 day tolerance)"
199 | return 1
200 | elif [ "$remote_epoch" -gt "$local_epoch" ]; then
201 | log_info "Remote index is newer than local index"
202 | return 0
203 | else
204 | log_info "Local index is up to date"
205 | return 1
206 | fi
207 | else
208 | log_info "No local index found"
209 | return 0
210 | fi
211 | }
212 |
213 | # Core utility functions
214 | download_file() {
215 | local url=$1
216 | local output_file=$2
217 |
218 | log_info "Downloading from $url"
219 | if ! wget --progress=bar:force:noscroll:giga -O "$output_file" "$url"; then
220 | log_error "Failed to download file from $url"
221 | return 1
222 | fi
223 | log_info "Index file downloaded successfully"
224 | return 0
225 | }
226 |
227 | verify_checksum() {
228 | local file=$1
229 | local md5_file=$2
230 | local dir
231 | dir=$(dirname "$file")
232 |
233 | log_info "Verifying MD5 checksum"
234 | if ! (cd "$dir" && md5sum -c <(cut -d' ' -f1 "$(basename "$md5_file")" > temp.md5 && echo "$(cat temp.md5) $(basename "$file")" && rm temp.md5)); then
235 | log_error "MD5 verification failed"
236 | return 1
237 | fi
238 | log_info "Checksum verification successful"
239 | return 0
240 | }
241 |
242 | extract_archive() {
243 | local archive=$1
244 | local extract_dir=$2
245 |
246 | log_debug "Creating extraction directory $extract_dir"
247 | mkdir -p "$extract_dir"
248 |
249 | log_info "Extracting $archive to $extract_dir"
250 | if ! pbzip2 -dc "$archive" | tar x -C "$extract_dir"; then
251 | log_error "Failed to extract files"
252 | return 1
253 | fi
254 | log_info: "Extraction completed successfully"
255 | return 0
256 | }
257 |
258 | stop_photon() {
259 | if [ -f /photon/photon.pid ]; then
260 | local pid
261 | pid=$(cat /photon/photon.pid)
262 |
263 | if ps -p "$pid" > /dev/null; then
264 | log_info "Stopping Photon service (PID: $pid)"
265 | if ! kill -15 "$pid"; then
266 | log_error "Failed to stop Photon service gracefully, attempting force kill"
267 | kill -9 "$pid" || true
268 | fi
269 |
270 | # Wait for process to stop
271 | local count=0
272 | while ps -p "$pid" > /dev/null && [ $count -lt 10 ]; do
273 | sleep 1
274 | ((count++))
275 | done
276 |
277 | if ps -p "$pid" > /dev/null; then
278 | log_error "Failed to stop Photon service"
279 | return 1
280 | fi
281 | else
282 | log_info "Photon service not running"
283 | fi
284 | rm -f /photon/photon.pid
285 | fi
286 | return 0
287 | }
288 |
289 | move_index() {
290 | local source_dir=$1
291 | local target_dir=$2
292 |
293 | # Find opensearch directory recursively
294 | log_info "Searching for opensearch directory in: $source_dir"
295 | local es_dir
296 | es_dir=$(find "$source_dir" -type d -name "node_1" | head -n 1)
297 | log_info "Found opensearch candidates: $(find "$source_dir" -type d -name "node_1" | tr '\n' ' ')"
298 |
299 | if [ -n "$es_dir" ]; then
300 | log_info "Found opensearch directory at $es_dir"
301 | log_debug "Moving opensearch from $es_dir to $target_dir"
302 | log_info "Current target directory state: $(ls -ld "$target_dir" 2>/dev/null || echo '')"
303 | mkdir -p "$(dirname "$target_dir")"
304 | log_info "Parent directory prepared. New state: $(ls -ld "$(dirname "$target_dir")" 2>/dev/null || echo '')"
305 | log_info "Executing mv command: mv $es_dir $target_dir"
306 | mv -v "$es_dir" "$target_dir" | while read -r line; do log_debug "mv: $line"; done
307 | log_debug "Move completed. Target directory now contains: $(ls -l "$target_dir" | wc -l) items"
308 | return 0
309 | else
310 | log_error "Could not find opensearch directory in extracted files"
311 | return 1
312 | fi
313 | }
314 |
315 | cleanup_temp() {
316 | log_info "Cleaning up temporary directory"
317 | log_debug "Pre-cleanup temporary directory contents: $(tree -a $TEMP_DIR 2>/dev/null || echo '')"
318 | log_debug "Executing: rm -rf ${TEMP_DIR:?}"
319 | rm -rfv "${TEMP_DIR:?}" | while read -r line; do log_debug "rm: $line"; done
320 | log_info "Final photon_data directory structure:\n$(tree -L 2 $PHOTON_DATA_DIR 2>/dev/null || echo '')"
321 | }
322 |
323 | cleanup_stale_es() {
324 | # Remove old elasticsearch index
325 | if [ -d "$ES_DATA_DIR" ]; then
326 | log_info "Removing old elasticsearch directory at $ES_DATA_DIR"
327 | log_debug "Executing: rm -rf $ES_DATA_DIR"
328 | if ! rm -rf "$ES_DATA_DIR"; then
329 | log_error "Failed to remove old elasticsearch index"
330 | fi
331 | fi
332 | }
333 |
334 | # Prepare download URL based on country code or custom base URL
335 | prepare_download_url() {
336 | # Ensure BASE_URL doesn't have trailing slash
337 | local base_url="${BASE_URL%/}"
338 | local result_url
339 |
340 | if [[ -n "${COUNTRY_CODE:-}" ]]; then
341 | result_url="${base_url}/extracts/by-country-code/${COUNTRY_CODE}/photon-db-${COUNTRY_CODE}-latest"
342 | else
343 | result_url="${base_url}/photon-db-latest"
344 | fi
345 |
346 | echo "$result_url"
347 | }
348 |
349 | # Download and verify index
350 | download_index() {
351 | local url
352 | url=$(prepare_download_url)
353 | log_debug "Download URL: $url"
354 | log_debug "Full tar.bz2 URL: ${url}.tar.bz2"
355 | log_debug "Full MD5 URL: ${url}.tar.bz2.md5"
356 |
357 | mkdir -p "$TEMP_DIR"
358 | log_debug "Created temp directory: $TEMP_DIR"
359 |
360 | # Check disk space before downloading
361 | log_debug "Checking disk space for download"
362 | if ! check_disk_space "$url"; then
363 | log_error "Disk space check failed"
364 | cleanup_temp
365 | return 1
366 | fi
367 |
368 | # Download files
369 | local download_url="${url}.tar.bz2"
370 | log_info "Downloading index from ${download_url}"
371 | log_debug "Executing: wget --progress=bar:force:noscroll:giga -O \"$TEMP_DIR/photon-db.tar.bz2\" \"${download_url}\""
372 |
373 | if ! wget --progress=bar:force:noscroll:giga -O "$TEMP_DIR/photon-db.tar.bz2" "${download_url}" 2>&1; then
374 | log_error "Failed to download index file from ${download_url}"
375 | cleanup_temp
376 | return 1
377 | fi
378 |
379 | log_debug "Index download successful. File size: $(du -h "$TEMP_DIR/photon-db.tar.bz2" | awk '{print $1}')"
380 |
381 | if [ "${SKIP_MD5_CHECK}" != "TRUE" ]; then
382 | log_debug "Downloading MD5 from ${url}.tar.bz2.md5"
383 | log_debug "Executing: wget -O \"$TEMP_DIR/photon-db.md5\" \"${url}.tar.bz2.md5\""
384 |
385 | local md5_output
386 | md5_output=$(wget -O "$TEMP_DIR/photon-db.md5" "${url}.tar.bz2.md5" 2>&1)
387 | local md5_status=$?
388 |
389 | if [ $md5_status -ne 0 ]; then
390 | log_error "Failed to download MD5 file from ${url}.tar.bz2.md5"
391 | log_debug "wget exit status: $md5_status"
392 | log_debug "wget output: $(echo "$md5_output" | head -20)"
393 | cleanup_temp
394 | return 1
395 | fi
396 |
397 | log_debug "MD5 download successful. MD5 content: $(cat "$TEMP_DIR/photon-db.md5" | head -1)"
398 |
399 | # Verify checksum
400 | log_debug "Starting MD5 verification"
401 | if ! (cd "$TEMP_DIR" && md5sum -c <(awk '{print $1" photon-db.tar.bz2"}' photon-db.md5)); then
402 | log_error "MD5 verification failed"
403 | cleanup_temp
404 | return 1
405 | fi
406 | log_info "MD5 verification successful"
407 | log_debug "MD5 verification completed"
408 |
409 | # Extract archive
410 | log_info "Extracting archive, this may take some time..."
411 | else
412 | log_info "Skipping MD5 verification as requested"
413 | fi
414 |
415 | log_info "Extracting archive to $TEMP_DIR"
416 | # Extract archive in place
417 | if ! pbzip2 -dc "$TEMP_DIR/photon-db.tar.bz2" | tar x -C "$TEMP_DIR"; then
418 | log_error "Failed to extract files"
419 | cleanup_temp
420 | return 1
421 | fi
422 |
423 | log_info "Extraction completed successfully"
424 | return 0
425 | }
426 |
427 | # Strategy-specific update functions
428 | parallel_update() {
429 | log_info "Starting parallel update process"
430 |
431 | # Download and prepare new index while current one is running
432 | log_debug "Downloading new index while current service is running"
433 | if ! download_index; then
434 | log_error "Failed to download index"
435 | return 1
436 | fi
437 |
438 | # Verify the downloaded index
439 | log_info "Verifying downloaded index structure"
440 | if ! verify_structure "$TEMP_DIR"; then
441 | log_error "Downloaded index verification failed"
442 | cleanup_temp
443 | return 1
444 | fi
445 |
446 |
447 | # Stop service and swap indexes
448 | log_info "Stopping Photon service before swapping indexes"
449 | if ! stop_photon; then
450 | log_error "Failed to stop Photon service cleanly"
451 | cleanup_temp
452 | return 1
453 | fi
454 |
455 | # Wait a moment for process to fully stop
456 | sleep 5
457 |
458 | # Backup and swap
459 | if [ -d "$INDEX_DIR" ]; then
460 | log_debug "Backing up current index to $INDEX_DIR.old"
461 | if ! mv "$INDEX_DIR" "$INDEX_DIR.old"; then
462 | log_error "Failed to backup current index"
463 | cleanup_temp
464 | return 1
465 | fi
466 | fi
467 |
468 | log_debug "Moving new index from $TEMP_DIR to $INDEX_DIR"
469 | if ! move_index "$TEMP_DIR" "$INDEX_DIR"; then
470 | log_error "Failed to move index, attempting to restore backup"
471 | if [ -d "$INDEX_DIR.old" ]; then
472 | if ! mv "$INDEX_DIR.old" "$INDEX_DIR"; then
473 | log_error "Failed to restore backup index"
474 | fi
475 | fi
476 | cleanup_temp
477 | return 1
478 | fi
479 |
480 | set_permissions "$INDEX_DIR" # Set permissions on the final index directory
481 |
482 | # Clean up
483 | log_debug "Removing old index backup"
484 |
485 |
486 | rm -rf "$INDEX_DIR.old" 2>/dev/null || true
487 |
488 |
489 | cleanup_stale_es
490 |
491 | log_info "Parallel update completed successfully"
492 |
493 | cleanup_temp
494 | return 0
495 | }
496 |
497 | sequential_update() {
498 | log_info "Starting sequential update process"
499 |
500 | log_info "Stopping Photon service before update"
501 | if ! stop_photon; then
502 | log_error "Failed to stop Photon service cleanly"
503 | return 1
504 | fi
505 |
506 | # Wait a moment for process to fully stop
507 | sleep 2
508 |
509 | # Remove existing index
510 | if [ -d "$INDEX_DIR" ]; then
511 | log_info "Removing existing opensearch directory at $INDEX_DIR"
512 | log_debug "Executing: rm -rf $INDEX_DIR"
513 | if ! rm -rf "$INDEX_DIR"; then
514 | log_error "Failed to remove existing index"
515 | return 1
516 | fi
517 | fi
518 |
519 | cleanup_stale_es
520 |
521 | log_info "Downloading new index"
522 | if ! download_index; then
523 | log_error "Failed to download index"
524 | return 1
525 | fi
526 |
527 | log_info "Moving index from $TEMP_DIR to $INDEX_DIR"
528 | if ! move_index "$TEMP_DIR" "$INDEX_DIR"; then
529 | log_error "Failed to move index"
530 | cleanup_temp
531 | return 1
532 | fi
533 |
534 | set_permissions "$INDEX_DIR" # Set permissions on the final index directory
535 |
536 | log_info "Verifying new index structure"
537 | if ! verify_structure "$DATA_DIR"; then # verify_structure "$DATA_DIR" checks $INDEX_DIR
538 | log_error "Failed to verify new index structure"
539 | cleanup_temp
540 | return 1
541 | fi
542 |
543 | log_info "Sequential update completed successfully"
544 | cleanup_temp
545 | return 0
546 | }
547 |
548 | update_index() {
549 | case "$UPDATE_STRATEGY" in
550 | PARALLEL)
551 | parallel_update
552 | ;;
553 | SEQUENTIAL)
554 | sequential_update
555 | ;;
556 | DISABLED)
557 | log_info "Index updates are disabled"
558 | ;;
559 | *)
560 | log_error "Invalid UPDATE_STRATEGY: $UPDATE_STRATEGY"
561 | return 1
562 | ;;
563 | esac
564 | }
565 |
566 | start_photon() {
567 | # Check if already running
568 | if [ -f /photon/photon.pid ]; then
569 | local pid
570 | pid=$(cat /photon/photon.pid)
571 | if ps -p "$pid" > /dev/null; then
572 | log_info "Photon service already running with PID: $pid"
573 | return 0
574 | else
575 | log_info "Removing stale PID file"
576 | rm -f /photon/photon.pid
577 | fi
578 | fi
579 |
580 | if [ -d "$INDEX_DIR" ]; then
581 | set_permissions "$INDEX_DIR" # Ensure permissions before start
582 | else
583 | log_error "Cannot start Photon: Index directory $INDEX_DIR does not exist."
584 | return 1
585 | fi
586 |
587 | log_info "Starting Photon service"
588 | java -jar photon.jar -data-dir /photon &
589 | local new_pid=$!
590 | echo $new_pid > /photon/photon.pid
591 |
592 | log_info "Photon service started successfully with PID: $new_pid"
593 | return 0
594 | }
595 |
596 | interval_to_seconds() {
597 | local interval=$1
598 | local value=${interval%[smhd]}
599 | local unit=${interval##*[0-9]}
600 |
601 | case $unit in
602 | s) echo $((value)) ;;
603 | m) echo $((value * 60)) ;;
604 | h) echo $((value * 3600)) ;;
605 | d) echo $((value * 86400)) ;;
606 | *) echo $((value * 3600)) ;; # Default to hours
607 | esac
608 | }
609 |
610 | setup_index() {
611 | mkdir -p "$DATA_DIR" "$TEMP_DIR"
612 |
613 | if [ -d "$INDEX_DIR" ]; then
614 | if verify_structure "$DATA_DIR"; then # verify_structure "$DATA_DIR" checks $INDEX_DIR
615 | log_info "Found existing valid opensearch index"
616 | set_permissions "$INDEX_DIR" # Ensure permissions on existing valid index
617 | return 0
618 | else
619 | log_error "Found invalid index structure, downloading fresh index"
620 | rm -rf "$INDEX_DIR"
621 | fi
622 | else
623 | log_info "No opensearch index found, performing initial download"
624 | fi
625 |
626 | if ! sequential_update; then
627 | log_error "Failed to setup initial index"
628 | return 1
629 | fi
630 |
631 | return 0
632 | }
633 |
634 | main() {
635 | if ! setup_index; then
636 | exit 1
637 | fi
638 |
639 | # Only run FORCE_UPDATE once during container startup
640 | if [ "${FORCE_UPDATE}" = "TRUE" ]; then
641 | log_info "Performing forced update on startup"
642 | if ! update_index; then
643 | log_error "Forced update failed"
644 | exit 1
645 | fi
646 | # Disable FORCE_UPDATE after first run
647 | FORCE_UPDATE="FALSE"
648 | log_info "FORCE_UPDATE disabled after initial run"
649 | fi
650 |
651 | if ! start_photon; then
652 | log_error "Failed to start Photon service"
653 | exit 1
654 | fi
655 |
656 | if [ "$UPDATE_STRATEGY" != "DISABLED" ]; then
657 | local update_seconds
658 | update_seconds=$(interval_to_seconds "$UPDATE_INTERVAL")
659 | log_info "Update strategy: $UPDATE_STRATEGY"
660 | log_info "Update interval: $UPDATE_INTERVAL (${update_seconds} seconds)"
661 |
662 | while true; do
663 | log_info "Sleeping for ${update_seconds} seconds until next update"
664 | sleep "$update_seconds"
665 | log_info "Checking for index updates"
666 |
667 | local url
668 | url=$(prepare_download_url)
669 |
670 | if check_remote_index "$url"; then
671 | log_info "Performing scheduled index update"
672 | update_index
673 | # Restart service after update
674 | if ! start_photon; then
675 | log_error "Failed to restart Photon service after update"
676 | exit 1
677 | fi
678 | fi
679 | done
680 | else
681 | log_info "Update strategy is disabled, skipping update loop"
682 | fi
683 |
684 | # Wait for Photon process to finish
685 | wait
686 | }
687 |
688 | main "$@"
689 |
--------------------------------------------------------------------------------