diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index de2bc4e..778a408 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ { - "name": "wspr_cdk", - "image": "mcr.microsoft.com/devcontainers/rust:bullseye", - "postCreateCommand": "bash .devcontainer/post_create.sh", + "name": "wspr_cdk_development_container", + "dockerFile": "Dockerfile", + "postCreateCommand": "bash ./scripts/bash/entrypoint.sh", "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/post_create.sh b/.devcontainer/post_create.sh deleted file mode 100644 index 4ba1bbf..0000000 --- a/.devcontainer/post_create.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -euxo pipefail -# pipefail - -# If set, the return value of a pipeline is the value of the last -# (rightmost) command to exit with a non-zero status, or zero if -# all commands in the pipeline exit successfully. -# This option is disabled by default. - -sudo apt-get update -sudo apt-get install -y python3-dev python3-pip python3-venv libclang-dev -sudo python3 -m venv .venv diff --git a/.dockerignore b/.dockerignore index 7e9894c..2eb31cd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -/**/target \ No newline at end of file +/**/target +service_account.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 364fdd2..3033266 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,16 +55,18 @@ ENV ROCKET_PORT=8000 # Build the project RUN cargo build --workspace --release +COPY /scripts/bash/entrypoint.sh /scripts/bash/entrypoint.sh +RUN chmod +x /scripts/bash/entrypoint.sh + #------------------------------------ # [CURRENT] entry point. -# Run maturin develop during container initialization -ENTRYPOINT ["/bin/bash", "-c", ". /opt/venv/bin/activate && maturin develop -m python_wrapper/Cargo.toml && ./target/release/wspr_cdk_server"] +# Run maturin develop on container initialization. +ENTRYPOINT ["/scripts/bash/entrypoint.sh"] #------------------------------------ EXPOSE 8000 # NOTES # -# End users need to provide/generate their own credentials. -# pip install patchelf \ No newline at end of file +# End users need to provide/generate their own credentials. \ No newline at end of file diff --git a/README.md b/README.md index b67dac2..45e60e9 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,59 @@ `wspr_cdk` provides an abstraction for accessing and analyzing **WSPR** (_Weak Signal Propagation Reporter_) real-time spot data. This crate allows you to perform queries and fetch data from the WSPR database with ease. +## Prerequisites + +When running the application, ensure that the `service_account.json` file has been set up correctly. This file contains the authentication _credentials_ needed to access the **Google Drive API**. + +- If this file is _missing_ or _incorrectly configured_, you will encounter an authentication error when attempting to upload files to Google Drive. + +Here's a step-by-step guide to ensure proper setup: + +1. **Create a Service Account**: + - Go to the Google Cloud Console. + - Navigate to the IAM & Admin > Service Accounts page. + - Click "Create Service Account". + - Fill out the necessary details and click "Create". +2. **Generate a JSON Key**: + + - After creating the service account, click on the service account you created. + - Go to the "Keys" tab. + - Click "Add Key", then select "Create new key". + - Choose **JSON** as the key type and click "Create". This will download a JSON file containing your credentials. + +3. **Provide Necessary Permissions**: + - Ensure that the service account has the required permissions to access Google Drive. You can grant the necessary permissions by assigning the appropriate roles to the service account. +4. **Configure Environment**: + - Place the downloaded `service_account.json` file in the appropriate location accessible to your application. Ensure that the file is named exactly `service_account.json`. + - If running the application in a Docker container, make sure the `service_account.json` file is _mounted_ into the container at runtime. + +These steps should ensure that the `service_account.json` file is correctly set up, thus allowing the `server` module to **authenticate** with Google Cloud successfully and avoid encountering the authentication _error mentioned_. + +### Usage + +- To run the **Python** server, use: + +```sh +docker run -it wspr_cdk python ./hyper/hyper/server.py --interval 5 +``` + +- To run the **Rust** server, use: + +```sh +docker run -e ROCKET_ADDRESS=0.0.0.0 -e ROCKET_PORT=8000 -it wspr_cdk rust +``` + ## Features -- Fetch **WSPR** spot data in various formats (**JSON**, **JSONCompact**, **JSONEachRow**) -- Easy integration with **Tokio** for asynchronous operations -- Abstractions to manage session state and dispatch actions to the **ClickHouse** client -- **Server component** for accessing and sharing real-time data via HTTP +- Fetch **WSPR** spot data in various formats (**JSON**, **JSONCompact**, **JSONEachRow**) +- Easy integration with **Tokio** for asynchronous operations +- Abstractions to manage session state and dispatch actions to the **ClickHouse** client +- **Server component** for accessing and sharing real-time data via HTTP ### Upcoming Features -- **Mutual TLS** for secure client-server communications -- **SSL (OpenSSL)** support for encrypted data transfer +- **Mutual TLS** for secure client-server communications +- **SSL (OpenSSL)** support for encrypted data transfer ## Installation @@ -21,7 +63,7 @@ To use this crate, add `wspr_cdk` to your `Cargo.toml`: ```toml [dependencies] wspr_cdk = "0.0.8" -``` +``` ## Environment Variable @@ -29,7 +71,7 @@ Before using the crate, ensure you set the following environment variable: ```sh export BASE_URL=http://db1.wspr.live/ -``` +``` ## Usage @@ -61,13 +103,13 @@ async fn main() { let json_response = serde_json::to_string_pretty(&response).unwrap(); println!("{}", json_response); } -``` +``` ### Example Query ```sh wget -q -O - "http://db1.wspr.live/?query=SELECT * FROM wspr.rx LIMIT 5 FORMAT JSON;"` -``` +``` ### Sample Output @@ -101,7 +143,7 @@ ClickHouseState { ], STATUS: "Fetching all records.", } -``` +``` ## Server Component @@ -142,7 +184,7 @@ async fn get_wspr_spots() -> Result>, status::Custom> async fn rocket() -> _ { rocket::build().mount("/", routes![get_wspr_spots]) } -``` +``` ### Sample cURL Request @@ -150,40 +192,41 @@ To fetch WSPR spots using the server component, you can use the following cURL c ```sh curl -X GET http://localhost:8000/api/spots -``` +``` ## Client-Side Usage Example You can also fetch WSPR data using client-side JavaScript. Here is a sample implementation: ```html - + - + WSPR Spots - - + +
- - - + } + getData(); + + -``` +``` ## WSPR Guidelines @@ -207,10 +250,10 @@ The `wspr_cdk` is also available as a Docker image: ```sh docker pull lexaraprime/wspr_cdk:master -``` +``` You can find it on Docker Hub: [lexaraprime/wspr_cdk](https://hub.docker.com/layers/lexaraprime/wspr_cdk/master/images/sha256-c869961d9a8413bf8ee562c3507632aeaa4b6e720a97792e7eef5ad984437872?context=repo) ---------------------------------- +--- -This documentation is also available as a crate on [crates.io](https://crates.io/) \ No newline at end of file +This documentation is also available as a crate on [crates.io](https://crates.io/) diff --git a/hyper/hyper/drive.py b/hyper/hyper/drive.py index 896278a..2add374 100644 --- a/hyper/hyper/drive.py +++ b/hyper/hyper/drive.py @@ -22,11 +22,12 @@ def upload_to_drive(file_path): service = build("drive", "v3", credentials=credentials) file_metadata = { - "name": "wspr_spot_data.csv", + "name": constants.FILE_NAME.strip(), "parents": [constants.PARENT_FOLDER_ID], } print("[Uploading] file to Google Drive...\n") + print("\n[Waiting]\nPress CTRL + C to exit...\n") service.files().create(body=file_metadata, media_body=file_path).execute() except Exception as e: diff --git a/hyper/hyper/server.py b/hyper/hyper/server.py index 07d3756..e645a56 100644 --- a/hyper/hyper/server.py +++ b/hyper/hyper/server.py @@ -1,3 +1,4 @@ +import argparse import asyncio import csv import os @@ -9,8 +10,9 @@ class Server: - def __init__(self): - self.write_path = os.path.join(constants.FILE_PATH, "wspr_spot_data.csv") + def __init__(self, interval): + self.write_path = os.path.join(constants.FILE_PATH, constants.FILE_NAME) + self.interval = interval async def write_to_csv(self): """ @@ -19,7 +21,7 @@ async def write_to_csv(self): """ try: - output = await python_wrapper.python_wrapper.get_wspr_spots("100000", "JSON") + output = await python_wrapper.python_wrapper.get_wspr_spots("10", "JSON") data = output.get_data() # Display data that's being fetched for [DEBUG] purposes. @@ -143,10 +145,20 @@ async def display_data(self, data): print("Code:", code_field) async def __call__(self): - await self.write_to_csv() + while True: + await self.write_to_csv() + await asyncio.sleep(self.interval) -server = Server() +def parse_args(): + parser = argparse.ArgumentParser(description="WSPR Spot Data Server") + parser.add_argument( + "--interval", type=int, default=900, help="Sleep interval in seconds" + ) + return parser.parse_args() + if __name__ == "__main__": + args = parse_args() + server = Server(args.interval) asyncio.run(server()) diff --git a/scripts/bash/entrypoint.sh b/scripts/bash/entrypoint.sh new file mode 100644 index 0000000..42d23b7 --- /dev/null +++ b/scripts/bash/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +source /opt/venv/bin/activate + +maturin develop -m python_wrapper/Cargo.toml + +# Check for arguments to determine the mode of operation. +if [[ "$1" == "python" ]]; then + shift + exec python3 "$@" +elif [[ "$1" == "rust" ]]; then + exec ./target/release/wspr_cdk_server +else + echo "Invalid option. Use 'python' to run the Python server or 'rust' to run the Rust server." + exit 1 +fi diff --git a/scripts/bash/inspect_build.sh b/scripts/bash/inspect_build.sh new file mode 100755 index 0000000..f6a7c3a --- /dev/null +++ b/scripts/bash/inspect_build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +printf "\n[Inspecting] container...\n" +sudo docker run -it test python ./hyper/hyper/server.py --interval 5 + +# To do -> Add commands for inspecting resulting image +# docker image inspect [OPTIONS] IMAGE [IMAGE...] \ No newline at end of file